diff --git a/autobuild.xml b/autobuild.xml index 274dde845..78e8aa34f 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1351,92 +1351,6 @@ version 1.5.3 - jsoncpp - - copyright - Copyright (c) 2007-2010 by Baptiste Lepilleur - description - JsonCpp is an implementation of a JSON (http://json.org) reader and writer in C++. - license - Public Domain - license_file - LICENSES/jsoncpp.txt - name - jsoncpp - platforms - - darwin - - archive - - hash - 3243745940e8419f93abf39fd3358158 - hash_algorithm - md5 - url - https://bitbucket.org/alchemyviewer/publiclibs-darwin/downloads/jsoncpp-1.6.5-darwin-201511222000.tar.bz2 - - name - darwin - - linux - - archive - - hash - 840cd9455638c0ea52c613cfddd07d5b - url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/jsoncpp-0.5.0-linux-20110315.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 3ebbace19eb931d9236432ba537c3727 - hash_algorithm - md5 - url - http://depot.alchemyviewer.org/pub/linux64/lib-trusty/jsoncpp-1.6.5-linux64-201603232323.tar.bz2 - - name - linux64 - - windows - - archive - - hash - de7bd51771d3d45185c4977e6aec3a37 - hash_algorithm - md5 - url - http://depot.alchemyviewer.org/pub/windows/lib-vc14/jsoncpp-1.6.5-windows-201601151019.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 592a4145fcc71be4ddae58b265885382 - hash_algorithm - md5 - url - https://depot.alchemyviewer.org/pub/windows64/lib-vc142/jsoncpp-1.8.4-windows64-201712251058.tar.bz2 - - name - windows64 - - - version - 1.8.4 - libhunspell copyright @@ -2013,6 +1927,38 @@ version 7.11.1.297294 + modernjson + + copyright + Copyright (c) 2013-2018 Niels Lohmann + description + JSON for Modern C++ + license + MIT + license_file + LICENSES/modernjson.txt + name + modernjson + platforms + + common + + archive + + hash + 6f11eca7e2a6ca61f9217e949a64f026 + hash_algorithm + md5 + url + https://depot.alchemyviewer.org/pub/common/lib/modernjson-3.2.0-common-201809210551.tar.bz2 + + name + common + + + version + 3.2.0 + nvapi copyright @@ -2476,7 +2422,7 @@ hash_algorithm md5 url - https://bitbucket.org/alchemyviewer/publiclibs-darwin/downloads/uriparser-0.8.4-darwin-201511221948.tar.bz2 + https://depot.alchemyviewer.org/pub/darwin/lib/uriparser-0.8.4-darwin-201511221948.tar.bz2 name darwin @@ -2498,11 +2444,11 @@ archive hash - 69224d9285e5a26fc7a9aca5d3da7543 + cf21d9f7ed7949d2230413e8a391fb73 hash_algorithm md5 url - http://depot.alchemyviewer.org/pub/linux64/lib-trusty/uriparser-0.8.4-linux64-201603240044.tar.bz2 + https://depot.alchemyviewer.org/pub/linux64/lib-xenial/uriparser-0.8.4-linux64-201609241428.tar.bz2 name linux64 @@ -2512,11 +2458,11 @@ archive hash - f9f82fbd29751a969e9ab55a1f33ac7b + 89f3fc7f3fe396439d306f099d1a12d3 hash_algorithm md5 url - http://depot.alchemyviewer.org/pub/windows/lib-vc14/uriparser-0.8.4-windows-201601151009.tar.bz2 + https://depot.alchemyviewer.org/pub/windows/lib-vc141/uriparser-0.8.4-windows-201703090606.tar.bz2 name windows diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 59735a520..b480c96b3 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -19,6 +19,9 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(Variables) include(BuildVersion) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type. One of: Debug Release RelWithDebInfo" FORCE) @@ -58,5 +61,8 @@ add_subdirectory(${LIBS_OPEN_PREFIX}plugins) add_subdirectory(${VIEWER_PREFIX}newview/statemachine) add_subdirectory(${VIEWER_PREFIX}newview) -add_dependencies(viewer singularity-bin) +add_dependencies(viewer ${VIEWER_BRANDING_ID}-bin) +if (WINDOWS) + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${VIEWER_BRANDING_ID}-bin) +endif (WINDOWS) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 4e1e6cb49..e9868cfea 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -7,7 +7,6 @@ set(${CMAKE_CURRENT_LIST_FILE}_INCLUDED "YES") include(Variables) - # Portable compilation flags. set(CMAKE_CXX_FLAGS_DEBUG "-D_DEBUG -DLL_DEBUG=1") @@ -31,14 +30,11 @@ if(NON_RELEASE_CRASH_REPORTING) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DLL_SEND_CRASH_REPORTS=1") endif() - # Don't bother with a MinSizeRel build. - set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;Debug" CACHE STRING "Supported build types." FORCE) # Platform-specific compilation flags. - if (WINDOWS) # Don't build DLLs. set(BUILD_SHARED_LIBS OFF) @@ -88,6 +84,7 @@ if (WINDOWS) /DNOMINMAX /DUNICODE /D_UNICODE + /DBOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE /GS /TP /W3 @@ -111,7 +108,7 @@ if (WINDOWS) endif (USE_LTO) if (WORD_SIZE EQUAL 32) - add_compile_options(/arch:SSE2) + add_compile_options(/arch:SSE3) endif (WORD_SIZE EQUAL 32) if (NOT DISABLE_FATAL_WARNINGS) @@ -128,21 +125,24 @@ set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math") if (LINUX) set(CMAKE_SKIP_RPATH TRUE) + add_compile_options( + -fvisibility=hidden + -fexceptions + -fno-math-errno + -fno-strict-aliasing + -fsigned-char + -g + -pthread + ) + add_definitions( - -DLL_LINUX=1 - -DAPPID=secondlife - -D_REENTRANT - -fexceptions - -fno-math-errno - -fno-strict-aliasing - -fsigned-char - -fvisibility=hidden - -g - -pthread - ) + -DLL_LINUX=1 + -DAPPID=secondlife + -D_REENTRANT + ) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") @@ -213,23 +213,23 @@ if (LINUX) if (NOT STANDALONE) set(MARCH_FLAG " -march=pentium4") endif (NOT STANDALONE) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse3") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse3") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}${MARCH_FLAG} -mfpmath=sse,387 -msse3 ${GCC_EXTRA_OPTIMIZATIONS}") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}${MARCH_FLAG} -mfpmath=sse,387 -msse3 ${GCC_EXTRA_OPTIMIZATIONS}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse3 ${GCC_EXTRA_OPTIMIZATIONS}") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse3 ${GCC_EXTRA_OPTIMIZATIONS}") endif (${ARCH} STREQUAL "x86_64") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}${MARCH_FLAG} -msse2") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}${MARCH_FLAG} -msse2") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -msse2") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -msse2") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse3") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline -msse3") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}${MARCH_FLAG} -msse3") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}${MARCH_FLAG} -msse3") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -msse3") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -msse3") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") if (NOT STANDALONE) - set(MARCH_FLAG " -axsse4.1 -msse2") + set(MARCH_FLAG " -axsse4.1 -msse3") endif (NOT STANDALONE) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}${MARCH_FLAG} -fno-inline-functions") diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 7c8e616d6..cacb7fd05 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -36,10 +36,10 @@ set(cmake_SOURCE_FILES FindGoogleBreakpad.cmake FindGooglePerfTools.cmake FindHunSpell.cmake - FindJsonCpp.cmake FindNDOF.cmake FindOpenJPEG.cmake FindTut.cmake + FindURIPARSER.cmake FindXmlRpcEpi.cmake FreeType.cmake GLOD.cmake @@ -50,7 +50,7 @@ set(cmake_SOURCE_FILES GooglePerfTools.cmake Hunspell.cmake JPEG.cmake - JsonCpp.cmake + Json.cmake LLAddBuildTest.cmake LLAppearance.cmake LLAudio.cmake @@ -65,7 +65,6 @@ set(cmake_SOURCE_FILES LLPlugin.cmake LLPrimitive.cmake LLPhysicsExtensions.cmake - LLSharedLibs.cmake LLRender.cmake LLSharedLibs.cmake LLUI.cmake @@ -93,6 +92,7 @@ set(cmake_SOURCE_FILES Tut.cmake UI.cmake UnixInstall.cmake + URIPARSER.cmake Variables.cmake ViewerMiscLibs.cmake WinManifest.cmake diff --git a/indra/cmake/FindJsonCpp.cmake b/indra/cmake/FindJsonCpp.cmake deleted file mode 100644 index a48c97396..000000000 --- a/indra/cmake/FindJsonCpp.cmake +++ /dev/null @@ -1,39 +0,0 @@ -# -*- cmake -*- - -# - Find JSONCpp -# Find the JSONCpp includes and library -# This module defines -# JSONCPP_FOUND, System has libjsoncpp. -# JSONCPP_INCLUDE_DIRS - The libjsoncpp include directories. -# JSONCPP_LIBRARIES - The libraries needed to use libjsoncpp. -# JSONCPP_DEFINITIONS - Compiler switches required for using libjsoncpp. - -FIND_PACKAGE(PkgConfig) -PKG_CHECK_MODULES(PC_JSONCPP jsoncpp) -SET(JSONCPP_DEFINITIONS ${PC_JSONCPP_CFLAGS_OTHER}) - -FIND_PATH(JSONCPP_INCLUDE_DIR json/reader.h - HINTS ${PC_JSONCPP_INCLUDE_DIR} ${PC_JSONCPP_INCLUDE_DIRS} - PATH_SUFFIXES jsoncpp) - -# Get the GCC compiler version -EXEC_PROGRAM(${CMAKE_CXX_COMPILER} - ARGS ${CMAKE_CXX_COMPILER_ARG1} -dumpversion - OUTPUT_VARIABLE _gcc_COMPILER_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - -# Try to find a library that was compiled with the same compiler version as we currently use. -FIND_LIBRARY(JSONCPP_LIBRARY - NAMES libjson_linux-gcc-${_gcc_COMPILER_VERSION}_libmt.so libjsoncpp.so - HINTS ${PC_JSONCPP_LIBDIR} ${PC_JSONCPP_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib) - -SET(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY}) -SET(JSONCPP_INCLUDE_DIRS ${JSONCPP_INCLUDE_DIR}) - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(JSONCPP DEFAULT_MSG - JSONCPP_LIBRARY JSONCPP_INCLUDE_DIR) - -MARK_AS_ADVANCED(JSONCPP_LIBRARY JSONCPP_INCLUDE_DIR) diff --git a/indra/cmake/FindURIPARSER.cmake b/indra/cmake/FindURIPARSER.cmake new file mode 100644 index 000000000..05f2d1737 --- /dev/null +++ b/indra/cmake/FindURIPARSER.cmake @@ -0,0 +1,46 @@ +# -*- cmake -*- + +# - Find uriparser +# Find the URIPARSER includes and library +# This module defines +# URIPARSER_INCLUDE_DIRS, where to find uriparser.h, etc. +# URIPARSER_LIBRARY, the libraries needed to use uriparser. +# URIPARSER_FOUND, If false, do not try to use uriparser. +# +# This FindURIPARSER is about 43 times as fast the one provided with cmake (2.8.x), +# because it doesn't look up the version of uriparser, resulting in a dramatic +# speed up for configure (from 4 minutes 22 seconds to 6 seconds). +# +# Note: Since this file is only used for standalone, the windows +# specific parts were left out. + +FIND_PATH(URIPARSER_INCLUDE_DIR uriparser/Uri.h + NO_SYSTEM_ENVIRONMENT_PATH + ) + +FIND_LIBRARY(URIPARSER_LIBRARY uriparser) + +if (URIPARSER_LIBRARY AND URIPARSER_INCLUDE_DIR) + SET(URIPARSER_INCLUDE_DIRS ${URIPARSER_INCLUDE_DIR}) + SET(URIPARSER_LIBRARY ${URIPARSER_LIBRARY}) + SET(URIPARSER_FOUND "YES") +else (URIPARSER_LIBRARY AND URIPARSER_INCLUDE_DIR) + SET(URIPARSER_FOUND "NO") +endif (URIPARSER_LIBRARY AND URIPARSER_INCLUDE_DIR) + +if (URIPARSER_FOUND) + if (NOT URIPARSER_FIND_QUIETLY) + message(STATUS "Found URIPARSER: ${URIPARSER_LIBRARY}") + SET(URIPARSER_FIND_QUIETLY TRUE) + endif (NOT URIPARSER_FIND_QUIETLY) +else (URIPARSER_FOUND) + if (URIPARSER_FIND_REQUIRED) + message(FATAL_ERROR "Could not find URIPARSER library") + endif (URIPARSER_FIND_REQUIRED) +endif (URIPARSER_FOUND) + +mark_as_advanced( + URIPARSER_LIBRARY + URIPARSER_INCLUDE_DIR + ) + diff --git a/indra/cmake/Json.cmake b/indra/cmake/Json.cmake new file mode 100644 index 000000000..5c9837047 --- /dev/null +++ b/indra/cmake/Json.cmake @@ -0,0 +1,6 @@ +# -*- cmake -*- + +include(Prebuilt) + +use_prebuilt_binary(modernjson) +set(JSON_INCLUDE_DIR "${LIBS_PREBUILT_DIR}/include") diff --git a/indra/cmake/JsonCpp.cmake b/indra/cmake/JsonCpp.cmake deleted file mode 100644 index 4bde364ce..000000000 --- a/indra/cmake/JsonCpp.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# -*- cmake -*- - -include(Prebuilt) - -set(JSONCPP_FIND_QUIETLY OFF) -set(JSONCPP_FIND_REQUIRED ON) - -if (STANDALONE) - include(FindJsonCpp) -else (STANDALONE) - use_prebuilt_binary(jsoncpp) - if (WINDOWS) - set(JSONCPP_LIBRARIES - debug jsoncppd.lib - optimized jsoncpp.lib) - elseif (DARWIN) - set(JSONCPP_LIBRARIES json_linux-gcc-4.0.1_libmt) - elseif (LINUX) - set(JSONCPP_LIBRARIES jsoncpp) - endif (WINDOWS) - set(JSONCPP_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/) -endif (STANDALONE) diff --git a/indra/cmake/URIPARSER.cmake b/indra/cmake/URIPARSER.cmake new file mode 100644 index 000000000..47c3ccd37 --- /dev/null +++ b/indra/cmake/URIPARSER.cmake @@ -0,0 +1,20 @@ +# -*- cmake -*- + +set(URIPARSER_FIND_QUIETLY ON) +set(URIPARSER_FIND_REQUIRED ON) + +include(Prebuilt) + +if (USESYSTEMLIBS) + include(FindURIPARSER) +else (USESYSTEMLIBS) + use_prebuilt_binary(uriparser) + if (WINDOWS) + set(URIPARSER_LIBRARY + debug uriparserd + optimized uriparser) + elseif (DARWIN OR LINUX) + set(URIPARSER_LIBRARY uriparser) + endif (WINDOWS) + set(URIPARSER_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/uriparser) +endif (USESYSTEMLIBS) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 80eeab0d4..b59485a06 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -10,21 +10,23 @@ include(Linking) include(Boost) include(OpenSSL) include(LLSharedLibs) +include(Json) include(GoogleBreakpad) include(Copy3rdPartyLibs) include(ZLIB) +include(URIPARSER) include_directories( ${EXPAT_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} + ${JSON_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS} ${BREAKPAD_INCLUDE_DIRECTORIES} ) set(llcommon_SOURCE_FILES aialert.cpp - aifile.cpp aiframetimer.cpp aisyncclient.cpp aithreadid.cpp @@ -85,6 +87,7 @@ set(llcommon_SOURCE_FILES llrun.cpp llscopedvolatileaprpool.h llsd.cpp + llsdjson.cpp llsdparam.cpp llsdserialize.cpp llsdserialize_xml.cpp @@ -101,6 +104,7 @@ set(llcommon_SOURCE_FILES llthreadsafequeue.cpp lltimer.cpp lluri.cpp + lluriparser.cpp lluuid.cpp llworkerthread.cpp metaclass.cpp @@ -114,7 +118,6 @@ set(llcommon_HEADER_FILES CMakeLists.txt aialert.h - aifile.h aiframetimer.h airecursive.h aisyncclient.h @@ -210,6 +213,7 @@ set(llcommon_HEADER_FILES llrun.h llsafehandle.h llsd.h + llsdjson.h llsdparam.h llsdserialize.h llsdserialize_xml.h @@ -238,6 +242,7 @@ set(llcommon_HEADER_FILES llunittype.h lltypeinfolookup.h lluri.h + lluriparser.h lluuid.h llwin32headers.h llwin32headerslean.h @@ -296,6 +301,7 @@ target_link_libraries( ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CORESERVICES_LIBRARY} + ${URIPARSER_LIBRARY} ) if (DARWIN) diff --git a/indra/llcommon/aifile.cpp b/indra/llcommon/aifile.cpp deleted file mode 100644 index cd2ade77a..000000000 --- a/indra/llcommon/aifile.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @file aifile.cpp - * @brief POSIX file operations that throw on error. - * - * Copyright (c) 2013, Aleric Inglewood. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * 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. - * - * CHANGELOG - * and additional copyright holders. - * - * 03/11/2013 - * Initial version, written by Aleric Inglewood @ SL - */ - -#include "linden_common.h" -#include "aifile.h" -#include "aialert.h" - -#if LL_WINDOWS -#include -#include // Windows errno -#else -#include -#endif - -AIFile::AIFile(std::string const& filename, char const* accessmode) -{ - mFp = AIFile::fopen(filename, accessmode); -} - -AIFile::~AIFile() -{ - AIFile::close(mFp); -} - -// Like THROW_MALERTE but appends "LLFile::strerr(errn) << " (" << errn << ')'" as argument to replace [ERROR]. -#define THROW_ERROR(...) \ - do { \ - int errn = errno; \ - std::ostringstream error; \ - error << LLFile::strerr(errn) << " (" << errn << ')'; \ - THROW_MALERT_CLASS(AIAlert::ErrorCode, errn, __VA_ARGS__ ("[ERROR]", error.str())); \ - } while(0) - -//static -void AIFile::mkdir(std::string const& dirname, int perms) -{ - int rc = LLFile::mkdir_nowarn(dirname, perms); - if (rc < 0 && errno != EEXIST) - { - THROW_ERROR("AIFile_mkdir_Failed_to_create_DIRNAME", AIArgs("[DIRNAME]", dirname)); - } -} - -//static -void AIFile::rmdir(std::string const& dirname) -{ - int rc = LLFile::rmdir_nowarn(dirname); - if (rc < 0 && errno != ENOENT) - { - THROW_ERROR("AIFile_rmdir_Failed_to_remove_DIRNAME", AIArgs("[DIRNAME]", dirname)); - } -} - -//static -LLFILE* AIFile::fopen(std::string const& filename, const char* mode) -{ - LLFILE* fp = LLFile::fopen(filename, mode); - if (!fp) - { - THROW_ERROR("AIFile_fopen_Failed_to_open_FILENAME", AIArgs("[FILENAME]", filename)); - } - return fp; -} - -//static -void AIFile::close(LLFILE* file) -{ - if (LLFile::close(file) < 0) - { - THROW_ERROR("AIFile_close_Failed_to_close_file", AIArgs); - } -} - -//static -void AIFile::remove(std::string const& filename) -{ - int rc = LLFile::remove_nowarn(filename); - if (rc < 0 && errno != ENOENT) - { - THROW_ERROR("AIFile_remove_Failed_to_remove_FILENAME", AIArgs("[FILENAME]", filename)); - } -} - -//static -void AIFile::rename(std::string const& filename, std::string const& newname) -{ - if (LLFile::rename_nowarn(filename, newname) < 0) - { - THROW_ERROR("AIFile_rename_Failed_to_rename_FILE_to_NEWFILE", AIArgs("[FILE]", filename)("[NEWFILE]", newname)); - } -} - diff --git a/indra/llcommon/aifile.h b/indra/llcommon/aifile.h deleted file mode 100644 index 1b110496a..000000000 --- a/indra/llcommon/aifile.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file aifile.h - * @brief Declaration of AIFile. - * - * Copyright (c) 2013, Aleric Inglewood. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * 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. - * - * CHANGELOG - * and additional copyright holders. - * - * 02/11/2013 - * Initial version, written by Aleric Inglewood @ SL - */ - -#ifndef AIFILE_H -#define AIFILE_H - -#include "llfile.h" - -// As LLFile, but throws AIAlert instead of printing a warning. -class LL_COMMON_API AIFile -{ - private: - LLFILE* mFp; - - public: - // Scoped file (exception safe). Throws AIAlertCode with errno on failure. - AIFile(std::string const& filename, char const* accessmode); - ~AIFile(); - - operator LLFILE* () const { return mFp; } - - // All these functions take UTF8 path/filenames. - static LLFILE* fopen(std::string const& filename, char const* accessmode); - static void close(LLFILE* file); - - static void mkdir(std::string const& dirname, int perms = 0700); // Does NOT throw when dirname already exists. - static void rmdir(std::string const& dirname); // Does NOT throw when dirname does not exist. - static void remove(std::string const& filename); // Does NOT throw when filename does not exist. - static void rename(std::string const& filename, std::string const& newname); -}; - -#endif // AIFILE_H diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index d600936d8..9936dfd33 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -131,19 +131,6 @@ const U32 DEFAULT_CGI_SERVICES_PORT = 12045; // on a single host for map tile generation. JC const U32 DEFAULT_MAPSERVER_PORT = 12124; -const char LAND_LAYER_CODE = 'L'; -const char WATER_LAYER_CODE = 'W'; -const char WIND_LAYER_CODE = '7'; -const char CLOUD_LAYER_CODE = '8'; - -// Aurora Sim -// Extended land layer for Aurora Sim -const char AURORA_LAND_LAYER_CODE = 'M'; -const char AURORA_WATER_LAYER_CODE = 'X'; -const char AURORA_WIND_LAYER_CODE = '9'; -const char AURORA_CLOUD_LAYER_CODE = ':'; -// Aurora Sim - enum ETerrainBrushType { // the valid brush numbers cannot be reordered, because they diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 5b22d5bab..66602f4e7 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -101,6 +101,9 @@ LLAssetDictionary::LLAssetDictionary() addEntry(LLAssetType::AT_OUTFIT, new AssetEntry("OUTFIT", "outfit", "outfit", false, false, false)); addEntry(LLAssetType::AT_MY_OUTFITS, new AssetEntry("MY_OUTFITS", "my_otfts", "my outfits", false, false, false)); addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, true, true)); + addEntry(LLAssetType::AT_SETTINGS, new AssetEntry("SETTINGS", "settings", "settings blob", true, true, true)); + + addEntry(LLAssetType::AT_UNKNOWN, new AssetEntry("UNKNOWN", "invalid", NULL, false, false, false)); addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, false, false, false)); }; diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index e6fee54ca..9435eeb2b 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -122,6 +122,9 @@ public: AT_LINK_FOLDER = 25, // Inventory folder link + + AT_MARKETPLACE_FOLDER = 26, + // Marketplace folder. Same as an AT_CATEGORY but different display methods. AT_CURRENT_OUTFIT = 46, @@ -132,7 +135,16 @@ public: AT_MESH = 49, // Mesh data in our proprietary SLM format - AT_COUNT = 50, + AT_RESERVED_1 = 50, + AT_RESERVED_2 = 51, + AT_RESERVED_3 = 52, + AT_RESERVED_4 = 53, + AT_RESERVED_5 = 54, + AT_RESERVED_6 = 55, + + AT_SETTINGS = 56, // Collection of settings + + AT_COUNT = 57, // +*********************************************************+ // | TO ADD AN ELEMENT TO THIS ENUM: | @@ -143,7 +155,7 @@ public: // | 4. ADD TO LLViewerAssetType.cpp | // | 5. ADD TO DEFAULT_ASSET_FOR_INV in LLInventoryType.cpp | // +*********************************************************+ - + AT_UNKNOWN = 255, AT_NONE = -1 }; diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index e6b688ee0..3cb14a2ca 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -120,6 +120,7 @@ namespace { LL_INFOS() << "Error setting log file to " << filename << LL_ENDL; } mWantsTime = true; + mWantsTags = true; } ~RecordToFile() @@ -252,23 +253,13 @@ namespace { #ifdef __GNUC__ // GCC: type_info::name() returns a mangled class name,st demangle - - static size_t abi_name_len = 100; - static char* abi_name_buf = (char*)malloc(abi_name_len); - // warning: above is voodoo inferred from the GCC manual, - // do NOT change - - int status; - // We don't use status, and shouldn't have to pass apointer to it - // but gcc 3.3 libstc++'s implementation of demangling is broken - // and fails without. - - char* name = abi::__cxa_demangle(type.name(), - abi_name_buf, &abi_name_len, &status); - // this call can realloc the abi_name_buf pointer (!) - - return name ? name : type.name(); - + // passing nullptr, 0 forces allocation of a unique buffer we can free + // fixing MAINT-8724 on OSX 10.14 + int status = -1; + char* name = abi::__cxa_demangle(type.name(), nullptr, 0, &status); + std::string result(name ? name : type.name()); + free(name); + return result; #elif LL_WINDOWS // DevStudio: type_info::name() includes the text "class " at the start @@ -600,7 +591,7 @@ namespace LLError mFunctionString += std::string(mFunction) + ":"; for (size_t i = 0; i < mTagCount; i++) { - mTagString += std::string("#") + mTags[i] + ((i == mTagCount - 1) ? "" : " "); + mTagString += std::string("#") + mTags[i] + ((i == mTagCount - 1) ? "" : ","); } } } @@ -983,13 +974,19 @@ namespace } if (show_level && r->wantsLevel()) - { - message_stream << site.mLevelString << " "; - } + { + message_stream << site.mLevelString; + } if (show_tags && r->wantsTags()) { - message_stream << site.mTagString << " "; + message_stream << site.mTagString; + } + + if ((show_level && r->wantsLevel())|| + (show_tags && r->wantsTags())) + { + message_stream << " "; } if (show_function && r->wantsFunctionName()) diff --git a/indra/llcommon/llformat.cpp b/indra/llcommon/llformat.cpp index f9f16006a..834bcf753 100644 --- a/indra/llcommon/llformat.cpp +++ b/indra/llcommon/llformat.cpp @@ -36,18 +36,38 @@ #include "llformat.h" #include +#include // common used function with va_list argument // wrapper for vsnprintf to be called from llformatXXX functions. -static void va_format(std::string& out, const char *fmt, va_list va) +static void va_format(std::string& out, const char *fmt, va_list& va) { - char tstr[1024]; /* Flawfinder: ignore */ -#if LL_WINDOWS - _vsnprintf(tstr, 1024, fmt, va); + if (!fmt || !fmt[0]) return; // Don't bother if null or empty c_str + + typedef typename std::vector> vec_t; + static thread_local vec_t charvector(1024); // Evolves into charveleon + #define vsnprintf(va) std::vsnprintf(charvector.data(), charvector.capacity(), fmt, va) +#ifdef LL_WINDOWS // We don't have to copy on windows + #define va2 va #else - vsnprintf(tstr, 1024, fmt, va); /* Flawfinder: ignore */ + va_list va2; + va_copy(va2, va); #endif - out.assign(tstr); + const auto smallsize(charvector.capacity()); + const auto size = vsnprintf(va); + if (size < 0) + { + LL_ERRS() << "Encoding failed, code " << size << ". String hint: " << out << '/' << fmt << LL_ENDL; + } + else if (static_cast(size) >= smallsize) // Resize if we need more space + { + charvector.resize(1+size); // Use the String Stone + vsnprintf(va2); + } +#ifndef LL_WINDOWS + va_end(va2); +#endif + out.assign(charvector.data()); } std::string llformat(const char *fmt, ...) diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp new file mode 100644 index 000000000..5b49f247a --- /dev/null +++ b/indra/llcommon/llsdjson.cpp @@ -0,0 +1,126 @@ +// 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 llsdjson.cpp + * @brief LLSD flexible data system + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, 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$ + */ + +// Must turn on conditional declarations in header file so definitions end up +// with proper linkage. +#define LLSD_DEBUG_INFO +#include "linden_common.h" + +#include "llsdjson.h" + +#include "llerror.h" + + +//========================================================================= +LLSD LlsdFromJson(const nlohmann::json &val) +{ + LLSD result; + + switch (val.type()) + { + default: + case nlohmann::json::value_t::null: + break; + case nlohmann::json::value_t::number_integer: + result = LLSD(val.get()); + break; + case nlohmann::json::value_t::number_unsigned: + result = LLSD(val.get()); + break; + case nlohmann::json::value_t::number_float: + result = LLSD(val.get()); + break; + case nlohmann::json::value_t::string: + result = LLSD(val.get()); + break; + case nlohmann::json::value_t::boolean: + result = LLSD(val.get()); + break; + case nlohmann::json::value_t::array: + result = LLSD::emptyArray(); + for (const auto &element : val) + { + result.append(LlsdFromJson(element)); + } + break; + case nlohmann::json::value_t::object: + result = LLSD::emptyMap(); + for (auto it = val.cbegin(); it != val.cend(); ++it) + { + result[it.key()] = LlsdFromJson(it.value()); + } + break; + } + return result; +} + +//========================================================================= +nlohmann::json LlsdToJson(const LLSD &val) +{ + nlohmann::json result; + + switch (val.type()) + { + case LLSD::TypeUndefined: + result = nullptr; + break; + case LLSD::TypeBoolean: + result = val.asBoolean(); + break; + case LLSD::TypeInteger: + result = val.asInteger(); + break; + case LLSD::TypeReal: + result = val.asReal(); + break; + case LLSD::TypeURI: + case LLSD::TypeDate: + case LLSD::TypeUUID: + case LLSD::TypeString: + result = val.asString(); + break; + case LLSD::TypeMap: + for (LLSD::map_const_iterator it = val.beginMap(); it != val.endMap(); ++it) + { + result[it->first] = LlsdToJson(it->second); + } + break; + case LLSD::TypeArray: + for (LLSD::array_const_iterator it = val.beginArray(); it != val.endArray(); ++it) + { + result.push_back(LlsdToJson(*it)); + } + break; + case LLSD::TypeBinary: + default: + LL_ERRS("LlsdToJson") << "Unsupported conversion to JSON from LLSD type (" << val.type() << ")." << LL_ENDL; + break; + } + + return result; +} diff --git a/indra/llcommon/llsdjson.h b/indra/llcommon/llsdjson.h new file mode 100644 index 000000000..bc76acbc8 --- /dev/null +++ b/indra/llcommon/llsdjson.h @@ -0,0 +1,77 @@ +/** + * @file llsdjson.cpp + * @brief LLSD flexible data system + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, 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_LLSDJSON_H +#define LL_LLSDJSON_H + +#include +#include +#include + +#include "stdtypes.h" + +#include "llsd.h" +#include + +/// Convert a parsed JSON structure into LLSD maintaining member names and +/// array indexes. +/// JSON/JavaScript types are converted as follows: +/// +/// JSON Type | LLSD Type +/// --------------+-------------- +/// null | undefined +/// integer | LLSD::Integer +/// unsigned | LLSD::Integer +/// real/numeric | LLSD::Real +/// string | LLSD::String +/// boolean | LLSD::Boolean +/// array | LLSD::Array +/// object | LLSD::Map +/// +/// 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); + +/// Convert an LLSD object into Parsed JSON object maintaining member names and +/// array indexs. +/// +/// Types are converted as follows: +/// LLSD Type | JSON Type +/// --------------+---------------- +/// TypeUndefined | null +/// TypeBoolean | boolean +/// TypeInteger | integer +/// TypeReal | real/numeric +/// TypeString | string +/// TypeURI | string +/// TypeDate | string +/// TypeUUID | string +/// TypeMap | object +/// TypeArray | array +/// TypeBinary | unsupported +nlohmann::json LlsdToJson(const LLSD &val); + +#endif // LL_LLSDJSON_H diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index dc1236fce..7203b4d6e 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -1,3 +1,5 @@ +// 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 llstring.cpp * @brief String utility functions and the std::string class. @@ -28,12 +30,17 @@ #include "llstring.h" #include "llerror.h" +#include "llfasttimer.h" +#include "llsd.h" #if LL_WINDOWS #include "llwin32headerslean.h" #include // for WideCharToMultiByte #endif +LLTrace::BlockTimerStatHandle FT_STRING_FORMAT("String Format"); + + std::string ll_safe_string(const char* in) { if(in) return std::string(in); @@ -47,6 +54,23 @@ std::string ll_safe_string(const char* in, S32 maxlen) return std::string(); } +bool is_char_hex(char hex) +{ + if((hex >= '0') && (hex <= '9')) + { + return true; + } + else if((hex >= 'a') && (hex <='f')) + { + return true; + } + else if((hex >= 'A') && (hex <='F')) + { + return true; + } + return false; // uh - oh, not hex any more... +} + U8 hex_as_nybble(char hex) { if((hex >= '0') && (hex <= '9')) @@ -85,7 +109,7 @@ bool iswindividual(llwchar elem) bool _read_file_into_string(std::string& str, const std::string& filename) { - llifstream ifs(filename, llifstream::binary); + llifstream ifs(filename.c_str(), llifstream::binary); if (!ifs.is_open()) { LL_INFOS() << "Unable to open file " << filename << LL_ENDL; @@ -552,6 +576,33 @@ std::string utf8str_truncate(const std::string& utf8str, const S32 max_len) } } +std::string utf8str_symbol_truncate(const std::string& utf8str, const S32 symbol_len) +{ + if (0 == symbol_len) + { + return std::string(); + } + if ((S32)utf8str.length() <= symbol_len) + { + return utf8str; + } + else + { + int len = 0, byteIndex = 0; + const char* aStr = utf8str.c_str(); + size_t origSize = utf8str.size(); + + for (byteIndex = 0; len < symbol_len && byteIndex < origSize; byteIndex++) + { + if ((aStr[byteIndex] & 0xc0) != 0x80) + { + len += 1; + } + } + return utf8str.substr(0, byteIndex); + } +} + std::string utf8str_substChar( const std::string& utf8str, const llwchar target_char, @@ -628,10 +679,10 @@ std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page) 0, in, len_in, - NULL, + nullptr, 0, - 0, - 0); + nullptr, + nullptr); // We will need two more bytes for the double NULL ending // created in WideCharToMultiByte(). char* pout = new char [len_out + 2]; @@ -645,8 +696,8 @@ std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page) len_in, pout, len_out, - 0, - 0); + nullptr, + nullptr); out.assign(pout); delete[] pout; } @@ -667,8 +718,8 @@ wchar_t* ll_convert_string_to_wide(const std::string& in, unsigned int code_page // reserve place to NULL terminator int output_str_len = in.length(); wchar_t* w_out = new wchar_t[output_str_len + 1]; - memset(w_out, 0, output_str_len + 1); + int real_output_str_len = MultiByteToWideChar (code_page, 0, in.c_str(), in.length(), w_out, output_str_len); //looks like MultiByteToWideChar didn't add null terminator to converted string, see EXT-4858. @@ -689,7 +740,7 @@ std::string ll_convert_string_to_utf8_string(const std::string& in) long LLStringOps::sPacificTimeOffset = 0; long LLStringOps::sLocalTimeOffset = 0; -bool LLStringOps::sPacificDaylightTime = 0; +bool LLStringOps::sPacificDaylightTime = false; std::map LLStringOps::datetimeToCodes; std::vector LLStringOps::sWeekDayList; @@ -708,7 +759,7 @@ S32 LLStringOps::collate(const llwchar* a, const llwchar* b) #if LL_WINDOWS // in Windows, wide string functions operator on 16-bit strings, // not the proper 32 bit wide string - return strcmp(wstring_to_utf8str(LLWString(a)).c_str(), wstring_to_utf8str(LLWString(b)).c_str()); + return strcoll(wstring_to_utf8str(LLWString(a)).c_str(), wstring_to_utf8str(LLWString(b)).c_str()); #else return wcscoll(a, b); #endif @@ -719,7 +770,7 @@ void LLStringOps::setupDatetimeInfo (bool daylight) time_t nowT, localT, gmtT; struct tm * tmpT; - nowT = time (NULL); + nowT = time (nullptr); tmpT = gmtime (&nowT); gmtT = mktime (tmpT); @@ -799,7 +850,7 @@ void LLStringOps::setupDayFormat(const std::string& data) } -std::string LLStringOps::getDatetimeCode (std::string key) +std::string LLStringOps::getDatetimeCode(const std::string& key) { std::map::iterator iter; @@ -1187,6 +1238,7 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token, template<> S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) { + LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT); S32 res = 0; std::string output; @@ -1259,6 +1311,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) template<> S32 LLStringUtil::format(std::string& s, const LLSD& substitutions) { + LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT); S32 res = 0; if (!substitutions.isMap()) @@ -1387,7 +1440,7 @@ void LLStringUtilBase::testHarness() s2.erase( 4, 1 ); llassert( s2 == "hell"); - s2.insert( 0, 1, 'y' ); + s2.insert( s2.begin(), 'y' ); llassert( s2 == "yhell"); s2.erase( 1, 3 ); llassert( s2 == "yl"); diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 091849277..35407ca4e 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -29,8 +29,11 @@ #include #include -#include +//#include #include +#include +#include +#include #include "llsd.h" #include "llfasttimer.h" @@ -49,6 +52,7 @@ #endif const char LL_UNKNOWN_CHAR = '?'; +class LLSD; #if LL_DARWIN || LL_LINUX || LL_SOLARIS // Template specialization of char_traits for U16s. Only necessary on Mac and Linux (exists on Windows already) @@ -209,7 +213,7 @@ public: // currently in daylight savings time? static bool getPacificDaylightTime(void) { return sPacificDaylightTime;} - static std::string getDatetimeCode (std::string key); + static std::string getDatetimeCode (const std::string& key); // Express a value like 1234567 as "1.23M" static std::string getReadableNumber(F64 num); @@ -301,7 +305,7 @@ public: static bool isValidIndex(const string_type& string, size_type i) { - return !string.empty() && (0 <= i) && (i <= string.size()); + return !string.empty() && (i <= string.size()); } static bool contains(const string_type& string, T c, size_type i=0) @@ -311,6 +315,7 @@ public: static void trimHead(string_type& string); static void trimTail(string_type& string); + static void trimTail(string_type& string, const string_type& tokens); static void trim(string_type& string) { trimHead(string); trimTail(string); } static void truncate(string_type& string, size_type count); @@ -340,6 +345,7 @@ public: static void addCRLF(string_type& string); static void removeCRLF(string_type& string); + static void removeWindowsCR(string_type& string); static void replaceTabsWithSpaces( string_type& string, size_type spaces_per_tab ); static void replaceNonstandardASCII( string_type& string, T replacement ); @@ -448,7 +454,7 @@ public: struct LLDictionaryLess { public: - bool operator()(const std::string& a, const std::string& b) + bool operator()(const std::string& a, const std::string& b) const { return (LLStringUtil::precedesDict(a, b) ? true : false); } @@ -478,6 +484,7 @@ inline std::string chop_tail_copy( * @brief This translates a nybble stored as a hex value from 0-f back * to a nybble in the low order bits of the return byte. */ +LL_COMMON_API bool is_char_hex(char hex); LL_COMMON_API U8 hex_as_nybble(char hex); /** @@ -532,10 +539,13 @@ LL_COMMON_API S32 wchar_to_utf8chars(llwchar inchar, char* outchars); LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str, S32 len); LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str); - LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len); LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str); +#if LL_WINDOWS +inline std::string wstring_to_utf8str(const llutf16string &utf16str) { return utf16str_to_utf8str(utf16str);} +#endif + // Length of this UTF32 string in bytes when transformed to UTF8 LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr); @@ -551,7 +561,7 @@ LL_COMMON_API S32 utf16str_wstring_length(const llutf16string &utf16str, S32 len LL_COMMON_API S32 wstring_utf16_length(const LLWString & wstr, S32 woffset, S32 wlen); // Length in wstring (i.e., llwchar count) of a part of a wstring specified by utf16 length (i.e., utf16 units.) -LL_COMMON_API S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, S32 woffset, S32 utf16_length, BOOL *unaligned = NULL); +LL_COMMON_API S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, S32 woffset, S32 utf16_length, BOOL *unaligned = nullptr); /** * @brief Properly truncate a utf8 string to a maximum byte count. @@ -571,6 +581,17 @@ LL_COMMON_API S32 utf8str_compare_insensitive( const std::string& lhs, const std::string& rhs); +/** +* @brief Properly truncate a utf8 string to a maximum character count. +* +* If symbol_len is longer than the string passed in, the return +* value == utf8str. +* @param utf8str A valid utf8 string to truncate. +* @param symbol_len The maximum number of symbols in the return value. +* @return Returns a valid utf8 string with symbol count <= max_len. +*/ +LL_COMMON_API std::string utf8str_symbol_truncate(const std::string& utf8str, const S32 symbol_len); + /** * @brief Replace all occurences of target_char with replace_char * @@ -813,8 +834,9 @@ public: } /// This implementation uses the answer cached by setiter(). - virtual bool escaped() const { return mIsEsc; } - virtual T next() + bool escaped() const override { return mIsEsc; } + + T next() override { // If we're looking at the escape character of an escape sequence, // skip that character. This is the one time we can modify 'mIter' @@ -830,21 +852,21 @@ public: return result; } - virtual bool is(T ch) const + bool is(T ch) const override { // Like base-class is(), except that an escaped character matches // nothing. return (! done()) && (! mIsEsc) && *mIter == ch; } - virtual bool oneof(const string_type& delims) const + bool oneof(const string_type& delims) const override { // Like base-class oneof(), except that an escaped character matches // nothing. return (! done()) && (! mIsEsc) && LLStringUtilBase::contains(delims, *mIter); } - virtual bool collect_until(string_type& into, const_iterator from, T delim) + bool collect_until(string_type& into, const_iterator from, T delim) override { // Deal with escapes in the characters we collect; that is, an escaped // character must become just that character without the preceding @@ -1158,7 +1180,7 @@ BOOL LLStringUtilBase::precedesDict( const string_type& a, const string_type& { if( a.size() && b.size() ) { - return (LLStringUtilBase::compareDict(a.c_str(), b.c_str()) < 0); + return (LLStringUtilBase::compareDict(a, b) < 0); } else { @@ -1213,7 +1235,7 @@ void LLStringUtilBase::trimHead(string_type& string) template void LLStringUtilBase::trimTail(string_type& string) { - if( string.size() ) + if(!string.empty()) { size_type len = string.length(); size_type i = len; @@ -1226,12 +1248,31 @@ void LLStringUtilBase::trimTail(string_type& string) } } +template +void LLStringUtilBase::trimTail(string_type& string, const string_type& tokens) +{ + if(!string.empty()) + { + size_type len = string.length(); + size_type i = len; + while( i > 0 && (tokens.find_first_of(string[i-1]) != string_type::npos) ) + { + i--; + } + + string.erase( i, len - i ); + } +} + // Replace line feeds with carriage return-line feed pairs. //static template void LLStringUtilBase::addCRLF(string_type& string) { + if (string.empty()) + return; + const T LF = 10; const T CR = 13; @@ -1274,6 +1315,9 @@ void LLStringUtilBase::addCRLF(string_type& string) template void LLStringUtilBase::removeCRLF(string_type& string) { + if (string.empty()) + return; + const T CR = 13; size_type cr_count = 0; @@ -1293,6 +1337,32 @@ void LLStringUtilBase::removeCRLF(string_type& string) //static template +void LLStringUtilBase::removeWindowsCR(string_type& string) +{ + if (string.empty()) + { + return; + } + const T LF = 10; + const T CR = 13; + + size_type cr_count = 0; + size_type len = string.size(); + size_type i; + for( i = 0; i < len - cr_count - 1; i++ ) + { + if( string[i+cr_count] == CR && string[i+cr_count+1] == LF) + { + cr_count++; + } + + string[i] = string[i+cr_count]; + } + string.erase(i, cr_count); +} + +//static +template void LLStringUtilBase::replaceChar( string_type& string, T target, T replacement ) { size_type found_pos = 0; @@ -1376,6 +1446,7 @@ BOOL LLStringUtilBase::containsNonprintable(const string_type& string) return rv; } +// *TODO: reimplement in terms of algorithm //static template void LLStringUtilBase::stripNonprintable(string_type& string) @@ -1386,33 +1457,23 @@ void LLStringUtilBase::stripNonprintable(string_type& string) { return; } - size_t src_size = string.size(); - char* c_string = nullptr; - try - { - c_string = new char[src_size + 1]; - } - catch (const std::bad_alloc&) - { - return; - } - copy(c_string, string.c_str(), src_size+1); - char* write_head = &c_string[0]; + const size_t src_size = string.size(); + auto c_string = std::make_unique(src_size + 1); + + copy(c_string.get(), string.c_str(), src_size+1); for (size_type i = 0; i < src_size; i++) { - char* read_head = &string[i]; - write_head = &c_string[j]; - if(!(*read_head < MIN)) + if(string[i] >= MIN) { - *write_head = *read_head; + c_string[j] = string[i]; ++j; } } c_string[j]= '\0'; - string = c_string; - delete []c_string; + string.assign(c_string.get()); } +// *TODO: reimplement in terms of algorithm template std::basic_string LLStringUtilBase::quote(const string_type& str, const string_type& triggers, @@ -1434,7 +1495,9 @@ std::basic_string LLStringUtilBase::quote(const string_type& str, } // For whatever reason, we must quote this string. + auto needed_escapes = std::count(str.begin(), str.end(), '"'); string_type result; + result.reserve(len + (needed_escapes * escape.length())); result.push_back('"'); for (typename string_type::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci) { diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index b990dfe2a..5f5a67a88 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -1,3 +1,5 @@ +// 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 lluri.cpp * @author Phoenix @@ -40,7 +42,8 @@ #include #include -void encode_character(std::ostream& ostr, std::string::value_type val) +// static +void LLURI::encodeCharacter(std::ostream& ostr, std::string::value_type val) { ostr << "%" @@ -95,7 +98,7 @@ std::string LLURI::escape( } else { - encode_character(ostr, c); + encodeCharacter(ostr, c); } } } @@ -106,7 +109,7 @@ std::string LLURI::escape( c = *it; if(allowed.find(c) == std::string::npos) { - encode_character(ostr, c); + encodeCharacter(ostr, c); } else { @@ -129,11 +132,30 @@ std::string LLURI::unescape(const std::string& str) { ++it; if(it == end) break; - U8 c = hex_as_nybble(*it++); - c = c << 4; - if (it == end) break; - c |= hex_as_nybble(*it); - ostr.put((char)c); + + if(is_char_hex(*it)) + { + U8 c = hex_as_nybble(*it++); + + c = c << 4; + if (it == end) break; + + if(is_char_hex(*it)) + { + c |= hex_as_nybble(*it); + ostr.put((char)c); + } + else + { + ostr.put((char)c); + ostr.put(*it); + } + } + else + { + ostr.put('%'); + ostr.put(*it); + } } else { @@ -169,11 +191,10 @@ namespace { return LLURI::escape(s, unreserved() + ":@!$'()*+,="); } // sub_delims - "&;" + ":@" } -// *TODO: Consider using curl. After http textures gets merged everywhere. // static std::string LLURI::escape(const std::string& str) { - static std::string default_allowed(unreserved() + ":@!$'()*+,=/?&#;"); + static std::string default_allowed = unreserved(); static bool initialized = false; if(!initialized) { @@ -228,7 +249,8 @@ void LLURI::parseAuthorityAndPathUsingOpaque() { if (mScheme == "http" || mScheme == "https" || mScheme == "ftp" || mScheme == "secondlife" || - mScheme == "x-grid-location-info") + mScheme == "x-grid-info" || + mScheme == "x-grid-location-info") // legacy { if (mEscapedOpaque.substr(0,2) != "//") { @@ -401,6 +423,15 @@ LLURI LLURI::buildHTTP(const std::string& prefix, return uri; } +// static +LLURI LLURI::buildHTTP(const std::string& scheme, + const std::string& prefix, + const LLSD& path, + const LLSD& query) +{ + return buildHTTP(llformat("%s://%s", scheme.c_str(), prefix.c_str()), path, query); +} + // static LLURI LLURI::buildHTTP(const std::string& host, const U32& port, @@ -485,6 +516,14 @@ std::string LLURI::hostName() const return unescape(host); } +std::string LLURI::hostNameAndPort() const +{ + std::string user, host, port; + findAuthorityParts(mEscapedAuthority, user, host, port); + return port.empty() ? unescape(host) : unescape(host + ":" + port); +} + + std::string LLURI::userName() const { std::string user, userPass, host, port; diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h index 57bbedf42..faa5f3a75 100644 --- a/indra/llcommon/lluri.h +++ b/indra/llcommon/lluri.h @@ -4,31 +4,25 @@ * @date 2006-02-05 * @brief Declaration of the URI class. * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2006&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$ */ @@ -72,6 +66,13 @@ public: const std::string& prefix, const LLSD& path, const LLSD& query); + + static LLURI buildHTTP( + const std::string& scheme, + const std::string& prefix, + const LLSD& path, + const LLSD& query); + ///< prefix is either a full URL prefix of the form /// "http://example.com:8080", or it can be simply a host and /// optional port like "example.com" or "example.com:8080", in @@ -99,20 +100,21 @@ public: std::string scheme() const; ///< ex.: "http", note lack of colon std::string opaque() const; ///< everything after the colon - // for schemes that follow path like syntax (http, https, ftp) - std::string authority() const; // ex.: "host.com:80" - std::string hostName() const; // ex.: "host.com" - std::string userName() const; - std::string password() const; - U16 hostPort() const; // ex.: 80, will include implicit port - BOOL defaultPort() const; // true if port is default for scheme - const std::string& escapedPath() const { return mEscapedPath; } - std::string path() const; // ex.: "/abc/def", includes leading slash - LLSD pathArray() const; // above decoded into an array of strings - std::string query() const; // ex.: "x=34", section after "?" - const std::string& escapedQuery() const { return mEscapedQuery; } - LLSD queryMap() const; // above decoded into a map - static LLSD queryMap(std::string escaped_query_string); + // for schemes that follow path like syntax (http, https, ftp) + std::string authority() const; // ex.: "user:pass@host.com:80" + std::string hostName() const; // ex.: "host.com" + std::string hostNameAndPort() const; // ex.: "host.com:80" + std::string userName() const; + std::string password() const; + U16 hostPort() const; // ex.: 80, will include implicit port + BOOL defaultPort() const; // true if port is default for scheme + const std::string& escapedPath() const { return mEscapedPath; } + std::string path() const; // ex.: "/abc/def", includes leading slash + LLSD pathArray() const; // above decoded into an array of strings + std::string query() const; // ex.: "x=34", section after "?" + const std::string& escapedQuery() const { return mEscapedQuery; } + LLSD queryMap() const; // above decoded into a map + static LLSD queryMap(std::string escaped_query_string); /** * @brief given a name value map, return a serialized query string. @@ -127,27 +129,24 @@ public: /** @name Escaping Utilities */ //@{ /** - * @brief Escape a raw url with a reasonable set of allowed characters. + * @brief 'Escape' symbol into stream * - * The default set was chosen to match HTTP urls and general - * guidelines for naming resources. Passing in a raw url does not - * produce well defined results because you really need to know - * which segments are path parts because path parts are supposed - * to be escaped individually. The default set chosen is: + * @param ostr Output stream. + * @param val Symbol to encode. + */ + static void encodeCharacter(std::ostream& ostr, std::string::value_type val); + + /** + * @brief Escape the string passed except for unreserved * * ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz * 0123456789 * -._~ - * :@!$'()*+,=/?&#; * - * *NOTE: This API is basically broken because it does not - * allow you to specify significant path characters. For example, - * if the filename actually contained a /, then you cannot use - * this function to generate the serialized url for that - * resource. + * @see http://www.ietf.org/rfc/rfc1738.txt * * @param str The raw URI to escape. - * @return Returns the escaped uri or an empty string. + * @return Returns the rfc 1738 escaped uri or an empty string. */ static std::string escape(const std::string& str); diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp new file mode 100644 index 000000000..e32d9f5c0 --- /dev/null +++ b/indra/llcommon/lluriparser.cpp @@ -0,0 +1,266 @@ +// 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 lluriparser.cpp + * @author Protey + * @date 2014-10-07 + * @brief Implementation of the LLUriParser class. + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "lluriparser.h" + +LLUriParser::LLUriParser(const std::string& u) +: mRes(0) +, mTmpScheme(false) +, mNormalizedTmp(false) +{ + mState.uri = &mUri; + + if (u.find("://") == std::string::npos) + { + mNormalizedUri = "http://"; + mTmpScheme = true; + } + + mNormalizedUri += u; + + mRes = parse(); +} + +LLUriParser::~LLUriParser() +{ + uriFreeUriMembersA(&mUri); +} + +S32 LLUriParser::parse() +{ + mRes = uriParseUriA(&mState, mNormalizedUri.c_str()); + return mRes; +} + +const char * LLUriParser::scheme() const +{ + return mScheme.c_str(); +} + +void LLUriParser::scheme(const std::string& s) +{ + mTmpScheme = !s.size(); + mScheme = s; +} + +const char * LLUriParser::port() const +{ + return mPort.c_str(); +} + +void LLUriParser::port(const std::string& s) +{ + mPort = s; +} + +const char * LLUriParser::host() const +{ + return mHost.c_str(); +} + +void LLUriParser::host(const std::string& s) +{ + mHost = s; +} + +const char * LLUriParser::path() const +{ + return mPath.c_str(); +} + +void LLUriParser::path(const std::string& s) +{ + mPath = s; +} + +const char * LLUriParser::query() const +{ + return mQuery.c_str(); +} + +void LLUriParser::query(const std::string& s) +{ + mQuery = s; +} + +const char * LLUriParser::fragment() const +{ + return mFragment.c_str(); +} + +void LLUriParser::fragment(const std::string& s) +{ + mFragment = s; +} + +void LLUriParser::textRangeToString(UriTextRangeA& textRange, std::string& str) +{ + if (textRange.first != nullptr && textRange.afterLast != nullptr && textRange.first < textRange.afterLast) + { + const ptrdiff_t len = textRange.afterLast - textRange.first; + str.assign(textRange.first, static_cast(len)); + } + else + { + str = LLStringUtil::null; + } +} + +void LLUriParser::extractParts() +{ + if (mTmpScheme || mNormalizedTmp) + { + mScheme.clear(); + } + else + { + textRangeToString(mUri.scheme, mScheme); + } + + textRangeToString(mUri.hostText, mHost); + textRangeToString(mUri.portText, mPort); + textRangeToString(mUri.query, mQuery); + textRangeToString(mUri.fragment, mFragment); + + UriPathSegmentA * pathHead = mUri.pathHead; + while (pathHead) + { + std::string partOfPath; + textRangeToString(pathHead->text, partOfPath); + + mPath += '/'; + mPath += partOfPath; + + pathHead = pathHead->next; + } +} + +S32 LLUriParser::normalize() +{ + mNormalizedTmp = mTmpScheme; + if (!mRes) + { + mRes = uriNormalizeSyntaxExA(&mUri, URI_NORMALIZE_SCHEME | URI_NORMALIZE_HOST); + + if (!mRes) + { + S32 chars_required; + mRes = uriToStringCharsRequiredA(&mUri, &chars_required); + + if (!mRes) + { + chars_required++; + std::vector label_buf(chars_required); + mRes = uriToStringA(&label_buf[0], &mUri, chars_required, nullptr); + + if (!mRes) + { + mNormalizedUri = &label_buf[mTmpScheme ? 7 : 0]; + mTmpScheme = false; + } + } + } + } + + if(mTmpScheme) + { + mNormalizedUri = mNormalizedUri.substr(7); + mTmpScheme = false; + } + + return mRes; +} + +void LLUriParser::glue(std::string& uri) const +{ + std::string first_part; + glueFirst(first_part); + + std::string second_part; + glueSecond(second_part); + + uri = first_part + second_part; +} + +void LLUriParser::glueFirst(std::string& uri, bool use_scheme) const +{ + if (use_scheme && mScheme.size()) + { + uri = mScheme; + uri += "://"; + } + else + { + uri.clear(); + } + + uri += mHost; +} + +void LLUriParser::glueSecond(std::string& uri) const +{ + if (mPort.size()) + { + uri = ':'; + uri += mPort; + } + else + { + uri.clear(); + } + + uri += mPath; + + if (mQuery.size()) + { + uri += '?'; + uri += mQuery; + } + + if (mFragment.size()) + { + uri += '#'; + uri += mFragment; + } +} + +bool LLUriParser::test() const +{ + std::string uri; + glue(uri); + + return uri == mNormalizedUri; +} + +const char * LLUriParser::normalizedUri() const +{ + return mNormalizedUri.c_str(); +} diff --git a/indra/llcommon/lluriparser.h b/indra/llcommon/lluriparser.h new file mode 100644 index 000000000..773d4189f --- /dev/null +++ b/indra/llcommon/lluriparser.h @@ -0,0 +1,87 @@ +/** + * @file lluriparser.h + * @author Protey + * @date 20146-10-07 + * @brief Declaration of the UriParser class. + * + * $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_LLURIPARSER_H +#define LL_LLURIPARSER_H + +#include +#include + +class LL_COMMON_API LLUriParser +{ +public: + LLUriParser(const std::string& u); + ~LLUriParser(); + + const char * scheme() const; + void scheme (const std::string& s); + + const char * port() const; + void port (const std::string& s); + + const char * host() const; + void host (const std::string& s); + + const char * path() const; + void path (const std::string& s); + + const char * query() const; + void query (const std::string& s); + + const char * fragment() const; + void fragment (const std::string& s); + + const char * normalizedUri() const; + + void extractParts(); + void glue(std::string& uri) const; + void glueFirst(std::string& uri, bool use_scheme = true) const; + void glueSecond(std::string& uri) const; + bool test() const; + S32 normalize(); + +private: + S32 parse(); + void textRangeToString(UriTextRangeA& textRange, std::string& str); + std::string mScheme; + std::string mHost; + std::string mPort; + std::string mPath; + std::string mQuery; + std::string mFragment; + std::string mNormalizedUri; + + UriParserStateA mState; + UriUriA mUri; + + S32 mRes; + bool mTmpScheme; + bool mNormalizedTmp; +}; + +#endif // LL_LLURIPARSER_H diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index bb8365528..5fe513310 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -103,6 +103,9 @@ LLFolderDictionary::LLFolderDictionary() addEntry(LLFolderType::FT_OUTBOX, new FolderEntry("outbox", TRUE)); addEntry(LLFolderType::FT_BASIC_ROOT, new FolderEntry("basic_rt", TRUE)); + addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new FolderEntry("merchant", FALSE)); + addEntry(LLFolderType::FT_MARKETPLACE_STOCK, new FolderEntry("stock", FALSE)); + addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new FolderEntry("version", FALSE)); addEntry(LLFolderType::FT_SUITCASE, new FolderEntry("suitcase", TRUE)); addEntry(LLFolderType::FT_NONE, new FolderEntry("-1", FALSE)); diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h index bb9a3f213..bea5dc379 100644 --- a/indra/llinventory/llfoldertype.h +++ b/indra/llinventory/llfoldertype.h @@ -94,6 +94,9 @@ public: FT_BASIC_ROOT = 52, + FT_MARKETPLACE_LISTINGS = 53, + FT_MARKETPLACE_STOCK = 54, + FT_MARKETPLACE_VERSION = 55, // Note: We actually *never* create folders with that type. This is used for icon override only. FT_SUITCASE = 100, FT_COUNT, diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp index 78651b55f..fcef1611c 100644 --- a/indra/llinventory/llinventorytype.cpp +++ b/indra/llinventory/llinventorytype.cpp @@ -85,6 +85,7 @@ LLInventoryDictionary::LLInventoryDictionary() addEntry(LLInventoryType::IT_ANIMATION, new InventoryEntry("animation", "animation", 1, LLAssetType::AT_ANIMATION)); addEntry(LLInventoryType::IT_GESTURE, new InventoryEntry("gesture", "gesture", 1, LLAssetType::AT_GESTURE)); addEntry(LLInventoryType::IT_MESH, new InventoryEntry("mesh", "mesh", 1, LLAssetType::AT_MESH)); + addEntry(LLInventoryType::IT_SETTINGS, new InventoryEntry("settings", "settings", 1, LLAssetType::AT_SETTINGS)); } @@ -145,6 +146,15 @@ DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] = LLInventoryType::IT_NONE, // 47 AT_NONE LLInventoryType::IT_NONE, // 48 AT_NONE LLInventoryType::IT_MESH, // 49 AT_MESH + + LLInventoryType::IT_NONE, // 50 AT_RESERVED_1 + LLInventoryType::IT_NONE, // 52 AT_RESERVED_2 + LLInventoryType::IT_NONE, // 53 AT_RESERVED_3 + LLInventoryType::IT_NONE, // 54 AT_RESERVED_4 + LLInventoryType::IT_NONE, // 55 AT_RESERVED_5 + LLInventoryType::IT_NONE, // 56 AT_RESERVED_6 + + LLInventoryType::IT_SETTINGS, // 57 AT_SETTINGS }; // static diff --git a/indra/llinventory/llinventorytype.h b/indra/llinventory/llinventorytype.h index b444ecfa5..1d9627ffb 100644 --- a/indra/llinventory/llinventorytype.h +++ b/indra/llinventory/llinventorytype.h @@ -62,8 +62,10 @@ public: IT_ANIMATION = 19, IT_GESTURE = 20, IT_MESH = 22, - IT_COUNT = 23, + IT_SETTINGS = 25, + IT_COUNT = 26, + IT_UNKNOWN = 255, IT_NONE = -1 }; @@ -108,8 +110,14 @@ public: ICONNAME_LINKFOLDER, ICONNAME_MESH, + ICONNAME_SETTINGS, + ICONNAME_SETTINGS_SKY, + ICONNAME_SETTINGS_WATER, + ICONNAME_SETTINGS_DAY, + ICONNAME_CLOTHING_UNKNOWN, ICONNAME_INVALID, + ICONNAME_UNKNOWN, ICONNAME_COUNT, ICONNAME_NONE = -1 }; diff --git a/indra/llinventory/llparcelflags.h b/indra/llinventory/llparcelflags.h index 343044727..9d4884e32 100644 --- a/indra/llinventory/llparcelflags.h +++ b/indra/llinventory/llparcelflags.h @@ -68,7 +68,7 @@ const U32 PF_ALLOW_ALL_OBJECT_ENTRY = 1 << 27; // Allow all objects to enter a const U32 PF_ALLOW_GROUP_OBJECT_ENTRY = 1 << 28; // Only allow group (and owner) objects to enter the parcel const U32 PF_ALLOW_VOICE_CHAT = 1 << 29; // Allow residents to use voice chat on this parcel const U32 PF_USE_ESTATE_VOICE_CHAN = 1 << 30; -const U32 PF_DENY_AGEUNVERIFIED = 1 << 31; // Prevent residents who aren't age-verified +const U32 PF_DENY_AGEUNVERIFIED = 1U << 31; // Prevent residents who aren't age-verified // NOTE: At one point we have used all of the bits. // We have deprecated two of them in 1.19.0 which *could* be reused, // but only after we are certain there are no simstates using those bits. diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index ecf324465..a802da5bd 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -395,7 +395,7 @@ inline U32 get_lower_power_two(U32 val, U32 max_power_two) { if(!max_power_two) { - max_power_two = 1 << 31 ; + max_power_two = 1U << 31 ; } if(max_power_two & (max_power_two - 1)) { @@ -417,7 +417,7 @@ inline U32 get_next_power_two(U32 val, U32 max_power_two) { if(!max_power_two) { - max_power_two = 1 << 31 ; + max_power_two = 1U << 31 ; } if(val >= max_power_two) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index a66790f1d..c23beea84 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -2291,12 +2291,13 @@ size_t BufferedCurlEasyRequest::curlHeaderCallback(char* data, size_t size, size } else { - reason = "Unexpected HTTP status."; + //reason = "Unexpected HTTP status."; LL_WARNS() << "Received unexpected status value from server (" << status << "): \"" << header << "\"" << LL_ENDL; } + // Singu TODO: WHAT THE HELL WAS THIS?! LYING? WHY WERE WE LYING???? Responders USE These Codes!!!!! // Either way, this status value is not understood (or taken into account). // Set it to internal error so that the rest of code treats it as an error. - status = HTTP_INTERNAL_ERROR_OTHER; + if (!status) status = HTTP_INTERNAL_ERROR_OTHER; } self_w->received_HTTP_header(); self_w->setStatusAndReason(status, reason); diff --git a/indra/llprimitive/object_flags.h b/indra/llprimitive/object_flags.h index f48b97d58..e97014470 100644 --- a/indra/llprimitive/object_flags.h +++ b/indra/llprimitive/object_flags.h @@ -69,6 +69,7 @@ const U32 FLAGS_TEMPORARY = (1U << 30); //const U32 FLAGS_UNUSED_007 = (1U << 31); // was FLAGS_ZLIB_COMPRESSED const U32 FLAGS_LOCAL = FLAGS_ANIM_SOURCE | FLAGS_CAMERA_SOURCE; +const U32 FLAGS_WORLD = FLAGS_USE_PHYSICS | FLAGS_PHANTOM | FLAGS_TEMPORARY_ON_REZ; typedef enum e_havok_joint_type { diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index d1e5dd41a..c1a9c51e9 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -584,7 +584,7 @@ void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_leve // Check if dimensions are a power of two! if (!checkSize(width,height)) { - LL_WARNS() << llformat("Texture has non power of two dimension: %dx%d",width,height) << " Unless on Aurora-Sim, beware." << LL_ENDL; + LL_WARNS() << llformat("Texture has non power of two dimension: %dx%d",width,height) << " Unless on WhiteCore, beware." << LL_ENDL; } if (mTexName) diff --git a/indra/llrender/llpostprocess.cpp b/indra/llrender/llpostprocess.cpp index 0bc624fbb..505dcf670 100644 --- a/indra/llrender/llpostprocess.cpp +++ b/indra/llrender/llpostprocess.cpp @@ -35,6 +35,7 @@ #include "llpostprocess.h" #include "lldir.h" +#include "llcontrol.h" #include "llfasttimer.h" #include "llgl.h" #include "llglslshader.h" @@ -286,7 +287,7 @@ public: LLVector2 screen_rect = LLPostProcess::getInstance()->getDimensions(); mPassLoc = getShader().getUniformLocation(sHorizontalPass); - LLVector4 vec[] = { LLVector4(1.3846153846, 3.2307692308, 0, 0) / screen_rect.mV[VX], LLVector4( 0,0, 1.3846153846, 3.2307692308 ) / screen_rect.mV[VY] }; + LLVector4 vec[] = { LLVector4(1.3846153846f, 3.2307692308f, 0.f, 0.f) / screen_rect.mV[VX], LLVector4( 0.f,0.f, 1.3846153846f, 3.2307692308f ) / screen_rect.mV[VY] }; getShader().uniform4fv(sKern, LL_ARRAY_SIZE(vec), (GLfloat*)vec); return QUAD_NORMAL; } @@ -355,42 +356,44 @@ LLPostProcess::LLPostProcess(void) : mShaders.push_back(new LLPosterizeShader()); /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.*/ - std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME)); - LL_DEBUGS("AppInit", "Shaders") << "Loading PostProcess Effects settings from " << pathName << LL_ENDL; - - llifstream effectsXML(pathName); - - if (effectsXML) + auto load_effects = [&](const std::string& pathName) { - LLPointer parser = new LLSDXMLParser(); + LL_DEBUGS("AppInit", "Shaders") << "Loading PostProcess Effects settings from " << pathName << LL_ENDL; + llifstream effectsXML(pathName); - parser->parse(effectsXML, mAllEffectInfo, LLSDSerialize::SIZE_UNLIMITED); - } + if (effectsXML) + { + LLPointer parser = new LLSDXMLParser(); + parser->parse(effectsXML, mAllEffectInfo, LLSDSerialize::SIZE_UNLIMITED); + } + }; + load_effects(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME)); + load_effects(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "windlight", XML_FILENAME)); - // Singu TODO: Make this configurable via settings - if (!mAllEffectInfo.has("default")) - mAllEffectInfo["default"] = LLSD::emptyMap(); + auto setting = gSavedSettings.getControl("SinguPostProcessDefault"); + setting->getSignal()->connect(std::bind(&LLPostProcess::setSelectedEffect, this, std::bind(&LLSD::asStringRef, std::placeholders::_2))); + const auto& str = setting->get().asStringRef(); + if (!mAllEffectInfo.has(str)) + mAllEffectInfo[str] = LLSD::emptyMap(); - LLSD& defaults = mAllEffectInfo["default"]; + LLSD& defaults = mAllEffectInfo[str]; - for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) + // Add defaults for all missing effects + for(auto& shader : mShaders) { - LLSD shader_defaults = (*it)->getDefaults(); + const LLSD shader_defaults = shader->getDefaults(); for (LLSD::map_const_iterator it2 = defaults.beginMap();it2 != defaults.endMap();++it2) { if(!defaults.has(it2->first)) defaults[it2->first]=it2->second; } } - for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) - { - (*it)->loadSettings(defaults); - } - setSelectedEffect("default"); + setSelectedEffect(str); } LLPostProcess::~LLPostProcess(void) { + gSavedSettings.setString("SinguPostProcessDefault", mSelectedEffectName); destroyGL() ; } @@ -663,10 +666,11 @@ void LLPostProcess::setSelectedEffect(std::string const & effectName) { mSelectedEffectName = effectName; mSelectedEffectInfo = mAllEffectInfo[effectName]; - for(std::list >::iterator it=mShaders.begin();it!=mShaders.end();++it) + for(auto shader : mShaders) { - (*it)->loadSettings(mSelectedEffectInfo); + shader->loadSettings(mSelectedEffectInfo); } + mSelectedEffectChanged(mSelectedEffectName); } void LLPostProcess::setSelectedEffectValue(std::string const & setting, LLSD value) @@ -702,8 +706,9 @@ void LLPostProcess::resetSelectedEffect() void LLPostProcess::saveEffectAs(std::string const & effectName) { mAllEffectInfo[effectName] = mSelectedEffectInfo; + mSelectedEffectChanged(mSelectedEffectName); // Might've changed, either way update the lists - std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME)); + std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "windlight", XML_FILENAME)); //LL_INFOS() << "Saving PostProcess Effects settings to " << pathName << LL_ENDL; llofstream effectsXML(pathName); diff --git a/indra/llrender/llpostprocess.h b/indra/llrender/llpostprocess.h index 14ea2881d..b130cf2af 100644 --- a/indra/llrender/llpostprocess.h +++ b/indra/llrender/llpostprocess.h @@ -34,6 +34,7 @@ #define LL_POSTPROCESS_H #include +#include #include "llsd.h" #include "llrendertarget.h" @@ -103,6 +104,9 @@ private: // The map of all availible effects LLSD mAllEffectInfo; + typedef boost::signals2::signal selected_effect_changed_signal; + selected_effect_changed_signal mSelectedEffectChanged; + public: LLPostProcess(void); ~LLPostProcess(void); @@ -144,6 +148,7 @@ public: // Setters void setSelectedEffect(std::string const & effectName); void setSelectedEffectValue(std::string const & setting, LLSD value); + auto setSelectedEffectChangeCallback(const selected_effect_changed_signal::slot_type& func) { return mSelectedEffectChanged.connect(func); } void resetSelectedEffect(); void saveEffectAs(std::string const & effectName); }; diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 9b5adf60c..568ec8c14 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -985,7 +985,7 @@ void LLShaderMgr::cleanupShaderSources() for (GLsizei i = 0; i < count; ++i) { std::multimap::iterator it = mShaderObjects.begin(); - for (; it != LLShaderMgr::instance()->mShaderObjects.end(); it++) + for (; it != mShaderObjects.end(); it++) { if ((*it).second.mHandle == shaders[i]) { diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 1ee716cb8..b00081882 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -44,6 +44,7 @@ set(llui_SOURCE_FILES llkeywords.cpp lllayoutstack.cpp lllineeditor.cpp + llloadingindicator.cpp lllocalcliprect.cpp llmenugl.cpp llmodaldialog.cpp @@ -122,6 +123,7 @@ set(llui_HEADER_FILES llkeywords.h lllayoutstack.h lllineeditor.h + llloadingindicator.h lllocalcliprect.h llmemberlistener.h llmenugl.h diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index e8bf11a1d..60eae831c 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -61,66 +61,92 @@ S32 BTN_HEIGHT = 0; S32 BTN_GRID = 12; S32 BORDER_SIZE = 1; + +LLButton::Params::Params() + : label_selected("label_selected"), // requires is_toggle true + label_shadow("label_shadow", true), + auto_resize("auto_resize", false), + use_ellipses("use_ellipses", false), + image_unselected("image_unselected", LLUI::getUIImage("button_enabled_32x128.tga")), + image_selected("image_selected", LLUI::getUIImage("button_enabled_selected_32x128.tga")), + image_hover_selected("image_hover_selected"), + image_hover_unselected("image_hover_unselected"), + image_disabled_selected("image_disabled_selected", LLUI::getUIImage("button_disabled_32x128.tga")), + image_disabled("image_disabled", LLUI::getUIImage("button_disabled_32x128.tga")), + image_pressed("image_pressed", LLUI::getUIImage("button_enabled_selected_32x128.tga")), + image_pressed_selected("image_pressed_selected", LLUI::getUIImage("button_enabled_32x128.tga")), + image_overlay("image_overlay"), + image_overlay_selected("image_overlay_selected"), + image_overlay_alignment("image_overlay_alignment", std::string("center")), + image_overlay_enable("image_overlay_enable", true), + label_color("label_color", LLUI::sColorsGroup->getColor("ButtonLabelColor")), + label_color_selected("label_color_selected", LLUI::sColorsGroup->getColor("ButtonLabelSelectedColor")), + label_color_disabled("label_color_disabled", LLUI::sColorsGroup->getColor("ButtonLabelDisabledColor")), + label_color_disabled_selected("label_color_disabled_selected", LLUI::sColorsGroup->getColor("ButtonLabelSelectedDisabledColor")), + image_color("image_color", LLUI::sColorsGroup->getColor("ButtonImageColor")), // requires is_toggle true + image_color_disabled("image_color_disabled", LLUI::sColorsGroup->getColor("ButtonImageColor")), + image_overlay_color("image_overlay_color", LLColor4::white), + image_overlay_selected_color("image_overlay_selected_color", LLColor4::white), + image_overlay_disabled_color("image_overlay_disabled_color", LLColor4(1.f, 1.f, 1.f, .5f)), + flash_color("flash_color", LLUI::sColorsGroup->getColor("ButtonFlashBgColor")), + pad_right("pad_right", LLUI::sConfigGroup->getS32("ButtonHPad")), + pad_left("pad_left", LLUI::sConfigGroup->getS32("ButtonHPad")), + pad_bottom("pad_bottom", LLUI::sConfigGroup->getS32("ButtonVPad")), + image_top_pad("image_top_pad"), + image_bottom_pad("image_bottom_pad"), + imgoverlay_label_space("imgoverlay_label_space", 1), + click_callback("click_callback"), + mouse_down_callback("mouse_down_callback"), + mouse_up_callback("mouse_up_callback"), + mouse_held_callback("mouse_held_callback"), + is_toggle("is_toggle", false), + scale_image("scale_image", true), + commit_on_return("commit_on_return", true), + display_pressed_state("display_pressed_state", true), + hover_glow_amount("hover_glow_amount", 0.25f), + //held_down_delay("held_down_delay"), + use_draw_context_alpha("use_draw_context_alpha", true), + //badge("badge"), + handle_right_mouse("handle_right_mouse", false), + button_flash_enable("button_flash_enable", false), + button_flash_count("button_flash_count", LLUI::sConfigGroup->getS32("ButtonFlashCount")), + button_flash_rate("button_flash_rate", LLUI::sConfigGroup->getF32("ButtonFlashRate")), + fade_when_disabled("fade_when_disabled", false), + help_url("help_url") +{ + addSynonym(is_toggle, "toggle"); + changeDefault(initial_value, LLSD(false)); + changeDefault(font_halign, LLFontGL::HCENTER); +} + LLButton::LLButton( const std::string& name, const LLRect& rect, const std::string& control_name, commit_callback_t commit_callback) : LLUICtrl(name, rect, TRUE, commit_callback), - - mMouseDownFrame( 0 ), + mMouseDownFrame(0), mMouseHeldDownCount(0), - mBorderEnabled( FALSE ), - mFlashing( FALSE ), + mBorderEnabled(FALSE), + mFlashing(FALSE), mCurGlowStrength(0.f), mNeedsHighlight(FALSE), - mUnselectedLabel(name), - mSelectedLabel(name), - mGLFont( LLFontGL::getFontSansSerif() ), - mHeldDownDelay( 0.5f ), // seconds until held-down callback is called - mHeldDownFrameDelay( 0 ), - mImageUnselected(LLUI::getUIImage("button_enabled_32x128.tga")), - mImageSelected(LLUI::getUIImage("button_enabled_selected_32x128.tga")), - mImageDisabled(LLUI::getUIImage("button_disabled_32x128.tga")), - mImageDisabledSelected(LLUI::getUIImage("button_disabled_32x128.tga")), - mImagePressed(LLUI::getUIImage("button_enabled_selected_32x128.tga")), - mImagePressedSelected(LLUI::getUIImage("button_enabled_32x128.tga")), + mHeldDownDelay(0.5f), // seconds until held-down callback is called + mHeldDownFrameDelay(0), mImageFlash(NULL), - mImageHoverSelected( NULL ), - mImageHoverUnselected( NULL ), - mUnselectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelColor" )), - mSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedColor")), - mDisabledLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelDisabledColor")), - mDisabledSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedDisabledColor")), - mImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), - mFlashBgColor(LLUI::sColorsGroup->getColor("ButtonFlashBgColor")), - mDisabledImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), - mImageOverlay(NULL), - mImageOverlayColor(LLColor4::white), - mImageOverlayDisabledColor(LLColor4(1.f,1.f,1.f,.5f)), - mImageOverlaySelectedColor(LLColor4::white), - mImageOverlayAlignment(LLFontGL::HCENTER), mImageOverlayTopPad(0), mImageOverlayBottomPad(0), mImgOverlayLabelSpace(1), - mIsToggle( FALSE ), - mScaleImage( TRUE ), - mDropShadowedText( TRUE ), - mAutoResize( FALSE ), - mHAlign( LLFontGL::HCENTER ), - mLeftHPad( LLBUTTON_H_PAD ), - mRightHPad( LLBUTTON_H_PAD ), - mBottomVPad( LLBUTTON_V_PAD ), - mHoverGlowStrength(0.25f), - mCommitOnReturn(TRUE), - mFadeWhenDisabled(FALSE), + mDropShadowedText(TRUE), mForcePressedState(false), - mDisplayPressedState(TRUE), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mHeldDownSignal(NULL), - mHandleRightMouse(false), - mButtonFlashCount(LLUI::sConfigGroup->getS32("ButtonFlashCount")), - mButtonFlashRate(LLUI::sConfigGroup->getF32("ButtonFlashRate")), - mAlpha(1.f) + mAlpha(1.f), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mHeldDownSignal(nullptr) { - init(control_name); + Params params; + params.label_selected(name) + .label(name) + .control_name(control_name) + .name(name) + .rect(rect); + initFromParams(params); } @@ -133,92 +159,133 @@ LLButton::LLButton(const std::string& name, const LLRect& rect, const std::string& unselected_label, const std::string& selected_label ) : LLUICtrl(name, rect, TRUE, commit_callback), - mMouseDownFrame( 0 ), + mMouseDownFrame(0), mMouseHeldDownCount(0), - mBorderEnabled( FALSE ), - mFlashing( FALSE ), + mBorderEnabled(FALSE), + mFlashing(FALSE), mCurGlowStrength(0.f), mNeedsHighlight(FALSE), - mUnselectedLabel(unselected_label), - mSelectedLabel(selected_label), - mGLFont( font ? font : LLFontGL::getFontSansSerif() ), - mHeldDownDelay( 0.5f ), // seconds until held-down callback is called - mHeldDownFrameDelay( 0 ), - mImageUnselected(LLUI::getUIImage("button_enabled_32x128.tga")), - mImageSelected(LLUI::getUIImage("button_enabled_selected_32x128.tga")), - mImageDisabled(LLUI::getUIImage("button_disabled_32x128.tga")), - mImageDisabledSelected(LLUI::getUIImage("button_disabled_32x128.tga")), - mImagePressed(LLUI::getUIImage("button_enabled_selected_32x128.tga")), - mImagePressedSelected(LLUI::getUIImage("button_enabled_32x128.tga")), + mHeldDownDelay(0.5f), // seconds until held-down callback is called + mHeldDownFrameDelay(0), mImageFlash(NULL), - mImageHoverSelected( NULL ), - mImageHoverUnselected( NULL ), - mUnselectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelColor" )), - mSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedColor")), - mDisabledLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelDisabledColor")), - mDisabledSelectedLabelColor(LLUI::sColorsGroup->getColor("ButtonLabelSelectedDisabledColor")), - mImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), - mFlashBgColor(LLUI::sColorsGroup->getColor("ButtonFlashBgColor")), - mDisabledImageColor(LLUI::sColorsGroup->getColor("ButtonImageColor")), - mImageOverlay(NULL), - mImageOverlayColor(LLColor4::white), - mImageOverlayDisabledColor(LLColor4(1.f,1.f,1.f,.5f)), - mImageOverlaySelectedColor(LLColor4::white), - mImageOverlayAlignment(LLFontGL::HCENTER), mImageOverlayTopPad(0), mImageOverlayBottomPad(0), mImgOverlayLabelSpace(1), - mIsToggle( FALSE ), - mScaleImage( TRUE ), - mDropShadowedText( TRUE ), - mAutoResize( FALSE ), - mHAlign( LLFontGL::HCENTER ), - mLeftHPad( LLBUTTON_H_PAD ), - mRightHPad( LLBUTTON_H_PAD ), - mBottomVPad( LLBUTTON_V_PAD ), - mHoverGlowStrength(0.25f), - mCommitOnReturn(TRUE), - mFadeWhenDisabled(FALSE), + mDropShadowedText(TRUE), mForcePressedState(false), - mDisplayPressedState(TRUE), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mHeldDownSignal(NULL), - mHandleRightMouse(false), - mButtonFlashCount(LLUI::sConfigGroup->getS32("ButtonFlashCount")), - mButtonFlashRate(LLUI::sConfigGroup->getF32("ButtonFlashRate")), - mAlpha(1.f) + mAlpha(1.f), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mHeldDownSignal(nullptr) { + Params params; + params.label_selected(selected_label) + .label(unselected_label) + .font(font) + .control_name(control_name) + .name(name) + .rect(rect); - if( unselected_image_name != "" ) + if (!unselected_image_name.empty()) { // user-specified image - don't use fixed borders unless requested - mImageUnselected = LLUI::getUIImage(unselected_image_name); - mImageDisabled = mImageUnselected; + auto image = LLUI::getUIImage(unselected_image_name); + params.image_unselected(image) + .image_disabled(image) + .image_pressed_selected(image) - mFadeWhenDisabled = TRUE; - //mScaleImage = FALSE; - - mImagePressedSelected = mImageUnselected; + .fade_when_disabled(true); + //params.scale_image(false); } - if( selected_image_name != "" ) + if (!selected_image_name.empty()) { // user-specified image - don't use fixed borders unless requested - mImageSelected = LLUI::getUIImage(selected_image_name); - mImageDisabledSelected = mImageSelected; + auto image = LLUI::getUIImage(selected_image_name); + params.image_selected(image) + .image_disabled_selected(image) + .image_pressed(image) - mFadeWhenDisabled = TRUE; - //mScaleImage = FALSE; - - mImagePressed = mImageSelected; + .fade_when_disabled(true); + //params.scale_image(false); } - init(control_name); + initFromParams(params); } -void LLButton::init(const std::string& control_name) +LLButton::LLButton(const Params& params) +: LLUICtrl(params), + mMouseDownFrame(0), + mMouseHeldDownCount(0), + mBorderEnabled(FALSE), + mFlashing(FALSE), + mCurGlowStrength(0.f), + mNeedsHighlight(FALSE), + mHeldDownDelay(0.5f), // seconds until held-down callback is called + mHeldDownFrameDelay(0), + mImageFlash(NULL), + mImageOverlayTopPad(0), + mImageOverlayBottomPad(0), + mImgOverlayLabelSpace(1), + mDropShadowedText(TRUE), + mForcePressedState(false), + mAlpha(1.f), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mHeldDownSignal(nullptr) { + initFromParams(params); +} + +void LLButton::initFromParams(const Params& params) +{ + LLUICtrl::initFromParams(params); + mUnselectedLabel = params.label; + mSelectedLabel = params.label_selected; + auto font = params.font(); + mGLFont = font ? font : LLFontGL::getFontSansSerif(); + mHAlign = params.font_halign; + mImageUnselected = params.image_unselected; + mImageSelected = params.image_selected; + mImageDisabled = params.image_disabled; + mImageDisabledSelected = params.image_disabled_selected; + mImagePressed = params.image_pressed; + mImagePressedSelected = params.image_pressed_selected; + mImageHoverSelected = params.image_hover_selected; + mImageHoverUnselected = params.image_hover_unselected; + mUnselectedLabelColor = params.label_color; + mSelectedLabelColor = params.label_color_selected; + mDisabledLabelColor = params.label_color_disabled; + mDisabledSelectedLabelColor = params.label_color_disabled_selected; + mImageColor = params.image_color; + mFlashBgColor = params.flash_color; + mDisabledImageColor = params.image_color_disabled; + mImageOverlay = params.image_overlay; + mImageOverlaySelected = params.image_overlay_selected; + mImageOverlayColor = params.image_overlay_color; + mImageOverlayDisabledColor = params.image_overlay_disabled_color; + mImageOverlaySelectedColor = params.image_overlay_selected_color; + mImageOverlayAlignment = LLFontGL::hAlignFromName(params.image_overlay_alignment); + mIsToggle = params.is_toggle; + mScaleImage = params.scale_image; + mAutoResize = params.auto_resize; + mLeftHPad = params.pad_left; + mRightHPad = params.pad_right; + mBottomVPad = params.pad_bottom; + mHoverGlowStrength = params.hover_glow_amount; + mCommitOnReturn = params.commit_on_return; + mFadeWhenDisabled = params.fade_when_disabled; + mDisplayPressedState = params.display_pressed_state; + if (params.mouse_down_callback.isProvided()) + setMouseDownCallback(params.mouse_down_callback); + if (params.mouse_up_callback.isProvided()) + setMouseUpCallback(params.mouse_up_callback); + if (params.mouse_held_callback.isProvided()) + setHeldDownCallback(params.mouse_held_callback); + mHandleRightMouse = params.handle_right_mouse; + mButtonFlashCount = params.button_flash_count; + mButtonFlashRate = params.button_flash_rate; + // Hack to make sure there is space for at least one character if (getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" "))) { @@ -229,9 +296,16 @@ void LLButton::init(const std::string& control_name) mMouseDownTimer.stop(); - setControlName(control_name, NULL); + setControlName(params.control_name, NULL); } +//static +const LLButton::Params& LLButton::getDefaultParams() +{ + // Singu Note: We diverge here, not using LLUICtrlFactory::getDefaultParams + static const Params p; + return p; +} // virtual LLButton::~LLButton() @@ -783,12 +857,13 @@ void LLButton::draw() } overlay_color.mV[VALPHA] *= alpha; + auto& overlay = (mIsToggle && mImageOverlaySelected && getToggleState()) ? mImageOverlaySelected : mImageOverlay; switch(mImageOverlayAlignment) { case LLFontGL::LEFT: text_left += overlay_width + mImgOverlayLabelSpace; text_width -= overlay_width + mImgOverlayLabelSpace; - mImageOverlay->draw( + overlay->draw( mLeftHPad, center_y - (overlay_height / 2), overlay_width, @@ -796,7 +871,7 @@ void LLButton::draw() overlay_color); break; case LLFontGL::HCENTER: - mImageOverlay->draw( + overlay->draw( center_x - (overlay_width / 2), center_y - (overlay_height / 2), overlay_width, @@ -806,7 +881,7 @@ void LLButton::draw() case LLFontGL::RIGHT: text_right -= overlay_width + mImgOverlayLabelSpace; text_width -= overlay_width + mImgOverlayLabelSpace; - mImageOverlay->draw( + overlay->draw( getRect().getWidth() - mRightHPad - overlay_width, center_y - (overlay_height / 2), overlay_width, @@ -1199,6 +1274,9 @@ LLView* LLButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *fa std::string image_overlay; node->getAttributeString("image_overlay", image_overlay); + std::string image_overlay_selected; + node->getAttributeString("image_overlay_selected", image_overlay_selected); + LLFontGL::HAlign image_overlay_alignment = LLFontGL::HCENTER; std::string image_overlay_alignment_string; if (node->hasAttribute("image_overlay_alignment")) @@ -1234,6 +1312,7 @@ LLView* LLButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *fa if(image_disabled != LLStringUtil::null) button->setImageDisabled(LLUI::getUIImage(image_disabled)); if(image_overlay != LLStringUtil::null) button->setImageOverlay(image_overlay, image_overlay_alignment); + if (!image_overlay_selected.empty()) button->mImageOverlaySelected = LLUI::getUIImage(image_overlay_selected); if (node->hasAttribute("halign")) { diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 1495740c6..812ea6de1 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -74,6 +74,87 @@ class LLButton : public LLUICtrl { public: + struct Params + : public LLInitParam::Block + { + // text label + Optional label_selected; + Optional label_shadow; + Optional auto_resize; + Optional use_ellipses; + + // images + Optional image_unselected, + image_selected, + image_hover_selected, + image_hover_unselected, + image_disabled_selected, + image_disabled, + image_flash, + image_pressed, + image_pressed_selected, + image_overlay, + image_overlay_selected; + + Optional image_overlay_alignment; + Optional image_overlay_enable; + + // colors + Optional label_color, + label_color_selected, + label_color_disabled, + label_color_disabled_selected, + image_color, + image_color_disabled, + image_overlay_color, + image_overlay_selected_color, + image_overlay_disabled_color, + flash_color; + + // layout + Optional pad_right; + Optional pad_left; + Optional pad_bottom; // under text label + + //image overlay paddings + Optional image_top_pad; + Optional image_bottom_pad; + + /** + * Space between image_overlay and label + */ + Optional imgoverlay_label_space; + + // callbacks + Optional click_callback, // alias -> commit_callback + mouse_down_callback, + mouse_up_callback, + mouse_held_callback; + + // misc + Optional is_toggle, + scale_image, + commit_on_return, + display_pressed_state; + + Optional hover_glow_amount; + //Optional held_down_delay; + + Optional use_draw_context_alpha; + + //Optional badge; + + Optional handle_right_mouse; + + Optional button_flash_enable; + Optional button_flash_count; + Optional button_flash_rate; + Optional fade_when_disabled; + Optional help_url; + + Params(); + }; + // simple button with text label LLButton(const std::string& name, const LLRect &rect = LLRect(), const std::string& control_name = std::string(), commit_callback_t commit_callback = NULL); @@ -86,6 +167,11 @@ public: const LLFontGL* mGLFont = NULL, const std::string& unselected_label = LLStringUtil::null, const std::string& selected_label = LLStringUtil::null ); + + void initFromParams(const Params& p); + static const LLButton::Params& getDefaultParams(); + LLButton(const Params& p = getDefaultParams()); + public: ~LLButton(); @@ -93,7 +179,6 @@ public: typedef boost::function button_callback_t; void addImageAttributeToXML(LLXMLNodePtr node, const LLPointer, const std::string& xmlTagName) const; - void init(const std::string& control_name); virtual LLXMLNodePtr getXML(bool save_children = true) const; static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); @@ -161,7 +246,6 @@ public: const std::string getLabelUnselected() const { return wstring_to_utf8str(mUnselectedLabel); } const std::string getLabelSelected() const { return wstring_to_utf8str(mSelectedLabel); } - void setImageColor(const std::string& color_control); void setImageColor(const LLColor4& c); /*virtual*/ void setColor(const LLColor4& c); @@ -205,9 +289,7 @@ public: void setImageDisabled(LLPointer image); void setImageDisabledSelected(LLPointer image); void setImageFlash(LLPointer image); - void setImagePressed(LLPointer image); - - static void onHeldDown(void *userdata); // to be called by gIdleCallbacks + void setHelpURLCallback(const std::string &help_url); const std::string& getHelpURL() const { return mHelpURL; } @@ -243,6 +325,7 @@ protected: S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called LLPointer mImageOverlay; + LLPointer mImageOverlaySelected; LLFontGL::HAlign mImageOverlayAlignment; LLUIColor mImageOverlayColor; LLUIColor mImageOverlaySelectedColor; diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 65a03b0b5..7a5141da2 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -436,6 +436,7 @@ void LLComboBox::setLabel(const LLStringExplicit& name) { if (!mSuppressTentative) mTextEntry->setTentative(mTextEntryTentative); } + mTextEntry->setCursor(0); // Be scrolled to the beginning! } if (!mAllowTextEntry) diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index dac5a0282..f5fa3ece2 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -35,8 +35,6 @@ #include "linden_common.h" -#include "llmultifloater.h" - #include "llfocusmgr.h" #include "lluictrlfactory.h" @@ -56,10 +54,13 @@ #include "llcontrol.h" #include "lltabcontainer.h" #include "v2math.h" +#include "lltrans.h" +#include "llmultifloater.h" #include "llfasttimer.h" #include "airecursive.h" #include "llnotifications.h" + const S32 MINIMIZED_WIDTH = 160; const S32 CLOSE_BOX_FROM_TOP = 1; // use this to control "jumping" behavior when Ctrl-Tabbing @@ -104,14 +105,14 @@ std::string LLFloater::sButtonNames[BUTTON_COUNT] = std::string LLFloater::sButtonToolTips[BUTTON_COUNT] = { #ifdef LL_DARWIN - "Close (Cmd-W)", //BUTTON_CLOSE + "BUTTON_CLOSE_DARWIN", //"Close (Cmd-W)", //BUTTON_CLOSE #else - "Close (Ctrl-W)", //BUTTON_CLOSE + "BUTTON_CLOSE_WIN", //"Close (Ctrl-W)", //BUTTON_CLOSE #endif - "Restore", //BUTTON_RESTORE - "Minimize", //BUTTON_MINIMIZE - "Tear Off", //BUTTON_TEAR_OFF - "Edit", //BUTTON_EDIT + "BUTTON_RESTORE", //"Restore", //BUTTON_RESTORE + "BUTTON_MINIMIZE", //"Minimize", //BUTTON_MINIMIZE + "BUTTON_TEAR_OFF", //"Tear Off", //BUTTON_TEAR_OFF + "BUTTON_EDIT", //"Edit", //BUTTON_EDIT }; @@ -130,6 +131,31 @@ LLFloater::handle_map_t LLFloater::sFloaterMap; LLFloaterView* gFloaterView = NULL; +//static +void LLFloater::initClass() +{ + // translate tooltips for floater buttons + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + sButtonToolTips[i] = LLTrans::getString( sButtonToolTips[i] ); + } + + LLControlVariable* ctrl = LLUI::sConfigGroup->getControl("ActiveFloaterTransparency"); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency)); + updateActiveFloaterTransparency(); + } + + ctrl = LLUI::sConfigGroup->getControl("InactiveFloaterTransparency"); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency)); + updateInactiveFloaterTransparency(); + } + +} + LLFloater::LLFloater() : //FIXME: we should initialize *all* member variables here LLPanel(), mAutoFocus(TRUE), @@ -355,6 +381,18 @@ void LLFloater::initFloater(const std::string& title, } } +// static +void LLFloater::updateActiveFloaterTransparency() +{ + sActiveControlTransparency = LLUI::sConfigGroup->getF32("ActiveFloaterTransparency"); +} + +// static +void LLFloater::updateInactiveFloaterTransparency() +{ + sInactiveControlTransparency = LLUI::sConfigGroup->getF32("InactiveFloaterTransparency"); +} + void LLFloater::addResizeCtrls() { // Resize bars (sides) @@ -720,11 +758,6 @@ void LLFloater::applyRectControl() void LLFloater::applyTitle() { - if (gNoRender) - { - return; - } - if (!mDragHandle) { return; @@ -753,7 +786,7 @@ void LLFloater::setTitle( const std::string& title ) applyTitle(); } -std::string LLFloater::getTitle() +std::string LLFloater::getTitle() const { if (mTitle.empty()) { @@ -771,7 +804,7 @@ void LLFloater::setShortTitle( const std::string& short_title ) applyTitle(); } -std::string LLFloater::getShortTitle() +std::string LLFloater::getShortTitle() const { if (mShortTitle.empty()) { @@ -783,8 +816,6 @@ std::string LLFloater::getShortTitle() } } - - BOOL LLFloater::canSnapTo(const LLView* other_view) { if (NULL == other_view) @@ -1023,7 +1054,8 @@ void LLFloater::setFocus( BOOL b ) if (b) { // only push focused floaters to front of stack if not in midst of ctrl-tab cycle - if (!getHost() && !((LLFloaterView*)getParent())->getCycleMode()) + LLFloaterView * parent = dynamic_cast(getParent()); + if (!getHost() && parent && !parent->getCycleMode()) { if (!isFrontmost()) { @@ -1040,6 +1072,7 @@ void LLFloater::setFocus( BOOL b ) last_focus->setFocus(TRUE); } } + updateTransparency(b ? TT_ACTIVE : TT_INACTIVE); } // virtual @@ -1081,8 +1114,9 @@ void LLFloater::setForeground(BOOL front) releaseFocus(); } - if (front || !LLUI::sConfigGroup->getBOOL("FloaterUnfocusedBackgroundOpaque")) // Singu Note: This can be removed when InactiveFloaterTransparency is added - setBackgroundOpaque( front ); + setBackgroundOpaque( front ); + // Singu Note: Upstream isn't doing this, I can't see where they were actually going inactive. Maybe setFocus(false) isn't being called, but we have parity there.. + updateTransparency(front || getIsChrome() ? TT_ACTIVE : TT_INACTIVE); } } @@ -1191,7 +1225,7 @@ void LLFloater::removeDependentFloater(LLFloater* floaterp) floaterp->mDependeeHandle = LLHandle(); } -BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons index) +BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index) { if( mButtonsEnabled[index] ) { @@ -1297,7 +1331,14 @@ void LLFloater::setFrontmost(BOOL take_focus) { // there are more than one floater view // so we need to query our parent directly - ((LLFloaterView*)getParent())->bringToFront(this, take_focus); + LLFloaterView * parent = dynamic_cast( getParent() ); + if (parent) + { + parent->bringToFront(this, take_focus); + } + + // Make sure to set the appropriate transparency type (STORM-732). + updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE); } } @@ -1432,42 +1473,45 @@ void LLFloater::onClickClose() // virtual void LLFloater::draw() { + const F32 alpha = getCurrentTransparency(); + // draw background if( isBackgroundVisible() ) { + drawShadow(this); + S32 left = LLPANEL_BORDER_WIDTH; S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH; S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH; S32 bottom = LLPANEL_BORDER_WIDTH; - static LLColor4 shadow_color = LLUI::sColorsGroup->getColor("ColorDropShadow"); - static F32 shadow_offset = (F32)LLUI::sConfigGroup->getS32("DropShadowFloater"); - if (!isBackgroundOpaque()) - { - shadow_offset *= 0.2f; - shadow_color.mV[VALPHA] *= 0.5f; - } - gl_drop_shadow(left, top, right, bottom, - shadow_color, - ll_round(shadow_offset)); - // No transparent windows in simple UI + LLColor4 color; if (isBackgroundOpaque()) { - gl_rect_2d( left, top, right, bottom, getBackgroundColor() ); + color = getBackgroundColor(); } else { - gl_rect_2d( left, top, right, bottom, getTransparentColor() ); + color = getTransparentColor(); } - if(gFocusMgr.childHasKeyboardFocus(this) && !getIsChrome() && !getCurrentTitle().empty()) { + // We're not using images, use old-school flat colors + gl_rect_2d( left, top, right, bottom, color % alpha ); + // draw highlight on title bar to indicate focus. RDW - const LLFontGL* font = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF ); - LLRect r = getRect(); - gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1, - LLUI::sColorsGroup->getColor("TitleBarFocusColor"), 0, TRUE); + if(gFocusMgr.childHasKeyboardFocus(this) + && !getIsChrome() + && !getCurrentTitle().empty()) + { + static auto titlebar_focus_color = LLUI::sColorsGroup->getColor("TitleBarFocusColor"); + + const LLFontGL* font = LLFontGL::getFontSansSerif(); + LLRect r = getRect(); + gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1, + titlebar_focus_color % alpha, 0, TRUE); + } } } @@ -1524,6 +1568,49 @@ void LLFloater::draw() } } +void LLFloater::drawShadow(LLPanel* panel) +{ + S32 left = LLPANEL_BORDER_WIDTH; + S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH; + S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH; + S32 bottom = LLPANEL_BORDER_WIDTH; + + static LLUICachedControl shadow_offset_S32 ("DropShadowFloater", 0); + static LLColor4 shadow_color = LLUI::sColorsGroup->getColor("ColorDropShadow"); + F32 shadow_offset = (F32)shadow_offset_S32; + + if (!panel->isBackgroundOpaque()) + { + shadow_offset *= 0.2f; + shadow_color.mV[VALPHA] *= 0.5f; + } + gl_drop_shadow(left, top, right, bottom, + shadow_color % getCurrentTransparency(), + ll_round(shadow_offset)); +} + +void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type) +{ + if (view) + { + if (view->isCtrl()) + { + static_cast(view)->setTransparencyType(transparency_type); + } + + for (LLView* pChild : *view->getChildList()) + { + if ((pChild->getChildCount()) || (pChild->isCtrl())) + updateTransparency(pChild, transparency_type); + } + } +} + +void LLFloater::updateTransparency(ETypeTransparency transparency_type) +{ + updateTransparency(this, transparency_type); +} + void LLFloater::setCanMinimize(BOOL can_minimize) { // if removing minimize/restore button programmatically, @@ -2319,7 +2406,7 @@ LLRect LLFloaterView::getSnapRect() const return snap_rect; } -LLFloater *LLFloaterView::getFocusedFloater() +LLFloater *LLFloaterView::getFocusedFloater() const { for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { @@ -2332,7 +2419,7 @@ LLFloater *LLFloaterView::getFocusedFloater() return NULL; } -LLFloater *LLFloaterView::getFrontmost() +LLFloater *LLFloaterView::getFrontmost() const { for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { @@ -2345,7 +2432,7 @@ LLFloater *LLFloaterView::getFrontmost() return NULL; } -LLFloater *LLFloaterView::getBackmost() +LLFloater *LLFloaterView::getBackmost() const { LLFloater* back_most = NULL; for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) @@ -2380,7 +2467,7 @@ void LLFloaterView::syncFloaterTabOrder() } } -LLFloater* LLFloaterView::getParentFloater(LLView* viewp) +LLFloater* LLFloaterView::getParentFloater(LLView* viewp) const { LLView* parentp = viewp->getParent(); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 0aeb11501..f53e2714f 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -94,16 +94,19 @@ class LLFloater : public LLPanel friend class LLFloaterView; friend class LLMultiFloater; public: - enum EFloaterButtons + enum EFloaterButton { - BUTTON_CLOSE, + BUTTON_CLOSE = 0, BUTTON_RESTORE, BUTTON_MINIMIZE, BUTTON_TEAR_OFF, BUTTON_EDIT, BUTTON_COUNT }; - + + // Load translations for tooltips for standard buttons + static void initClass(); + LLFloater(); LLFloater(const std::string& name); //simple constructor for data-driven initialization LLFloater( const std::string& name, const LLRect& rect, const std::string& title, @@ -164,9 +167,9 @@ public: void applyTitle(); const std::string& getCurrentTitle() const; void setTitle( const std::string& title); - std::string getTitle(); + std::string getTitle() const; void setShortTitle( const std::string& short_title ); - std::string getShortTitle(); + std::string getShortTitle() const; void setTitleVisible(bool visible); virtual void setMinimized(BOOL b); void moveResizeHandlesToFront(); @@ -174,8 +177,8 @@ public: void addDependentFloater(LLHandle dependent_handle, BOOL reposition = TRUE); LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); } void removeDependentFloater(LLFloater* dependent); - BOOL isMinimized() { return mMinimized; } - BOOL isFrontmost(); + BOOL isMinimized() const { return mMinimized; } + virtual BOOL isFrontmost(); BOOL isDependent() { return !mDependeeHandle.isDead(); } void setCanMinimize(BOOL can_minimize); void setCanClose(BOOL can_close); @@ -199,6 +202,7 @@ public: virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); virtual void draw(); + virtual void drawShadow(LLPanel* panel); virtual void onOpen() {} @@ -244,6 +248,8 @@ public: static BOOL getEditModeEnabled() { return sEditModeEnabled; } static LLMultiFloater* getFloaterHost() {return sHostp; } + void updateTransparency(ETypeTransparency transparency_type); + void enableResizeCtrls(bool enable, bool width = true, bool height = true); protected: @@ -259,21 +265,29 @@ protected: void destroy() { die(); } // Don't call this directly. You probably want to call close(). JC private: - void setForeground(BOOL b); // called only by floaterview void cleanupHandles(); // remove handles to dead floaters void createMinimizeButton(); void updateButtons(); void buildButtons(); - BOOL offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons index); + BOOL offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index); void addResizeCtrls(); void layoutResizeCtrls(); + static void updateActiveFloaterTransparency(); + static void updateInactiveFloaterTransparency(); + void updateTransparency(LLView* view, ETypeTransparency transparency_type); + LLRect mExpandedRect; + +protected: LLDragHandle* mDragHandle; LLResizeBar* mResizeBar[4]; LLResizeHandle* mResizeHandle[4]; LLButton *mMinimizeButton; + + LLButton* mButtons[BUTTON_COUNT]; +private: BOOL mCanTearOff; BOOL mMinimized; BOOL mForeground; @@ -294,10 +308,7 @@ private: handle_set_t mDependents; bool mDragOnLeft; - BOOL mButtonsEnabled[BUTTON_COUNT]; -protected: - LLButton* mButtons[BUTTON_COUNT]; -private: + bool mButtonsEnabled[BUTTON_COUNT]; F32 mButtonScale; BOOL mAutoFocus; LLHandle mSnappedTo; @@ -344,7 +355,7 @@ public: /*virtual*/ void draw(); /*virtual*/ LLRect getSnapRect() const; - void refresh(); + /*virtual*/ void refresh(); void getNewFloaterPosition( S32* left, S32* top ); void resetStartingFloaterPosition(); @@ -373,10 +384,10 @@ public: void minimizeAllChildren(); // - LLFloater* getFrontmost(); - LLFloater* getBackmost(); - LLFloater* getParentFloater(LLView* viewp); - LLFloater* getFocusedFloater(); + LLFloater* getFrontmost() const; + LLFloater* getBackmost() const; + LLFloater* getParentFloater(LLView* viewp) const; + LLFloater* getFocusedFloater() const; void syncFloaterTabOrder(); // Returns z order of child provided. 0 is closest, larger numbers diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 1be8be2b0..60983c9d4 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -33,10 +33,10 @@ const F32 FOCUS_FADE_TIME = 0.3f; LLFocusableElement::LLFocusableElement() -: mFocusLostCallback(NULL), - mFocusReceivedCallback(NULL), - mFocusChangedCallback(NULL), - mTopLostCallback(NULL) +: mFocusLostCallback(nullptr), + mFocusReceivedCallback(nullptr), + mFocusChangedCallback(nullptr), + mTopLostCallback(nullptr) { } @@ -146,14 +146,14 @@ struct LLFocusMgr::Impl LLFocusMgr gFocusMgr; LLFocusMgr::LLFocusMgr() -: mLockedView( NULL ), - mMouseCaptor( NULL ), - mKeyboardFocus( NULL ), - mLastKeyboardFocus( NULL ), - mDefaultKeyboardFocus( NULL ), - mLastDefaultKeyboardFocus( NULL ), +: mLockedView(nullptr ), + mMouseCaptor(nullptr ), + mKeyboardFocus(nullptr ), + mLastKeyboardFocus(nullptr ), + mDefaultKeyboardFocus(nullptr ), + mLastDefaultKeyboardFocus(nullptr ), mKeystrokesOnly(FALSE), - mTopCtrl( NULL ), + mTopCtrl(nullptr ), mAppHasFocus(TRUE), // Macs don't seem to notify us that we've gotten focus, so default to true mImpl(new LLFocusMgr::Impl) { @@ -163,22 +163,22 @@ LLFocusMgr::~LLFocusMgr() { mImpl->mFocusHistory.clear(); delete mImpl; - mImpl = NULL; + mImpl = nullptr; } void LLFocusMgr::releaseFocusIfNeeded( const LLView* view ) { if( childHasMouseCapture( view ) ) { - setMouseCapture( NULL ); + setMouseCapture(nullptr ); } if( childHasKeyboardFocus( view )) { if (view == mLockedView) { - mLockedView = NULL; - setKeyboardFocus( NULL ); + mLockedView = nullptr; + setKeyboardFocus(nullptr ); } else { @@ -188,7 +188,7 @@ void LLFocusMgr::releaseFocusIfNeeded( const LLView* view ) if( childIsTopCtrl( view ) ) { - setTopCtrl( NULL ); + setTopCtrl(nullptr); } } @@ -197,7 +197,7 @@ void LLFocusMgr::restoreDefaultKeyboardFocus(LLFocusableElement* current_default if (current_default_focus && mDefaultKeyboardFocus == current_default_focus) { setDefaultKeyboardFocus(mLastDefaultKeyboardFocus); - mLastDefaultKeyboardFocus = NULL; + mLastDefaultKeyboardFocus = nullptr; } } @@ -206,7 +206,7 @@ void LLFocusMgr::restoreKeyboardFocus(LLFocusableElement* current_focus) if (current_focus && mKeyboardFocus == current_focus) { setKeyboardFocus(mLastKeyboardFocus); - mLastKeyboardFocus = NULL; + mLastKeyboardFocus = nullptr; } } @@ -219,7 +219,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL focus_dirty = false; if (mLockedView && - (new_focus == NULL || + (new_focus == nullptr || (new_focus != mLockedView && dynamic_cast(new_focus) && !dynamic_cast(new_focus)->hasAncestor(mLockedView)))) @@ -291,7 +291,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL // If we've got a default keyboard focus, and the caller is // releasing keyboard focus, move to the default. - if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL) + if (mDefaultKeyboardFocus != nullptr && mKeyboardFocus == nullptr) { mDefaultKeyboardFocus->setFocus(TRUE); } @@ -343,7 +343,7 @@ BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const // Returns TRUE is parent or any descedent of parent is the mouse captor. BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const { - if( mMouseCaptor && dynamic_cast(mMouseCaptor) != NULL ) + if( mMouseCaptor && dynamic_cast(mMouseCaptor) != nullptr ) { LLView* captor_view = (LLView*)mMouseCaptor; while( captor_view ) @@ -364,23 +364,24 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* f // in order to unlock it if (focus == mLockedView) { - mLockedView = NULL; + mLockedView = nullptr; } + if (mKeyboardFocus == focus) { - mKeyboardFocus = NULL; + mKeyboardFocus = nullptr; } if (mLastKeyboardFocus == focus) { - mLastKeyboardFocus = NULL; + mLastKeyboardFocus = nullptr; } if (mDefaultKeyboardFocus == focus) { - mDefaultKeyboardFocus = NULL; + mDefaultKeyboardFocus = nullptr; } if (mLastDefaultKeyboardFocus == focus) { - mLastDefaultKeyboardFocus = NULL; + mLastDefaultKeyboardFocus = nullptr; } } @@ -430,7 +431,7 @@ void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor { if( mMouseCaptor == captor ) { - mMouseCaptor = NULL; + mMouseCaptor = nullptr; } } @@ -470,7 +471,7 @@ void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view ) { if( mTopCtrl == top_view ) { - mTopCtrl = NULL; + mTopCtrl = nullptr; } } @@ -481,7 +482,7 @@ void LLFocusMgr::lockFocus() void LLFocusMgr::unlockFocus() { - mLockedView = NULL; + mLockedView = nullptr; } F32 LLFocusMgr::getFocusFlashAmt() const @@ -516,7 +517,7 @@ void LLFocusMgr::setAppHasFocus(BOOL focus) // release focus from "top ctrl"s, which generally hides them if (!focus && mTopCtrl) { - setTopCtrl(NULL); + setTopCtrl(nullptr); } mAppHasFocus = focus; } @@ -532,7 +533,7 @@ LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const return static_cast(found_it->second.get()); } } - return NULL; + return nullptr; } void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root) diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 66b8d64ec..1db2638e0 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1497,8 +1497,9 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) break; case KEY_RETURN: + if (getCommitOnReturn()) onCommit(); // store sent line in history - updateHistory(); + else updateHistory(); break; case KEY_ESCAPE: diff --git a/indra/llui/llloadingindicator.cpp b/indra/llui/llloadingindicator.cpp new file mode 100644 index 000000000..915571874 --- /dev/null +++ b/indra/llui/llloadingindicator.cpp @@ -0,0 +1,104 @@ +// 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 llloadingindicator.cpp + * @brief Perpetual loading indicator + * + * $LicenseInfo:firstyear=2010&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 "llloadingindicator.h" + +// Linden library includes +#include "llsingleton.h" + +// Project includes +#include "lluictrlfactory.h" +#include "lluiimage.h" + +static LLRegisterWidget r("loading_indicator"); + +/////////////////////////////////////////////////////////////////////////////// +// LLLoadingIndicator class +/////////////////////////////////////////////////////////////////////////////// + +LLLoadingIndicator::LLLoadingIndicator(const Params& p) +: LLUICtrl(p), + mImagesPerSec(p.images_per_sec > 0 ? p.images_per_sec : 1.0f), + mCurImageIdx(0) +{ +} + +void LLLoadingIndicator::initFromParams(const Params& p) +{ + for (LLUIImage* image : p.images().image) + { + mImages.push_back(image); + } + + if (mImages.empty()) // Singu Note: We don't have default widgets yet, so let's just handle this here + for (int i = 1; i <= 12; ++i) + mImages.push_back(LLUI::getUIImage(llformat("Progress_%d", i))); + + // Start timer for switching images. + start(); +} + +void LLLoadingIndicator::draw() +{ + // Time to switch to the next image? + if (mImageSwitchTimer.getStarted() && mImageSwitchTimer.hasExpired()) + { + // Switch to the next image. + if (!mImages.empty()) + { + mCurImageIdx = (mCurImageIdx + 1) % mImages.size(); + } + + // Restart timer. + start(); + } + + LLUIImagePtr cur_image = mImages.empty() ? LLUIImagePtr(NULL) : mImages[mCurImageIdx]; + + // Draw current image. + if( cur_image.notNull() ) + { + cur_image->draw(getLocalRect(), LLColor4::white % getDrawContext().mAlpha); + } + + LLUICtrl::draw(); +} + +void LLLoadingIndicator::stop() +{ + mImageSwitchTimer.stop(); +} + +void LLLoadingIndicator::start() +{ + mImageSwitchTimer.start(); + F32 period = 1.0f / (mImages.size() * mImagesPerSec); + mImageSwitchTimer.setTimerExpirySec(period); +} diff --git a/indra/llui/llloadingindicator.h b/indra/llui/llloadingindicator.h new file mode 100644 index 000000000..b5e86c0ac --- /dev/null +++ b/indra/llui/llloadingindicator.h @@ -0,0 +1,104 @@ +/** + * @file llloadingindicator.h + * @brief Perpetual loading indicator + * + * $LicenseInfo:firstyear=2010&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_LLLOADINGINDICATOR_H +#define LL_LLLOADINGINDICATOR_H + +#include "lluictrl.h" +#include "lluiimage.h" + +/////////////////////////////////////////////////////////////////////////////// +// class LLLoadingIndicator +/////////////////////////////////////////////////////////////////////////////// + +/** + * Perpetual loading indicator (a la MacOSX or YouTube) + * + * Number of rotations per second can be overridden + * with the "images_per_sec" parameter. + * + * Can start/stop spinning. + * + * @see start() + * @see stop() + */ +class LLLoadingIndicator +: public LLUICtrl +{ + LOG_CLASS(LLLoadingIndicator); +public: + + struct Images : public LLInitParam::Block + { + Multiple image; + + Images() + : image("image") + {} + }; + + struct Params : public LLInitParam::Block + { + Optional images_per_sec; + Optional > images; + + Params() + : images_per_sec("images_per_sec", 1.0f), + images("images") + {} + }; + + virtual ~LLLoadingIndicator() {} + + // llview overrides + void draw() override; + + /** + * Stop spinning. + */ + void stop(); + + /** + * Start spinning. + */ + void start(); + + void reset() { mCurImageIdx = 0; } + +private: + LLLoadingIndicator(const Params&); + void initFromParams(const Params&); + + friend class LLUICtrlFactory; + + F32 mImagesPerSec; + S8 mCurImageIdx; + LLFrameTimer mImageSwitchTimer; + + std::vector mImages; +}; + +#endif // LL_LLLOADINGINDICATOR_H diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 3dd787089..4b38899d3 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -70,7 +70,7 @@ using namespace LLOldEvents; // static -LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL; +LLMenuHolderGL *LLMenuGL::sMenuContainer = nullptr; S32 MENU_BAR_HEIGHT = 0; S32 MENU_BAR_WIDTH = 0; @@ -258,7 +258,7 @@ BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) // if not, it will be added to the list BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) { - LLKeyBinding *accelerator = NULL; + LLKeyBinding *accelerator = nullptr; if (mAcceleratorKey != KEY_NONE) { @@ -268,19 +268,37 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) accelerator = *list_it; if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS))) { + + // *NOTE: get calling code to throw up warning or route + // warning messages back to app-provided output + // std::string warning; + // warning.append("Duplicate key binding <"); + // appendAcceleratorString( warning ); + // warning.append("> for menu items:\n "); + // warning.append(accelerator->mName); + // warning.append("\n "); + // warning.append(mLabel); + + // LL_WARNS() << warning << LL_ENDL; + // LLAlertDialog::modalAlert(warning); return FALSE; } } if (!accelerator) { - accelerator = new LLKeyBinding; - if (accelerator) + try { + accelerator = new LLKeyBinding; accelerator->mKey = mAcceleratorKey; accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS); // accelerator->mName = mLabel; + listp->push_back(accelerator);//addData(accelerator); + } + catch (const std::bad_alloc& e) + { + LL_WARNS() << "Failed to allocate memory for keybinding with exception: " << e.what() << LL_ENDL; + return FALSE; } - listp->push_back(accelerator);//addData(accelerator); } } return TRUE; @@ -681,7 +699,7 @@ class LLMenuItemVerticalSeparatorGL public: LLMenuItemVerticalSeparatorGL( void ); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } + BOOL handleMouseDown(S32 x, S32 y, MASK mask) override { return FALSE; } }; LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void ) @@ -733,7 +751,7 @@ LLFloater* LLMenuItemTearOffGL::getParentFloater() } } - return NULL; + return nullptr; } void LLMenuItemTearOffGL::onCommit() @@ -4873,7 +4891,7 @@ void LLContextMenu::hide() { mHoverItem->setHighlight( FALSE ); } - mHoverItem = NULL; + mHoverItem = nullptr; } @@ -4983,7 +5001,11 @@ bool LLContextMenu::addChild(LLView* view, S32 tab_group) if (LLContextMenu* context = dynamic_cast(view)) return appendContextSubMenu(context); if (LLMenuItemGL* item = dynamic_cast(view)) + { + if (!mItems.empty() && mItems.back()->getType() == "separator" && item->getType() == "separator") // Singu TODO: Profile this? Does it matter? + item->setVisible(false); return append(item); + } if (LLMenuGL* menu = dynamic_cast(view)) return appendMenu(menu); return false; @@ -5024,7 +5046,7 @@ LLPieMenu::LLPieMenu(const std::string& name, const std::string& label) // Separators on pie menus are invisible bool LLPieMenu::addChild(LLView* view, S32 tab_group) { - if (LLContextMenu::addChild(view, tab_group)) + if (LLContextMenu::addChild(view, tab_group) && view->getVisible()) { LLMenuItemSeparatorGL* sep = dynamic_cast(view); if(sep) diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 4c5811321..7c3626d35 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -580,6 +580,7 @@ public: void resetScrollPositionOnShow(bool reset_scroll_pos) { mResetScrollPositionOnShow = reset_scroll_pos; } bool isScrollPositionOnShowReset() { return mResetScrollPositionOnShow; } protected: + friend class LLTextEditor; void createSpilloverBranch(); void cleanupSpilloverBranch(); // Add the menu item to this menu. diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 5c9dfb74a..1f2f4cec4 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -284,7 +284,8 @@ LLNotificationForm::LLNotificationForm(const LLSD& sd) } else { - LL_WARNS() << "Invalid form data " << sd << LL_ENDL; + if (!sd.isUndefined()) + LL_WARNS() << "Invalid form data " << sd << LL_ENDL; mFormData = LLSD::emptyArray(); } } diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index b587de148..e311daa1c 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -196,14 +196,16 @@ void LLPanel::draw() // draw background if( mBgVisible ) { - LLRect local_rect = getLocalRect(); + F32 alpha = getCurrentTransparency(); + + LLRect&& local_rect = getLocalRect(); if (mBgOpaque ) { - gl_rect_2d( local_rect, mBgColorOpaque ); + gl_rect_2d( local_rect, mBgColorOpaque % alpha); } else { - gl_rect_2d( local_rect, mBgColorAlpha ); + gl_rect_2d( local_rect, mBgColorAlpha % alpha); } } @@ -439,8 +441,31 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac std::string name("panel"); node->getAttributeString("name", name); - LLPanel* panelp = factory->createFactoryPanel(name); + std::string class_attr; + node->getAttributeString("class", class_attr); + + LLPanel* panelp = NULL; + + { + if(!class_attr.empty()) + { + panelp = LLRegisterPanelClass::instance().createPanelClass(class_attr); + } + + if (!panelp) + { + panelp = factory->createFactoryPanel(name); + /* Singu TODO: Future? + llassert(panelp); + + if (!panelp) + { + return NULL; // :( + }*/ + } + } LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION); + // Fall back on a default panel, if there was no special factory. if (!panelp) { diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index d2ca151e5..f379cd2a6 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -39,8 +39,8 @@ #include "lluictrl.h" #include "llbutton.h" #include "lllineeditor.h" -#include "lluiimage.h" #include "llviewborder.h" +#include "lluiimage.h" #include "lluistring.h" #include "v4color.h" #include @@ -142,6 +142,7 @@ public: CommitCallbackRegistry::ScopedRegistrar& getCommitCallbackRegistrar() { return mCommitCallbackRegistrar; } EnableCallbackRegistry::ScopedRegistrar& getEnableCallbackRegistrar() { return mEnableCallbackRegistrar; } + virtual BOOL initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); void initChildrenXML(LLXMLNodePtr node, LLUICtrlFactory* factory); void setPanelParameters(LLXMLNodePtr node, LLView *parentp); @@ -151,6 +152,7 @@ public: std::string getString(const std::string& name) const; // ** Wrappers for setting child properties by name ** -TomY + // WARNING: These are deprecated, please use getChild("name")->doStuff() idiom instead // LLView void childSetVisible(const std::string& name, bool visible); @@ -196,7 +198,7 @@ public: BOOL childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text); BOOL childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text); BOOL childSetToolTipArg(const std::string& id, const std::string& key, const LLStringExplicit& text); - + // LLSlider / LLMultiSlider / LLSpinCtrl void childSetMinValue(const std::string& id, LLSD min_value); void childSetMaxValue(const std::string& id, LLSD max_value); @@ -228,6 +230,7 @@ public: static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); boost::signals2::connection setVisibleCallback( const commit_signal_t::slot_type& cb ); + protected: // Override to set not found list LLButton* getDefaultButton() { return mDefaultBtn; } @@ -262,4 +265,56 @@ private: }; // end class LLPanel +typedef boost::function LLPanelClassCreatorFunc; + +// local static instance for registering a particular panel class + +class LLRegisterPanelClass +: public LLSingleton< LLRegisterPanelClass > +{ +public: + // reigister with either the provided builder, or the generic templated builder + void addPanelClass(const std::string& tag,LLPanelClassCreatorFunc func) + { + mPanelClassesNames[tag] = func; + } + + LLPanel* createPanelClass(const std::string& tag) + { + param_name_map_t::iterator iT = mPanelClassesNames.find(tag); + if(iT == mPanelClassesNames.end()) + return 0; + return iT->second(); + } + template + static T* defaultPanelClassBuilder() + { + T* pT = new T(); + return pT; + } + +private: + typedef std::map< std::string, LLPanelClassCreatorFunc> param_name_map_t; + + param_name_map_t mPanelClassesNames; +}; + + +// local static instance for registering a particular panel class +template + class LLPanelInjector +{ +public: + // register with either the provided builder, or the generic templated builder + LLPanelInjector(const std::string& tag); +}; + + +template + LLPanelInjector::LLPanelInjector(const std::string& tag) +{ + LLRegisterPanelClass::instance().addPanelClass(tag,&LLRegisterPanelClass::defaultPanelClassBuilder); +} + + #endif diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index 8e7e886ef..10beb2477 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -54,7 +54,6 @@ LLSlider::LLSlider( F32 min_value, F32 max_value, F32 increment, - BOOL volume, const std::string& control_name) : LLUICtrl( name, rect, TRUE, commit_callback, @@ -64,7 +63,6 @@ LLSlider::LLSlider( mMinValue( min_value ), mMaxValue( max_value ), mIncrement( increment ), - mVolumeSlider( volume ), mMouseOffset( 0 ), mTrackColor( LLUI::sColorsGroup->getColor( "SliderTrackColor" ) ), mThumbOutlineColor( LLUI::sColorsGroup->getColor( "SliderThumbOutlineColor" ) ), @@ -172,7 +170,7 @@ BOOL LLSlider::handleMouseUp(S32 x, S32 y, MASK mask) if( hasMouseCapture() ) { - gFocusMgr.setMouseCapture( NULL ); + gFocusMgr.setMouseCapture(nullptr ); if (mMouseUpSignal) (*mMouseUpSignal)( this, getValueF32() ); @@ -229,15 +227,12 @@ BOOL LLSlider::handleKeyHere(KEY key, MASK mask) BOOL handled = FALSE; switch(key) { - case KEY_UP: case KEY_DOWN: - // eat up and down keys to be consistent - handled = TRUE; - break; case KEY_LEFT: setValueAndCommit(getValueF32() - getIncrement()); handled = TRUE; break; + case KEY_UP: case KEY_RIGHT: setValueAndCommit(getValueF32() + getIncrement()); handled = TRUE; @@ -248,6 +243,16 @@ BOOL LLSlider::handleKeyHere(KEY key, MASK mask) return handled; } +BOOL LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + F32 old_val = getValueF32(); + F32 new_val = old_val - clicks * getIncrement(); + setValueAndCommit(new_val); + // Singu Note: We have to tell the viewer we're done dragging if it cares + if (mMouseUpSignal && old_val != new_val) (*mMouseUpSignal)(this, new_val); + return TRUE; +} + void LLSlider::draw() { F32 alpha = getDrawContext().mAlpha; @@ -276,7 +281,7 @@ void LLSlider::draw() mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor() % alpha, gFocusMgr.getFocusFlashWidth()); } - if( hasMouseCapture() ) + if( hasMouseCapture() ) // currently clicking on slider { // Show ghost where thumb was before dragging began. if (mThumbImage.notNull()) @@ -310,20 +315,11 @@ LLXMLNodePtr LLSlider::getXML(bool save_children) const { LLXMLNodePtr node = LLUICtrl::getXML(); - if (mVolumeSlider) - { - node->setName(LL_VOLUME_SLIDER_CTRL_TAG); - } - else - { - node->setName(LL_SLIDER_TAG); - } - + node->setName(LL_SLIDER_TAG); node->createChild("initial_val", TRUE)->setFloatValue(getInitialValue()); node->createChild("min_val", TRUE)->setFloatValue(getMinValue()); node->createChild("max_val", TRUE)->setFloatValue(getMaxValue()); node->createChild("increment", TRUE)->setFloatValue(getIncrement()); - node->createChild("volume", TRUE)->setBoolValue(mVolumeSlider); return node; } @@ -358,17 +354,13 @@ LLView* LLSlider::fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFacto F32 increment = 0.1f; node->getAttributeF32("increment", increment); - BOOL volume = node->hasName("volume_slider") ? TRUE : FALSE; - node->getAttributeBOOL("volume", volume); - LLSlider* slider = new LLSlider("slider_bar", rect, NULL, initial_value, min_value, max_value, - increment, - volume); + increment); slider->initFromXML(node, parent); diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h index 59bda673f..4fe90193e 100644 --- a/indra/llui/llslider.h +++ b/indra/llui/llslider.h @@ -48,7 +48,6 @@ public: F32 min_value, F32 max_value, F32 increment, - BOOL volume, //TODO: create a "volume" slider sub-class or just use image art, no? -MG const std::string& control_name = LLStringUtil::null ); virtual LLXMLNodePtr getXML(bool save_children = true) const; @@ -75,12 +74,12 @@ public: boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); - - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual void draw(); + BOOL handleHover(S32 x, S32 y, MASK mask) override; + BOOL handleMouseUp(S32 x, S32 y, MASK mask) override; + BOOL handleMouseDown(S32 x, S32 y, MASK mask) override; + BOOL handleKeyHere(KEY key, MASK mask) override; + BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) override; + void draw() override; private: void setValueAndCommit(F32 value); @@ -92,7 +91,6 @@ private: F32 mMaxValue; F32 mIncrement; - BOOL mVolumeSlider; S32 mMouseOffset; LLRect mDragStartThumbRect; diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index be2562e6d..0f164711e 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -59,7 +59,6 @@ LLSliderCtrl::LLSliderCtrl(const std::string& name, const LLRect& rect, S32 text_left, BOOL show_text, BOOL can_edit_text, - BOOL volume, commit_callback_t commit_callback, F32 initial_value, F32 min_value, F32 max_value, F32 increment, const std::string& control_which) @@ -67,7 +66,6 @@ LLSliderCtrl::LLSliderCtrl(const std::string& name, const LLRect& rect, mFont(font), mShowText( show_text ), mCanEditText( can_edit_text ), - mVolumeSlider( volume ), mPrecision( 3 ), mLabelBox( NULL ), mLabelWidth( label_width ), @@ -105,7 +103,7 @@ LLSliderCtrl::LLSliderCtrl(const std::string& name, const LLRect& rect, mSlider = new LLSlider(std::string("slider"), slider_rect, boost::bind(&LLSliderCtrl::onSliderCommit,_1,_2), - initial_value, min_value, max_value, increment, volume, + initial_value, min_value, max_value, increment, control_which ); addChild( mSlider ); @@ -387,8 +385,6 @@ LLXMLNodePtr LLSliderCtrl::getXML(bool save_children) const node->createChild("show_text", TRUE)->setBoolValue(mShowText); node->createChild("can_edit_text", TRUE)->setBoolValue(mCanEditText); - - node->createChild("volume", TRUE)->setBoolValue(mVolumeSlider); node->createChild("decimal_digits", TRUE)->setIntValue(mPrecision); @@ -438,9 +434,6 @@ LLView* LLSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory BOOL can_edit_text = FALSE; node->getAttributeBOOL("can_edit_text", can_edit_text); - BOOL volume = FALSE; - node->getAttributeBOOL("volume", volume); - F32 initial_value = 0.f; node->getAttributeF32("initial_val", initial_value); @@ -497,7 +490,6 @@ LLView* LLSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory rect.getWidth() - text_left, show_text, can_edit_text, - volume, NULL, initial_value, min_value, diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h index a5bc3488c..0b173a531 100644 --- a/indra/llui/llsliderctrl.h +++ b/indra/llui/llsliderctrl.h @@ -57,7 +57,6 @@ public: S32 text_left, BOOL show_text, BOOL can_edit_text, - BOOL volume, //TODO: create a "volume" slider sub-class or just use image art, no? -MG commit_callback_t commit_callback, F32 initial_value, F32 min_value, F32 max_value, F32 increment, const std::string& control_which = LLStringUtil::null ); @@ -125,8 +124,7 @@ private: const LLFontGL* mFont; BOOL mShowText; BOOL mCanEditText; - BOOL mVolumeSlider; - + S32 mPrecision; LLTextBox* mLabelBox; S32 mLabelWidth; diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp index 751156a31..dca6e79d0 100644 --- a/indra/llui/llstatbar.cpp +++ b/indra/llui/llstatbar.cpp @@ -175,17 +175,15 @@ void LLStatBar::draw() LLFontGL::getFontMonospace()->renderUTF8(mLabel, 0, 0, getRect().getHeight(), LLColor4(1.f, 1.f, 1.f, 1.f), LLFontGL::LEFT, LLFontGL::TOP); - std::string value_format; + std::string value_str; if (!mUnitLabel.empty()) { - value_format = llformat( "%%.%df%%s", mPrecision); - value_str = llformat( value_format.c_str(), mValue, mUnitLabel.c_str()); + value_str = llformat( "%.*f%s", mPrecision, mValue, mUnitLabel.c_str()); } else { - value_format = llformat( "%%.%df", mPrecision); - value_str = llformat( value_format.c_str(), mValue); + value_str = llformat("%.*f", mPrecision, mValue); } // Draw the value. @@ -193,11 +191,8 @@ void LLStatBar::draw() LLColor4(1.f, 1.f, 1.f, 0.5f), LLFontGL::RIGHT, LLFontGL::TOP); - value_format = llformat( "%%.%df", mPrecision); if (mDisplayBar) { - std::string tick_label; - // Draw the tick marks. F32 tick_value; top = bar_top; @@ -220,9 +215,8 @@ void LLStatBar::draw() right = left + tick_width; gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.25f)); - tick_label = llformat( value_format.c_str(), tick_value); // draw labels for the tick marks - LLFontGL::getFontMonospace()->renderUTF8(tick_label, 0, left - 1, bar_top - bar_height - tick_height, + LLFontGL::getFontMonospace()->renderUTF8(llformat("%.*f", mPrecision, tick_value), 0, left - 1, bar_top - bar_height - tick_height, LLColor4(1.f, 1.f, 1.f, 0.5f), LLFontGL::LEFT, LLFontGL::TOP); } diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 1db493c01..3996470be 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -43,6 +43,7 @@ #include "lluictrlfactory.h" #include "lluiimage.h" #include "llurlaction.h" +#include "llurlregistry.h" #include "llrect.h" #include "llfocusmgr.h" #include "lltimer.h" @@ -64,6 +65,7 @@ #include "lltextparser.h" #include "lldir.h" #include +#include "llmemberlistener.h" #include "llmenugl.h" #include "../newview/lgghunspell_wrapper.h" @@ -95,9 +97,6 @@ const S32 PREEDIT_STANDOUT_GAP = 1; const S32 PREEDIT_STANDOUT_POSITION = 2; const S32 PREEDIT_STANDOUT_THICKNESS = 2; - -LLColor4 LLTextEditor::mLinkColor = LLColor4::blue; - /////////////////////////////////////////////////////////////////// class LLTextEditor::LLTextCmdInsert : public LLTextEditor::LLTextCmd @@ -245,47 +244,51 @@ private: /////////////////////////////////////////////////////////////////// +LLTextEditor::is_friend_signal_t* LLTextEditor::mIsFriendSignal = nullptr; +LLTextEditor::is_blocked_signal_t* LLTextEditor::mIsObjectBlockedSignal = nullptr; -LLTextEditor::LLTextEditor( - const std::string& name, - const LLRect& rect, +LLTextEditor::LLTextEditor( + const std::string& name, + const LLRect& rect, S32 max_length, // In bytes - const std::string &default_text, + const std::string &default_text, const LLFontGL* font, - BOOL allow_embedded_items) - : - LLUICtrl( name, rect, TRUE, NULL, FOLLOWS_TOP | FOLLOWS_LEFT ), + BOOL allow_embedded_items, + bool parse_html) + : + LLUICtrl(name, rect, TRUE, NULL, FOLLOWS_TOP | FOLLOWS_LEFT), mTextIsUpToDate(TRUE), - mMaxTextByteLength( max_length ), + mMaxTextByteLength(max_length), mPopupMenuHandle(), mBaseDocIsPristine(TRUE), - mPristineCmd( NULL ), - mLastCmd( NULL ), - mCursorPos( 0 ), - mIsSelecting( FALSE ), - mSelectionStart( 0 ), - mSelectionEnd( 0 ), - mScrolledToBottom( TRUE ), - mOnScrollEndCallback( NULL ), - mOnScrollEndData( NULL ), - mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ), - mFgColor( LLUI::sColorsGroup->getColor( "TextFgColor" ) ), - mDefaultColor( LLUI::sColorsGroup->getColor( "TextDefaultColor" ) ), - mReadOnlyFgColor( LLUI::sColorsGroup->getColor( "TextFgReadOnlyColor" ) ), - mWriteableBgColor( LLUI::sColorsGroup->getColor( "TextBgWriteableColor" ) ), - mReadOnlyBgColor( LLUI::sColorsGroup->getColor( "TextBgReadOnlyColor" ) ), - mFocusBgColor( LLUI::sColorsGroup->getColor( "TextBgFocusColor" ) ), - mReadOnly(FALSE), - mWordWrap( FALSE ), - mShowLineNumbers ( FALSE ), - mTabsToNextField( TRUE ), - mCommitOnFocusLost( FALSE ), - mHideScrollbarForShortDocs( FALSE ), - mTakesNonScrollClicks( TRUE ), - mTrackBottom( FALSE ), - mAllowEmbeddedItems( allow_embedded_items ), + mPristineCmd(NULL), + mLastCmd(NULL), + mCursorPos(0), + mIsSelecting(FALSE), + mSelectionStart(0), + mSelectionEnd(0), + mScrolledToBottom(TRUE), + mOnScrollEndCallback(NULL), + mOnScrollEndData(NULL), + mCursorColor(LLUI::sColorsGroup->getColor("TextCursorColor")), + mFgColor(LLUI::sColorsGroup->getColor("TextFgColor")), + mDefaultColor(LLUI::sColorsGroup->getColor("TextDefaultColor")), + mReadOnlyFgColor(LLUI::sColorsGroup->getColor("TextFgReadOnlyColor")), + mWriteableBgColor(LLUI::sColorsGroup->getColor("TextBgWriteableColor")), + mReadOnlyBgColor(LLUI::sColorsGroup->getColor("TextBgReadOnlyColor")), + mFocusBgColor(LLUI::sColorsGroup->getColor("TextBgFocusColor")), + mLinkColor(nullptr), + mReadOnly(parse_html), + mWordWrap(FALSE), + mShowLineNumbers(FALSE), + mTabsToNextField(TRUE), + mCommitOnFocusLost(FALSE), + mHideScrollbarForShortDocs(FALSE), + mTakesNonScrollClicks(TRUE), + mTrackBottom(FALSE), + mAllowEmbeddedItems(allow_embedded_items), mAcceptCallingCardNames(FALSE), - mHandleEditKeysDirectly( FALSE ), + mHandleEditKeysDirectly(FALSE), mMouseDownX(0), mMouseDownY(0), mLastSelectionX(-1), @@ -312,56 +315,39 @@ LLTextEditor::LLTextEditor( updateTextRect(); - S32 line_height = ll_round( mGLFont->getLineHeight() ); + S32 line_height = ll_round(mGLFont->getLineHeight()); S32 page_size = mTextRect.getHeight() / line_height; // Init the scrollbar LLRect scroll_rect; - scroll_rect.setOriginAndSize( + scroll_rect.setOriginAndSize( getRect().getWidth() - SCROLLBAR_SIZE, 1, SCROLLBAR_SIZE, getRect().getHeight() - 1); S32 lines_in_doc = getLineCount(); - mScrollbar = new LLScrollbar( std::string("Scrollbar"), scroll_rect, + mScrollbar = new LLScrollbar(std::string("Scrollbar"), scroll_rect, LLScrollbar::VERTICAL, - lines_in_doc, - 0, + lines_in_doc, + 0, page_size, NULL); mScrollbar->setFollowsRight(); mScrollbar->setFollowsTop(); mScrollbar->setFollowsBottom(); - mScrollbar->setEnabled( TRUE ); - mScrollbar->setVisible( TRUE ); + mScrollbar->setEnabled(TRUE); + mScrollbar->setVisible(TRUE); mScrollbar->setOnScrollEndCallback(mOnScrollEndCallback, mOnScrollEndData); addChild(mScrollbar); - mBorder = new LLViewBorder( std::string("text ed border"), LLRect(0, getRect().getHeight(), getRect().getWidth(), 0), LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, UI_TEXTEDITOR_BORDER ); - addChild( mBorder ); + mBorder = new LLViewBorder(std::string("text ed border"), LLRect(0, getRect().getHeight(), getRect().getWidth(), 0), LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, UI_TEXTEDITOR_BORDER); + addChild(mBorder); + mParseHTML = parse_html; appendText(default_text, FALSE, FALSE); - + resetDirty(); // Update saved text state - mParseHTML=FALSE; - mHTML.clear(); - // make the popup menu available - //LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_texteditor.xml", parent_view); - LLMenuGL* menu = new LLMenuGL("rclickmenu"); - /*if (!menu) - { - menu = new LLMenuGL(LLStringUtil::null); - }*/ - menu->addChild(new LLMenuItemCallGL("Cut", context_cut, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Copy", context_copy, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Paste", context_paste, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Delete", context_delete, NULL, this)); - menu->addChild(new LLMenuItemCallGL("Select All", context_selectall, NULL, this)); - menu->addSeparator(); - menu->setCanTearOff(FALSE); - menu->setVisible(FALSE); - mPopupMenuHandle = menu->getHandle(); setCommitCallback(boost::bind(&LLTextEditor::setControlValue, this, _2)); } @@ -369,23 +355,100 @@ LLTextEditor::LLTextEditor( LLTextEditor::~LLTextEditor() { gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() + if (mLinkColor) + { + delete mLinkColor; + mLinkColor = nullptr; + } // Scrollbar is deleted by LLView std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); - //LLView::deleteViewByHandle(mPopupMenuHandle); + mSegments.clear(); + auto menu = mPopupMenuHandle.get(); + if (menu) + { + menu->die(); + mPopupMenuHandle.markDead(); + } + /* Singu Note: Static this, we'll use it wherever we can! + delete mIsFriendSignal; + delete mIsObjectBlockedSignal;*/ } -void LLTextEditor::context_cut(void* data) + +const std::string& LLTextEditor::getMenuSegmentUrl() const { - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->cut(); + auto segment = getSegmentAtLocalPos(mLastContextMenuX, mLastContextMenuY); + auto style = segment->getStyle(); + return style ? style->getLinkHREF() : LLStringUtil::null; } -void LLTextEditor::context_copy(void* data) + +static LLTextEditor* get_focused_text_editor() { - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->copy(); + auto* list = dynamic_cast(gFocusMgr.getKeyboardFocus()); + llassert(list); // This listener only applies to lists + return list; } +class ContextText : public LLMemberListener +{ + bool handleEvent(LLPointer, const LLSD& userdata) override + { + auto text = get_focused_text_editor(); + const auto& op = userdata.asStringRef(); + if (op == "Cut") text->cut(); + else if (op == "Copy") text->copy(); + else if (op == "CopyRaw") text->copyRaw(); + else if (op == "Paste") text->paste(); + else if (op == "Delete") text->doDelete(); + else if (op == "SelectAll") text->selectAll(); + return true; + } +}; + +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& op = userdata.asStringRef(); + if (op == "Open") LLUrlAction::openURL(url); + else if (op == "OpenInternal") LLUrlAction::openURLInternal(url); + else if (op == "OpenExternal") LLUrlAction::openURLExternal(url); + else if (op == "Execute") LLUrlAction::executeSLURL(url, true); + 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); + return true; + } +}; + +class ContextUrlCopy : public LLMemberListener +{ + + bool handleEvent(LLPointer, const LLSD& userdata) override + { + const auto& url = get_focused_url(); + const auto& type = userdata.asStringRef(); + // Empty works like avatar and group, "object" is an object (you needed to be told this) + const auto& id = type.empty() ? LLUrlAction::getUserID(url) : LLUrlAction::getObjectId(url); + LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(id)); + return true; + } +}; + void LLTextEditor::spell_correct(void* data) { @@ -463,21 +526,12 @@ void LLTextEditor::spell_add(void* data) } } - -void LLTextEditor::context_paste(void* data) +//static +void LLTextEditor::addMenuListeners() { - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->paste(); -} -void LLTextEditor::context_delete(void* data) -{ - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->doDelete(); -} -void LLTextEditor::context_selectall(void* data) -{ - LLTextEditor* line = (LLTextEditor*)data; - if(line)line->selectAll(); + (new ContextText)->registerListener(LLMenuGL::sMenuContainer, "Text"); + (new ContextUrl)->registerListener(LLMenuGL::sMenuContainer, "Text.Url"); + (new ContextUrlCopy)->registerListener(LLMenuGL::sMenuContainer, "Text.Url.CopyUUID"); } void LLTextEditor::setTrackColor( const LLColor4& color ) @@ -637,40 +691,84 @@ BOOL LLTextEditor::truncate() return did_truncate; } -void LLTextEditor::setText(const LLStringExplicit &utf8str) +LLMenuGL* LLTextEditor::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) { - // LLStringUtil::removeCRLF(utf8str); - mUTF8Text = utf8str_removeCRLF(utf8str); - // mUTF8Text = utf8str; - mWText = utf8str_to_wstring(mUTF8Text); - mTextIsUpToDate = TRUE; + // work out the XUI menu file to use for this url + LLUrlMatch match; + std::string url = in_url; + if (!LLUrlRegistry::instance().findUrl(url, match)) + { + return nullptr; + } - truncate(); - blockUndo(); + std::string xui_file = match.getMenuName(); + if (xui_file.empty()) + { + return nullptr; + } - setCursorPos(0); - deselect(); + // create and return the context menu from the XUI file + llassert(LLMenuGL::sMenuContainer != NULL); + auto menu = LLUICtrlFactory::getInstance()->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"); - needsReflow(); + if (addFriendButton && removeFriendButton) + { + addFriendButton->setEnabled(!isFriend); + removeFriendButton->setEnabled(isFriend); + } + } - resetDirty(); + 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; } -void LLTextEditor::setWText(const LLWString &wtext) +void LLTextEditor::setText(const LLStringExplicit &utf8str) { - mWText = wtext; - mUTF8Text.clear(); - mTextIsUpToDate = FALSE; + // clear out the existing text and segments + mWText.clear(); - truncate(); - blockUndo(); + clearSegments(); +// createDefaultSegment(); - setCursorPos(0); deselect(); - needsReflow(); + // append the new text (supports Url linking) + std::string text(utf8str); + //LLStringUtil::removeCRLF(text); + + // appendText modifies mCursorPos... + appendText(utf8str, false, false); + // ...so move cursor to top after appending text + setCursorPos(0); resetDirty(); + + mUTF8Text = text; + mTextIsUpToDate = true; +} + +void LLTextEditor::setWText(const LLWString& text) +{ + setText(wstring_to_utf8str(text)); } // virtual @@ -766,7 +864,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insen } // If still -1, then search_text just isn't found. - if (-1 == loc) + if (-1 == loc) { mIsSelecting = FALSE; mSelectionEnd = 0; @@ -889,9 +987,9 @@ S32 LLTextEditor::getLineStart( S32 line ) const { S32 num_lines = getLineCount(); if (num_lines == 0) - { + { return 0; - } + } line = llclamp(line, 0, num_lines-1); S32 segidx = mLineStartList[line].mSegment; @@ -1374,6 +1472,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) } BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) { + setFocus(TRUE); //setCursorAtLocalPos( x, y, TRUE ); @@ -1384,43 +1483,56 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); if (menu) { - for(int i = 0;i<(int)suggestionMenuItems.size();i++) + for (auto tempBind : suggestionMenuItems) { - SpellMenuBind * tempBind = suggestionMenuItems[i]; - if(tempBind) + if (tempBind) { menu->removeChild(tempBind->menuItem); tempBind->menuItem->die(); - //delete tempBind->menuItem; - //tempBind->menuItem = NULL; delete tempBind; } } suggestionMenuItems.clear(); + menu->die(); + } + + auto segment = getSegmentAtLocalPos(x, y); + const LLStyleSP style = segment ? segment->getStyle() : nullptr; + auto submenu = (style && style->isLink()) ? createUrlContextMenu(x, y, style->getLinkHREF()) : nullptr; + // Add url menu to base menu if we have a selection, otherwise make it the menu. + menu = (submenu && !hasSelection()) ? submenu : LLUICtrlFactory::getInstance()->buildMenu("menu_texteditor.xml", LLMenuGL::sMenuContainer); + mPopupMenuHandle = menu->getHandle(); + if (menu) + { + if (submenu && submenu != menu) + { + submenu->removeChild(submenu->getChild("Select All")); // There can be only one! + menu->appendMenu(submenu); + } // spell_check="true" in xui - menu->setItemVisible("Spelsep", !mReadOnly && mSpellCheckable); if (!mReadOnly && mSpellCheckable) { bool is_word_part = getWordBoundriesAt(pos, &wordStart, &wordLen); if (is_word_part) { const LLWString &text = mWText; - std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart,wordLen)); + std::string selectedWord(std::string(text.begin(), text.end()).substr(wordStart, wordLen)); if (!glggHunSpell->isSpelledRight(selectedWord)) { //misspelled word here, and you have just right clicked on it! std::vector suggs = glggHunSpell->getSuggestionList(selectedWord); - for (int i = 0; i<(int)suggs.size(); i++) + menu->addSeparator(); + for (auto word : suggs) { SpellMenuBind * tempStruct = new SpellMenuBind; tempStruct->origin = this; - tempStruct->word = suggs[i]; + tempStruct->word = word; tempStruct->wordPositionEnd = wordStart + wordLen; - tempStruct->wordPositionStart=wordStart; - tempStruct->wordY=y; + tempStruct->wordPositionStart = wordStart; + tempStruct->wordY = y; LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( tempStruct->word, spell_correct, NULL, tempStruct); tempStruct->menuItem = suggMenuItem; @@ -1431,8 +1543,8 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) tempStruct->origin = this; tempStruct->word = selectedWord; tempStruct->wordPositionEnd = wordStart + wordLen; - tempStruct->wordPositionStart=wordStart; - tempStruct->wordY=y; + tempStruct->wordPositionStart = wordStart; + tempStruct->wordY = y; LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( "Add Word", spell_add, NULL, tempStruct); tempStruct->menuItem = suggMenuItem; @@ -1446,11 +1558,11 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) if (glggHunSpell->getSpellCheckHighlight()) { tempStruct->word = "Hide Misspellings"; - } + } else - { + { tempStruct->word = "Show Misspellings"; - } + } LLMenuItemCallGL * suggMenuItem = new LLMenuItemCallGL( tempStruct->word, spell_show, NULL, tempStruct); @@ -1458,6 +1570,7 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask ) suggestionMenuItems.push_back(tempStruct); menu->addChild(suggMenuItem); } + mLastContextMenuX = x; mLastContextMenuY = y; menu->buildDrawLabels(); @@ -1484,6 +1597,7 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; + auto old_hover = mHoverSegment; mHoverSegment = NULL; if(hasMouseCapture() ) { @@ -1568,6 +1682,14 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) } } + if (old_hover != mHoverSegment) + { + if (old_hover) + old_hover->underlineOnHover(false); + if (mHoverSegment) + mHoverSegment->underlineOnHover(true); + } + if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) { mOnScrollEndCallback(mOnScrollEndData); @@ -1575,6 +1697,14 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) return handled; } +void LLTextEditor::onMouseLeave(S32 x, S32 y, MASK mask) +{ + if (mHoverSegment) + { + mHoverSegment->underlineOnHover(false); + mHoverSegment = nullptr; + } +} BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) { @@ -1733,11 +1863,6 @@ S32 LLTextEditor::remove(const S32 pos, const S32 length, const BOOL group_with_ return execute( new LLTextCmdRemove( pos, group_with_next_op, length ) ); } -S32 LLTextEditor::append(const LLWString &wstr, const BOOL group_with_next_op) -{ - return insert(mWText.length(), wstr, group_with_next_op); -} - S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) { if ((S32)mWText.length() == pos) @@ -2170,7 +2295,7 @@ BOOL LLTextEditor::canCopy() const } // copy selection to clipboard -void LLTextEditor::copy() +void LLTextEditor::copy(bool raw) { if( !canCopy() ) { @@ -2178,7 +2303,54 @@ void LLTextEditor::copy() } S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); S32 length = llabs( mSelectionStart - mSelectionEnd ); - gClipboard.copyFromSubstring(mWText, left_pos, length, mSourceID); + // Does our selection include any Segments with links? + if (mParseHTML && raw) + { + auto begin = std::find_if(mSegments.begin(), mSegments.end(), [left_pos](const LLTextSegmentPtr& ptr) { + return ptr->getEnd() > left_pos; + }); + auto last = mSegments.end(); + if (begin == last || begin->isNull()) + { + gClipboard.copyFromSubstring(mWText, left_pos, length, mSourceID); + return; + } + S32 right_pos = left_pos + length, offset = 0; + { + // If our selection starts in the middle of a link, set our left_pos to the beginning of its segment. + auto segment = **begin; + if (auto style = segment.getStyle()) + if (style->isLink()) + left_pos = llmin(segment.getStart(), left_pos); + } + auto text = mWText.substr(left_pos, length); + for (; begin->notNull() && begin != last && (*begin)->getStart() <= right_pos; ++begin) + { + auto segment = **begin; + //llassert(segment->getStyle()); // If someone is stores the result of the S32 constructor, they're in so much trouble!! + const auto& link = segment.getStyle()->getLinkHREF(); + if (!link.empty()) + { + const S32 label_length = (segment.getEnd() - segment.getStart()); + const S32 start = (segment.getStart()+offset)-left_pos; + const auto label = text.substr(start, label_length); + const auto wlink = utf8str_to_wstring(link); + const auto pos = wlink.find(label); + // Do not replace if normal link, or contains normal link (but may omit protocol) but ends the same way + // (i.e. [http://foo.bar/baz foo.bar] should still be restored here but not foo.bar/baz or foo.bar + if (pos == std::string::npos // Label is not (part of) the url + || (pos != 0 && wlink[pos-1] != '/') || pos+label.size() != wlink.size()) // Label is part of the url but there's more on either side of the url after the protocol + { + constexpr llwchar startchar = '[', space = ' ', endchar = ']'; + const auto raw_html = startchar + wlink + space + label + endchar; + text.replace(start, label_length, raw_html); + offset += raw_html.size() - label_length; // Track how much we've offset the string by replacing labels with their raw html and thus adding characters + } + } + } + gClipboard.copyFromSubstring(text, 0, text.length(), mSourceID); + } + else gClipboard.copyFromSubstring(mWText, left_pos, length, mSourceID); } BOOL LLTextEditor::canPaste() const @@ -2416,6 +2588,17 @@ BOOL LLTextEditor::handleEditKey(const KEY key, const MASK mask) } handled = TRUE; } + else if ((mask & (MASK_CONTROL|MASK_SHIFT)) == (MASK_CONTROL|MASK_SHIFT)) + { + if (key == 'C') + { + if (canCopy()) + copy(true); + else + reportBadKeystroke(); + handled = TRUE; + } + } else if( MASK_CONTROL & mask ) { @@ -3446,12 +3629,14 @@ void LLTextEditor::drawText() F32 text_y = (F32)(mTextRect.mTop - line_height); while((mTextRect.mBottom <= text_y) && (cur_line < num_lines)) { + S32 next_line = cur_line + 1; + S32 next_start = -1; S32 line_end = text_len; - if ((cur_line + 1) < num_lines) + if (next_line < num_lines) { - next_start = getLineStart(cur_line + 1); + next_start = getLineStart(next_line); line_end = next_start; } line_wraps = text[line_end-1] != '\n'; @@ -3487,6 +3672,7 @@ void LLTextEditor::drawText() NULL); // max pixels } + // draw a single line of text S32 seg_start = line_start; while( seg_start < line_end ) { @@ -3521,13 +3707,6 @@ void LLTextEditor::drawText() style->mUnderline = TRUE; } - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - - if ( (mParseHTML) && (left_pos > seg_start) && (left_pos < clipped_end) && mIsSelecting && (mSelectionStart == mSelectionEnd) ) - { - mHTML = style->getLinkHREF(); - } - drawClippedSegment( text, seg_start, clipped_end, text_x, text_y, selection_left, selection_right, style, &text_x ); if( text_x == text_start && mShowLineNumbers ) @@ -4077,31 +4256,67 @@ void LLTextEditor::appendColoredText(const std::string &new_text, style->setVisible(true); style->setColor(lcolor); style->setFontName(font_name); - appendStyledText(new_text, allow_undo, prepend_newline, style); + appendText(new_text, allow_undo, prepend_newline, style); } -void LLTextEditor::appendStyledText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - LLStyleSP stylep) +static LLTrace::BlockTimerStatHandle FTM_APPEND_TEXT("Append Text"); + +void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline, + const LLStyleSP style, bool force_replace_links) { - S32 part = (S32)LLTextParser::WHOLE; - if(mParseHTML) + LL_RECORD_BLOCK_TIME(FTM_APPEND_TEXT); + if (new_text.empty()) + return; + + std::string text = prepend_newline && !mWText.empty() ? ('\n' + new_text) : new_text; + appendTextImpl(text, style); + + if (!allow_undo) { + blockUndo(); + } +} +static LLTrace::BlockTimerStatHandle FTM_PARSE_HTML("Parse HTML"); + +// Appends new text to end of document +void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP style, bool force_replace_links) +{ + std::string text = new_text; + static const LLUICachedControl replace_links("SinguReplaceLinks"); + force_replace_links = force_replace_links || replace_links; + bool is_link = style && style->isLink(); // Don't search for URLs inside a link segment (STORM-358). + + S32 part = (S32)LLTextParser::WHOLE; + if (mReadOnly && mParseHTML && !is_link) // Singu Note: Do not replace html if the user is going to edit it. (Like in profiles) + { + LL_RECORD_BLOCK_TIME(FTM_PARSE_HTML); S32 start=0,end=0; - std::string text = new_text; - while ( findHTML(text, &start, &end) ) + LLUrlMatch match; + auto append_substr = [&](const size_t& pos, const size_t& count) { - LLStyleSP html(new LLStyle); - html->setVisible(true); - html->setColor(mLinkColor); - if (stylep) + appendAndHighlightText(text.substr(pos, count), part, style); + }; + auto append_link = [&](const std::string& link, LLStyleSP link_style) + { + if (style) // Respect styling { - html->setFontName(stylep->getFontString()); + const auto& text_style = *style; + link_style->mItalic = text_style.mItalic; + link_style->mBold = text_style.mBold; + link_style->mUnderline = text_style.mUnderline; } - html->mUnderline = TRUE; + // Hack around colors looking bad on some backgrounds by allowing setting link color for this editor + if (mLinkColor) link_style->setColor(*mLinkColor); + appendAndHighlightText(link, part, link_style, true/*match.underlineOnHoverOnly()*/); + }; + const auto&& cb = force_replace_links ? boost::bind(&LLTextEditor::replaceUrl, this, _1, _2, _3) : LLUrlLabelCallback::slot_function_type(); + while (!text.empty() && LLUrlRegistry::instance().findUrl(text, match, cb)) + { + start = match.getStart(); + end = match.getEnd()+1; + // output the text before the Url if (start > 0) { if (part == (S32)LLTextParser::WHOLE || @@ -4113,15 +4328,69 @@ void LLTextEditor::appendStyledText(const std::string &new_text, { part = (S32)LLTextParser::MIDDLE; } - std::string subtext=text.substr(0,start); - appendHighlightedText(subtext,allow_undo, prepend_newline, part, stylep); + append_substr(0, start); } - - html->setLinkHREF(text.substr(start,end-start)); - appendText(text.substr(start, end-start),allow_undo, prepend_newline, html); - if (end < (S32)text.length()) + + auto url = match.getUrl(); + const auto& label = match.getLabel(); + if (force_replace_links || replace_links || url == label) { - text = text.substr(end,text.length() - end); + // add icon before url if need + /* Singu TODO: Icons next to links? + LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted()); + if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() ) + { + link_style->setImage(match.getIcon()); + setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon")); + }*/ + + // output the styled url + append_link(label + match.getQuery(), match.getStyle()); + bool tooltip_required = !match.getTooltip().empty(); + + // set the tooltip for the Url label + if (tooltip_required) + { + setLastSegmentToolTip(match.getTooltip()); + } + + /* Singu Note: Upstream uses hardcoded Grey here, they have no care for skins, this could be awful! + Also it splits up the link! For now just make it append it to the same segment with its label above. + // show query part of url with gray color only for LLUrlEntryHTTP and LLUrlEntryHTTPNoProtocol url entries + std::string label = match.getQuery(); + if (!label.empty()) + { + link_params.color = LLColor4::grey; + link_params.readonly_color = LLColor4::grey; + appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly()); + append_link(label); + + // set the tooltip for the query part of url + if (tooltip_required) + { + setLastSegmentToolTip(match.getTooltip()); + } + }*/ + } + else // Still link the link itself + { + const auto pos = text.find(url); + bool fallback(pos == std::string::npos); // In special cases like no protocol and brackets + bool brackets(fallback && text.find('[') == start); // Brackets + if (fallback == brackets && start < pos) // Leading text, only in special case if brackets present + append_substr(start, brackets ? 1 : pos-start); + // In the special cases, only link exactly the url, this might not have a protocol so calculate the exact string + if (fallback) url = brackets ? text.substr(start+1, text.find(' ', start+2)-start) : text.substr(start, end-start); + append_link(url, match.getStyle()); // Append the link + const auto url_end = pos + url.size(); + if (fallback == brackets && end > url_end) // Ending text, only in special case if brackets present + append_substr(url_end, end-url_end); + } + + // move on to the rest of the text after the Url + if (end < (S32)text.length()) + { + text = text.substr(end, text.length() - end); end=0; part=(S32)LLTextParser::END; } @@ -4130,51 +4399,123 @@ void LLTextEditor::appendStyledText(const std::string &new_text, break; } } - if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END; - if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, stylep); + if (part != (S32)LLTextParser::WHOLE) + part=(S32)LLTextParser::END; + if (end < (S32)text.length()) + appendAndHighlightText(text, part, style); } else { - appendHighlightedText(new_text, allow_undo, prepend_newline, part, stylep); + appendAndHighlightText(text, part, style); } } -void LLTextEditor::appendHighlightedText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - S32 highlight_part, - LLStyleSP stylep) +void LLTextEditor::setLastSegmentToolTip(const std::string &tooltip) { - // If LindenUserDir is empty then we didn't login yet. - // In that case we can't instantiate LLTextParser, which - // is initialized per user. - if (mParseHighlights && !gDirUtilp->getLindenUserDir(true).empty()) - { - LLTextParser* highlight = LLTextParser::getInstance(); - - if (highlight && stylep) - { - LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), (LLTextParser::EHighlightPosition)highlight_part); - bool lprepend=prepend_newline; - for (S32 i=0;isetColor(lcolor); - if (i != 0 && (pieces.size() > 1) ) lprepend=FALSE; - appendText((std::string)pieces[i]["text"], allow_undo, lprepend, lstylep); - } - return; - } - } - appendText(new_text, allow_undo, prepend_newline, stylep); + LLTextSegmentPtr segment = mSegments.back(); + segment->setToolTip(tooltip); } -// Appends new text to end of document -void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline, - const LLStyleSP stylep) +void LLTextEditor::appendLineBreakSegment(/*const LLStyle::Params& style_params*/) +{ + /* + segment_vec_t segments; + LLStyleSP sp(new LLStyle(style_params)); + const auto& length = getLength(); + segments.push_back(new LLTextSegment(sp, length, length + 1));*/ + + insertStringNoUndo(getLength(), LLWString(1, '\n')); +} + +void LLTextEditor::appendAndHighlightText(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep, bool underline_on_hover) +{ + if (new_text.empty()) return; + + std::string::size_type start = 0; + /*std::string::size_type pos = new_text.find('\n',start); + + while(pos!=-1) + { + if(pos!=start) + { + std::string str = std::string(new_text,start,pos-start); + appendAndHighlightTextImpl(str,highlight_part, stylep); + } + appendLineBreakSegment(); + start = pos+1; + pos = new_text.find('\n',start); + }*/ + + std::string str = std::string(new_text,start,new_text.length()-start); + appendAndHighlightTextImpl(str,highlight_part, stylep, underline_on_hover); +} + + +void LLTextEditor::replaceUrl(const std::string &url, + const std::string &label, + const std::string &icon) +{ + // get the full (wide) text for the editor so we can change it + LLWString text = getWText(); + LLWString wlabel = utf8str_to_wstring(label); + bool modified = false; + S32 seg_start = 0; + + // iterate through each segment looking for ones styled as links + for (auto it = mSegments.begin(); it != mSegments.end(); ++it) + { + LLTextSegment *seg = *it; + LLStyleSP style = seg->getStyle(); + + // update segment start/end length in case we replaced text earlier + S32 seg_length = seg->getEnd() - seg->getStart(); + seg->setStart(seg_start); + seg->setEnd(seg_start + seg_length); + + // if we find a link with our Url, then replace the label + if (style->getLinkHREF() == url) + { + S32 start = seg->getStart(); + S32 end = seg->getEnd(); + text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1); + seg->setEnd(start + wlabel.size()); + modified = true; + } + + /* Singu TODO: Icons with Urls? + // Icon might be updated when more avatar or group info + // becomes available + if (style->isImage() && style->getLinkHREF() == url) + { + LLUIImagePtr image = image_from_icon_name( icon ); + if (image) + { + LLStyle::Params icon_params; + icon_params.image = image; + LLStyleConstSP new_style(new LLStyle(icon_params)); + seg->setStyle(new_style); + modified = true; + } + } + */ + + // work out the character offset for the next segment + seg_start = seg->getEnd(); + } + + // update the editor with the new (wide) text string + if (modified) + { + mWText = text; + mTextIsUpToDate = FALSE; + deselect(); + setCursorPos(mCursorPos); + needsReflow(); + } +} + + +void LLTextEditor::appendAndHighlightTextImpl(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep, bool underline_on_hover) { // Save old state BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()); @@ -4189,29 +4530,71 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool setCursorPos(old_length); - // Add carriage return if not first line - if (getLength() != 0 - && prepend_newline) + // This is where we appendHighlightedText + // If LindenUserDir is empty then we didn't login yet. + // In that case we can't instantiate LLTextParser, which is initialized per user. + if (mParseHighlights && !gDirUtilp->getLindenUserDir(true).empty()) { - std::string final_text = "\n"; - final_text += new_text; - append(utf8str_to_wstring(final_text), TRUE); + LLTextParser* highlight = LLTextParser::getInstance(); + + if (highlight && stylep) + { + LLStyleSP highlight_params(new LLStyle(*stylep)); + LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params->getColor(), (LLTextParser::EHighlightPosition)highlight_part); + for (S32 i=0;isetColor(lcolor); + + LLWString wide_text; + wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); + + S32 cur_length = getLength(); + insertStringNoUndo(cur_length, wide_text); + LLStyleSP sp(new LLStyle(*highlight_params)); + LLTextSegmentPtr segmentp; + segmentp = new LLTextSegment(sp, cur_length, cur_length + wide_text.size()); + if (underline_on_hover) segmentp->setUnderlineOnHover(true); + mSegments.push_back(segmentp); + } + return; + } } - else + //else { - append(utf8str_to_wstring(new_text), TRUE ); + LLWString wide_text; + wide_text = utf8str_to_wstring(new_text); + + auto length = getLength(); + auto insert_len = length + insertStringNoUndo(length, utf8str_to_wstring(new_text)); + + if (stylep) + { + S32 segment_start = old_length; + S32 segment_end = old_length + wide_text.size(); + LLTextSegmentPtr segment = new LLTextSegment(stylep, segment_start, segment_end); + if (underline_on_hover) segment->setUnderlineOnHover(true); + mSegments.push_back(segment); + } + else // If no style, still make a segment, + { + auto segment = mSegments.empty() ? nullptr : mSegments.back(); + if (!segment || !segment->getIsDefault()) + { + LLColor4& text_color = (mReadOnly ? mReadOnlyFgColor : mFgColor); + LLTextSegmentPtr segment = new LLTextSegment(text_color, length, insert_len); + segment->setIsDefault(true); // call it a default segment so we can consolidate later. + mSegments.push_back(segment); + } + else // It's later! + { + segment->setEnd(insert_len); + } + } } - if (stylep) - { - S32 segment_start = old_length; - S32 segment_end = getLength(); - LLTextSegmentPtr segment = new LLTextSegment(stylep, segment_start, segment_end ); - mSegments.push_back(segment); - } - - needsReflow(); - // Set the cursor and scroll position // Maintain the scroll position unless the scroll was at the end of the doc (in which // case, move it to the new end of the doc) or unless the user was doing actively selecting @@ -4230,9 +4613,6 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool mSelectionStart = selection_start; mSelectionEnd = selection_end; - - - mIsSelecting = was_selecting; setCursorPos(cursor_pos); } @@ -4244,11 +4624,6 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool { setCursorPos(cursor_pos); } - - if( !allow_undo ) - { - blockUndo(); - } } void LLTextEditor::removeTextFromEnd(S32 num_chars) @@ -4281,13 +4656,16 @@ S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr) mWText.insert(pos, wstr); mTextIsUpToDate = FALSE; - if ( truncate() ) + //HACK: If we are readonly we shouldn't need to truncate + if (!mReadOnly && truncate()) { // The user's not getting everything he's hoping for make_ui_sound("UISndBadKeystroke"); insert_len = mWText.length() - old_len; } + needsReflow(/*pos*/); + return insert_len; } @@ -4409,6 +4787,25 @@ void LLTextEditor::loadKeywords(const std::string& filename, static LLTrace::BlockTimerStatHandle FTM_SYNTAX_HIGHLIGHTING("Syntax Highlighting"); static LLTrace::BlockTimerStatHandle FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments"); + +void LLTextEditor::createDefaultSegment() +{ + if (mSegments.empty()) + { + LLColor4& text_color = (mReadOnly ? mReadOnlyFgColor : mFgColor); + LLTextSegmentPtr default_segment = new LLTextSegment(text_color, 0, mWText.length()); + default_segment->setIsDefault(TRUE); + mSegments.push_back(default_segment); + } +} + +void LLTextEditor::clearSegments() +{ + mLineStartList.clear(); + mSegments.clear(); + createDefaultSegment(); +} + void LLTextEditor::updateSegments() { { @@ -4428,15 +4825,9 @@ void LLTextEditor::updateSegments() // Make sure we have at least one segment if (mSegments.size() == 1 && mSegments[0]->getIsDefault()) { - mSegments.clear(); // create default segment - } - if (mSegments.empty()) - { - LLColor4& text_color = ( mReadOnly ? mReadOnlyFgColor : mFgColor ); - LLTextSegmentPtr default_segment = new LLTextSegment( text_color, 0, mWText.length() ); - default_segment->setIsDefault(TRUE); - mSegments.push_back(default_segment); + clearSegments(); // create default segment } + else createDefaultSegment(); } // Only effective if text was removed from the end of the editor @@ -4539,10 +4930,16 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask) // This mouse up was part of a click. // Regardless of where the cursor is, see if we recently touched a link // and launch it if we did. - if (mParseHTML && mHTML.length() > 0) + if (mParseHTML) { - LLUrlAction::clickAction(mHTML); - mHTML.clear(); + if (auto segment = getSegmentAtLocalPos(x, y)) + { + if (auto style = segment->getStyle()) + { + if (style->isLink()) + LLUrlAction::clickAction(style->getLinkHREF(), true); + } + } } } @@ -4695,6 +5092,24 @@ 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 @@ -4740,15 +5155,34 @@ LLTextSegment::LLTextSegment( const LLColor3& color, S32 start, S32 end ) : BOOL LLTextSegment::getToolTip(std::string& msg) const { + // do we have a tooltip for a loaded keyword (for script editor)? if (mToken && !mToken->getToolTip().empty()) { const LLWString& wmsg = mToken->getToolTip(); msg = wstring_to_utf8str(wmsg); return TRUE; } + // or do we have an explicitly set tooltip (e.g., for Urls) + if (!mTooltip.empty()) + { + msg = mTooltip; + return TRUE; + } + return FALSE; } +void LLTextSegment::setToolTip(const std::string& tooltip) +{ + // we cannot replace a keyword tooltip that's loaded from a file + if (mToken) + { + LL_WARNS() << "LLTextSegment::setToolTip: cannot replace keyword tooltip." << LL_ENDL; + return; + } + mTooltip = tooltip; +} + void LLTextSegment::dump() const @@ -4777,6 +5211,7 @@ LLXMLNodePtr LLTextEditor::getXML(bool save_children) const node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mGLFont)); node->createChild("word_wrap", TRUE)->setBoolValue(mWordWrap); node->createChild("hide_scrollbar", TRUE)->setBoolValue(mHideScrollbarForShortDocs); + node->createChild("hide_border", TRUE)->setBoolValue(!mBorder->getVisible()); addColorXML(node, mCursorColor, "cursor_color", "TextCursorColor"); addColorXML(node, mFgColor, "text_color", "TextFgColor"); @@ -4827,173 +5262,6 @@ void LLTextEditor::setTextEditorParameters(LLXMLNodePtr node) } } -/////////////////////////////////////////////////////////////////// -// Refactoring note: We may eventually want to replace this with boost::regex or -// boost::tokenizer capabilities since we've already fixed at least two JIRAs -// concerning logic issues associated with this function. -S32 LLTextEditor::findHTMLToken(const std::string &line, S32 pos, BOOL reverse) const -{ - std::string openers=" \t\n('\"[{<>"; - std::string closers=" \t\n)'\"]}><;"; - - if (reverse) - { - for (int index=pos; index >= 0; index--) - { - char c = line[index]; - S32 m2 = openers.find(c); - if (m2 >= 0) - { - return index+1; - } - } - return 0; // index is -1, don't want to return that. - } - else - { - // adjust the search slightly, to allow matching parenthesis inside the URL - S32 paren_count = 0; - for (int index=pos; index<(S32)line.length(); index++) - { - char c = line[index]; - - if (c == '(') - { - paren_count++; - } - else if (c == ')') - { - if (paren_count <= 0) - { - return index; - } - else - { - paren_count--; - } - } - else - { - S32 m2 = closers.find(c); - if (m2 >= 0) - { - return index; - } - } - } - return line.length(); - } -} - -BOOL LLTextEditor::findHTML(const std::string &line, S32 *begin, S32 *end) const -{ - - S32 m1,m2,m3; - BOOL matched = FALSE; - - m1=line.find("://",*end); - - if (m1 >= 0) //Easy match. - { - *begin = findHTMLToken(line, m1, TRUE); - *end = findHTMLToken(line, m1, FALSE); - - //Load_url only handles http and https so don't hilite ftp, smb, etc. - m2 = line.substr(*begin,(m1 - *begin)).find("http"); - m3 = line.substr(*begin,(m1 - *begin)).find("secondlife"); - - std::string badneighbors=".,<>?';\"][}{=-+_)(*&^%$#@!~`\t\r\n\\"; - - if (m2 >= 0 || m3>=0) - { - S32 bn = badneighbors.find(line.substr(m1+3,1)); - - if (bn < 0) - { - matched = TRUE; - } - } - } -/* matches things like secondlife.com (no http://) needs a whitelist to really be effective. - else //Harder match. - { - m1 = line.find(".",*end); - - if (m1 >= 0) - { - *end = findHTMLToken(line, m1, FALSE); - *begin = findHTMLToken(line, m1, TRUE); - - m1 = line.rfind(".",*end); - - if ( ( *end - m1 ) > 2 && m1 > *begin) - { - std::string badneighbors=".,<>/?';\"][}{=-+_)(*&^%$#@!~`"; - m2 = badneighbors.find(line.substr(m1+1,1)); - m3 = badneighbors.find(line.substr(m1-1,1)); - if (m3<0 && m2<0) - { - matched = TRUE; - } - } - } - } - */ - - if (matched) - { - S32 strpos, strpos2; - - std::string url = line.substr(*begin,*end - *begin); - std::string slurlID = "slurl.com/secondlife/"; - strpos = url.find(slurlID); - - if (strpos < 0) - { - slurlID="maps.secondlife.com/secondlife/"; - strpos = url.find(slurlID); - } - - if (strpos < 0) - { - slurlID="secondlife://"; - strpos = url.find(slurlID); - } - - if (strpos < 0) - { - slurlID="sl://"; - strpos = url.find(slurlID); - } - - if (strpos >= 0) - { - strpos+=slurlID.length(); - - while ( ( strpos2=url.find("/",strpos) ) == -1 ) - { - if ((*end+2) >= (S32)line.length() || line.substr(*end,1) != " " ) - { - matched=FALSE; - break; - } - - strpos = (*end + 1) - *begin; - - *end = findHTMLToken(line,(*begin + strpos),FALSE); - url = line.substr(*begin,*end - *begin); - } - } - - } - - if (!matched) - { - *begin=*end=0; - } - return matched; -} - void LLTextEditor::updateAllowingLanguageInput() diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 3bfdfb6d6..1bac9702c 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -65,14 +65,22 @@ public: LLTextEditor(const std::string& name, const LLRect& rect, S32 max_length, - const std::string &default_text, + const std::string &default_text, const LLFontGL* glfont = NULL, - BOOL allow_embedded_items = FALSE); + BOOL allow_embedded_items = FALSE, + bool parse_html = false); virtual ~LLTextEditor(); - typedef boost::signals2::signal keystroke_signal_t; + const std::string& getMenuSegmentUrl() const; + 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); + static void addMenuListeners(); void setKeystrokeCallback(const keystroke_signal_t::slot_type& callback); virtual LLXMLNodePtr getXML(bool save_children = true) const; @@ -84,6 +92,7 @@ public: virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleHover(S32 x, S32 y, MASK mask); + virtual void onMouseLeave(S32 x, S32 y, MASK mask) override; virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask ); virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); @@ -131,7 +140,9 @@ public: virtual BOOL canRedo() const; virtual void cut(); virtual BOOL canCut() const; - virtual void copy(); + void copy(bool raw); + void copyRaw() { copy(true); } + virtual void copy() { copy(false); } virtual BOOL canCopy() const; virtual void paste(); virtual BOOL canPaste() const; @@ -149,12 +160,6 @@ public: virtual BOOL canSelectAll() const; virtual void deselect(); virtual BOOL canDeselect() const; - static void context_cut(void* data); - - static void context_copy(void* data); - static void context_paste(void* data); - static void context_delete(void* data); - static void context_selectall(void* data); static void spell_correct(void* data); static void spell_add(void* data); static void spell_show(void* data); @@ -176,20 +181,24 @@ public: void insertText(const std::string &text, BOOL deleteSelection = TRUE); // appends text at end void appendText(const std::string &wtext, bool allow_undo, bool prepend_newline, - const LLStyleSP stylep = NULL); + const LLStyleSP stylep = NULL, bool force_replace_links = true); + void appendTextImpl(const std::string& new_text, const LLStyleSP style, bool force_replace_links = true); + + void setLastSegmentToolTip(const std::string& tooltip); + + void appendLineBreakSegment(); + + void appendAndHighlightText(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep, bool underline_on_hover = false); + + void replaceUrl(const std::string& url, const std::string& label, const std::string& icon); void appendColoredText(const std::string &wtext, bool allow_undo, bool prepend_newline, const LLColor4 &color, const std::string& font_name = LLStringUtil::null); - // if styled text starts a line, you need to prepend a newline. - void appendStyledText(const std::string &new_text, bool allow_undo, - bool prepend_newline, - LLStyleSP stylep = NULL); - void appendHighlightedText(const std::string &new_text, bool allow_undo, - bool prepend_newline, S32 highlight_part, - LLStyleSP stylep); + void appendAndHighlightTextImpl(const std::string& new_text, S32 highlight_part, const LLStyleSP stylep, bool underline_on_hover = false); + // Removes text from the end of document // Does not change highlight or cursor position. void removeTextFromEnd(S32 num_chars); @@ -253,8 +262,8 @@ public: void setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; } - // Callbacks - static void setLinkColor(LLColor4 color) { mLinkColor = color; } + // Use for when the link color needs to be changed to avoid looking awful + void setLinkColor(LLColor4* color) { if (mLinkColor) delete mLinkColor; mLinkColor = color; } void setOnScrollEndCallback(void (*callback)(void*), void* userdata); @@ -263,7 +272,9 @@ public: LLSD getValue() const; const std::string& getText() const; - + + LLMenuGL* createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url + // Non-undoable void setText(const LLStringExplicit &utf8str); void setWText(const LLWString &wtext); @@ -284,6 +295,7 @@ public: llwchar getWChar(S32 pos) const { return mWText[pos]; } LLWString getWSubString(S32 pos, S32 len) const { return mWText.substr(pos, len); } + const LLTextSegment* getLastSegment() const { return mSegments.empty() ? nullptr : mSegments.back(); } const LLTextSegment* getCurrentSegment() const { return getSegmentAtOffset(mCursorPos); } const LLTextSegment* getPreviousSegment() const; void getSelectedSegments(std::vector& segments) const; @@ -308,7 +320,6 @@ protected: void updateTextRect(); const LLRect& getTextRect() const { return mTextRect; } - void assignEmbedded(const std::string &s); BOOL truncate(); // Returns true if truncation occurs void removeCharOrTab(); @@ -364,9 +375,6 @@ protected: virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; } virtual void bindEmbeddedChars(const LLFontGL* font) const {} virtual void unbindEmbeddedChars(const LLFontGL* font) const {} - - S32 findHTMLToken(const std::string &line, S32 pos, BOOL reverse) const; - BOOL findHTML(const std::string &line, S32 *begin, S32 *end) const; // Abstract inner base class representing an undoable editor command. // Concrete sub-classes can be defined for operations such as insert, remove, etc. @@ -408,7 +416,6 @@ protected: void removeWord(bool prev); S32 insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op); S32 remove(const S32 pos, const S32 length, const BOOL group_with_next_op); - S32 append(const LLWString &wstr, const BOOL group_with_next_op); // Direct operations S32 insertStringNoUndo(S32 pos, const LLWString &wstr); // returns num of chars actually inserted @@ -454,7 +461,6 @@ protected: BOOL mParseHTML; BOOL mParseHighlights; - std::string mHTML; typedef std::vector segment_list_t; segment_list_t mSegments; @@ -467,6 +473,10 @@ 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; @@ -480,6 +490,8 @@ private: void pasteHelper(bool is_primary); void onKeyStroke(); + void createDefaultSegment(); + void clearSegments(); void updateSegments(); void pruneSegments(); @@ -502,7 +514,7 @@ private: // Data // LLKeywords mKeywords; - static LLColor4 mLinkColor; + LLColor4* mLinkColor; // Concrete LLTextCmd sub-classes used by the LLTextEditor base class class LLTextCmdInsert; @@ -615,6 +627,7 @@ public: LLTextSegment( const LLColor3& color, S32 start, S32 end ); S32 getStart() const { return mStart; } + void setStart(S32 start) { mStart = start; } S32 getEnd() const { return mEnd; } void setEnd( S32 end ) { mEnd = end; } const LLColor4& getColor() const { return mStyle->getColor(); } @@ -623,9 +636,12 @@ public: void setStyle(const LLStyleSP &style) { mStyle = style; } void setIsDefault(BOOL b) { mIsDefault = b; } BOOL getIsDefault() const { return mIsDefault; } + void setUnderlineOnHover(bool b) { mUnderlineOnHover = b; } + void underlineOnHover(bool hover) { if (mUnderlineOnHover) mStyle->mUnderline = hover; } void setToken( LLKeywordToken* token ) { mToken = token; } LLKeywordToken* getToken() const { return mToken; } BOOL getToolTip( std::string& msg ) const; + void setToolTip(const std::string& tooltip); void dump() const; @@ -643,6 +659,8 @@ private: S32 mEnd; LLKeywordToken* mToken; BOOL mIsDefault; + bool mUnderlineOnHover = false; + std::string mTooltip; }; diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 6ff4260bd..26acc25da 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -38,6 +38,9 @@ static LLRegisterWidget r("ui_ctrl"); +F32 LLUICtrl::sActiveControlTransparency = 1.0f; +F32 LLUICtrl::sInactiveControlTransparency = 1.0f; + LLUICtrl::CallbackParam::CallbackParam() : name("name"), function_name("function"), @@ -94,25 +97,26 @@ const LLUICtrl::Params& LLUICtrl::getDefaultParams() LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel) : LLView(p), + mCommitSignal(nullptr), + mValidateSignal(nullptr), + mMouseEnterSignal(nullptr), + mMouseLeaveSignal(nullptr), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mRightMouseDownSignal(nullptr), + mRightMouseUpSignal(nullptr), + mDoubleClickSignal(nullptr), + mViewModel(viewmodel), + mEnabledControlVariable(nullptr), + mDisabledControlVariable(nullptr), + mMakeVisibleControlVariable(nullptr), + mMakeInvisibleControlVariable(nullptr), mIsChrome(FALSE), mRequestsFront(p.requests_front), mTabStop(TRUE), mTentative(FALSE), - mViewModel(viewmodel), - mEnabledControlVariable(NULL), - mDisabledControlVariable(NULL), - mMakeVisibleControlVariable(NULL), - mMakeInvisibleControlVariable(NULL), - mCommitSignal(NULL), - mValidateSignal(NULL), - mMouseEnterSignal(NULL), - mMouseLeaveSignal(NULL), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mRightMouseDownSignal(NULL), - mRightMouseUpSignal(NULL), - mDoubleClickSignal(NULL), - mCommitOnReturn(FALSE) + mCommitOnReturn(FALSE), + mTransparencyType(TT_DEFAULT) { } @@ -210,20 +214,21 @@ LLUICtrl::LLUICtrl(const std::string& name, const LLRect rect, BOOL mouse_opaque mTabStop( TRUE ), mTentative( FALSE ), mViewModel(LLViewModelPtr(new LLViewModel)), - mEnabledControlVariable(NULL), - mDisabledControlVariable(NULL), - mMakeVisibleControlVariable(NULL), - mMakeInvisibleControlVariable(NULL), - mCommitSignal(NULL), - mValidateSignal(NULL), - mMouseEnterSignal(NULL), - mMouseLeaveSignal(NULL), - mMouseDownSignal(NULL), - mMouseUpSignal(NULL), - mRightMouseDownSignal(NULL), - mRightMouseUpSignal(NULL), - mDoubleClickSignal(NULL), - mCommitOnReturn(FALSE) + mEnabledControlVariable(nullptr), + mDisabledControlVariable(nullptr), + mMakeVisibleControlVariable(nullptr), + mMakeInvisibleControlVariable(nullptr), + mCommitSignal(nullptr), + mValidateSignal(nullptr), + mMouseEnterSignal(nullptr), + mMouseLeaveSignal(nullptr), + mMouseDownSignal(nullptr), + mMouseUpSignal(nullptr), + mRightMouseDownSignal(nullptr), + mRightMouseUpSignal(nullptr), + mDoubleClickSignal(nullptr), + mCommitOnReturn(FALSE), + mTransparencyType(TT_DEFAULT) { if(commit_callback) setCommitCallback(commit_callback); @@ -338,6 +343,8 @@ BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask) { (*mMouseDownSignal)(this,x,y,mask); } + LL_DEBUGS() << "LLUICtrl::handleMousedown - handled is returning as: " << handled << " " << LL_ENDL; + return handled; } @@ -349,6 +356,7 @@ BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask) { (*mMouseUpSignal)(this,x,y,mask); } + return handled; } @@ -466,7 +474,7 @@ void LLUICtrl::setEnabledControlVariable(LLControlVariable* control) if (mEnabledControlVariable) { mEnabledControlConnection.disconnect(); // disconnect current signal - mEnabledControlVariable = NULL; + mEnabledControlVariable = nullptr; } if (control) { @@ -481,7 +489,7 @@ void LLUICtrl::setDisabledControlVariable(LLControlVariable* control) if (mDisabledControlVariable) { mDisabledControlConnection.disconnect(); // disconnect current signal - mDisabledControlVariable = NULL; + mDisabledControlVariable = nullptr; } if (control) { @@ -496,7 +504,7 @@ void LLUICtrl::setMakeVisibleControlVariable(LLControlVariable* control) if (mMakeVisibleControlVariable) { mMakeVisibleControlConnection.disconnect(); // disconnect current signal - mMakeVisibleControlVariable = NULL; + mMakeVisibleControlVariable = nullptr; } if (control) { @@ -511,7 +519,7 @@ void LLUICtrl::setMakeInvisibleControlVariable(LLControlVariable* control) if (mMakeInvisibleControlVariable) { mMakeInvisibleControlConnection.disconnect(); // disconnect current signal - mMakeInvisibleControlVariable = NULL; + mMakeInvisibleControlVariable = nullptr; } if (control) { @@ -526,7 +534,12 @@ bool LLUICtrl::controlListener(const LLSD& newvalue, LLHandle handle, LLUICtrl* ctrl = handle.get(); if (ctrl) { - if (type == "enabled") + if (type == "value") + { + ctrl->setValue(newvalue); + return true; + } + else if (type == "enabled") { ctrl->setEnabled(newvalue.asBoolean()); return true; @@ -565,19 +578,19 @@ BOOL LLUICtrl::setLabelArg( const std::string& key, const LLStringExplicit& text // virtual LLCtrlSelectionInterface* LLUICtrl::getSelectionInterface() { - return NULL; + return nullptr; } // virtual LLCtrlListInterface* LLUICtrl::getListInterface() { - return NULL; + return nullptr; } // virtual LLCtrlScrollInterface* LLUICtrl::getScrollInterface() { - return NULL; + return nullptr; } BOOL LLUICtrl::hasFocus() const @@ -603,7 +616,7 @@ void LLUICtrl::setFocus(BOOL b) { if( gFocusMgr.childHasKeyboardFocus(this)) { - gFocusMgr.setKeyboardFocus( NULL ); + gFocusMgr.setKeyboardFocus(nullptr ); } } } @@ -1008,11 +1021,11 @@ LLUICtrl* LLUICtrl::getParentUICtrl() const } // *TODO: Deprecate; for backwards compatability only: -boost::signals2::connection LLUICtrl::setCommitCallback( boost::function cb, void* data) +boost::signals2::connection LLUICtrl::setCommitCallback( std::function cb, void* data) { return setCommitCallback( boost::bind(cb, _1, data)); } -boost::signals2::connection LLUICtrl::setValidateBeforeCommit( boost::function cb ) +boost::signals2::connection LLUICtrl::setValidateBeforeCommit( std::function cb ) { if (!mValidateSignal) mValidateSignal = new enable_signal_t(); return mValidateSignal->connect(boost::bind(cb, _2)); @@ -1030,7 +1043,6 @@ BOOL LLUICtrl::getTentative() const return mTentative; } - // virtual void LLUICtrl::setColor(const LLColor4& color) { } @@ -1047,6 +1059,37 @@ void LLUICtrl::setMinValue(LLSD min_value) void LLUICtrl::setMaxValue(LLSD max_value) { } +F32 LLUICtrl::getCurrentTransparency() +{ + F32 alpha = 0; + + switch(mTransparencyType) + { + case TT_DEFAULT: + alpha = getDrawContext().mAlpha; + break; + + case TT_ACTIVE: + alpha = sActiveControlTransparency; + break; + + case TT_INACTIVE: + alpha = sInactiveControlTransparency; + break; + + case TT_FADING: + alpha = sInactiveControlTransparency / 2.f; + break; + } + + return alpha; +} + +void LLUICtrl::setTransparencyType(ETypeTransparency type) +{ + mTransparencyType = type; +} + boost::signals2::connection LLUICtrl::setCommitCallback(const CommitCallbackParam& cb) { return setCommitCallback(initCommitCallback(cb)); diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index ca0e11d16..ff28f2519 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -34,16 +34,11 @@ #ifndef LL_LLUICTRL_H #define LL_LLUICTRL_H +//#include "llboost.h" #include "llrect.h" #include "llsd.h" #include "llregistry.h" -#ifndef BOOST_FUNCTION_HPP_INCLUDED -#include -#define BOOST_FUNCTION_HPP_INCLUDED -#endif -#include - #include "llinitparam.h" #include "llview.h" #include "llviewmodel.h" // *TODO move dependency to .cpp file @@ -128,6 +123,13 @@ public: Params(); }; + enum ETypeTransparency + { + TT_DEFAULT, + TT_ACTIVE, // focused floater + TT_INACTIVE, // other floaters + TT_FADING, // fading toast + }; /*virtual*/ ~LLUICtrl(); void initFromParams(const Params& p); @@ -147,26 +149,26 @@ public: // We shouldn't ever need to set this directly //virtual void setViewModel(const LLViewModelPtr&); - virtual BOOL postBuild(); + BOOL postBuild() override; public: // LLView interface /*virtual*/ void initFromXML(LLXMLNodePtr node, LLView* parent); /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; - /*virtual*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); - /*virtual*/ BOOL isCtrl() const; - /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask); - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL canFocusChildren() const; - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ) override; + /*virtual*/ BOOL isCtrl() const override; + /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask) override; + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL canFocusChildren() const override; + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask) override; // From LLFocusableElement - /*virtual*/ void setFocus( BOOL b ); - /*virtual*/ BOOL hasFocus() const; + /*virtual*/ void setFocus( BOOL b ) override; + /*virtual*/ BOOL hasFocus() const override; // New virtuals @@ -207,11 +209,17 @@ public: // Clear any user-provided input (text in a text editor, checked checkbox, // selected radio button, etc.). Defaults to no-op. virtual void clear(); + virtual void setColor(const LLColor4& color); virtual void setAlpha(F32 alpha); virtual void setMinValue(LLSD min_value); virtual void setMaxValue(LLSD max_value); + F32 getCurrentTransparency(); + + void setTransparencyType(ETypeTransparency type); + ETypeTransparency getTransparencyType() const {return mTransparencyType;} + BOOL focusNextItem(BOOL text_entry_only); BOOL focusPrevItem(BOOL text_entry_only); virtual // Singu Note: focusFirstItem is overridden for our old chat ui to prevent focusing on topmost uictrls. @@ -248,8 +256,8 @@ public: boost::signals2::connection setDoubleClickCallback( const mouse_signal_t::slot_type& cb ); // *TODO: Deprecate; for backwards compatability only: - boost::signals2::connection setCommitCallback( boost::function cb, void* data); - boost::signals2::connection setValidateBeforeCommit( boost::function cb ); + boost::signals2::connection setCommitCallback( std::function cb, void* data); + boost::signals2::connection setValidateBeforeCommit( std::function cb ); static LLView* fromXML(LLXMLNodePtr node, LLView* parent, class LLUICtrlFactory* factory); @@ -266,9 +274,13 @@ public: template class CallbackRegistry : public LLRegistrySingleton {}; - class CommitCallbackRegistry : public CallbackRegistry{}; + class CommitCallbackRegistry : public CallbackRegistry + { + }; // the enable callback registry is also used for visiblity callbacks - class EnableCallbackRegistry : public CallbackRegistry{}; + class EnableCallbackRegistry : public CallbackRegistry + { + }; protected: @@ -297,6 +309,10 @@ protected: boost::signals2::connection mMakeVisibleControlConnection; LLControlVariable* mMakeInvisibleControlVariable; boost::signals2::connection mMakeInvisibleControlConnection; + + static F32 sActiveControlTransparency; + static F32 sInactiveControlTransparency; + private: BOOL mIsChrome; @@ -304,6 +320,8 @@ private: BOOL mTabStop; BOOL mTentative; + ETypeTransparency mTransparencyType; + bool mCommitOnReturn; class DefaultTabGroupFirstSorter; diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 9209d484f..e41a8082a 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -412,13 +412,13 @@ LLMenuGL *LLUICtrlFactory::buildMenu(const std::string &filename, LLView* parent } // root must be called panel - if( !root->hasName( "menu_bar" ) && !root->hasName( "menu" )) + if( !root->hasName( "menu_bar" ) && !root->hasName( "menu" ) && !root->hasName("context_menu")) { LL_WARNS() << "Root node should be named menu bar or menu in : " << filename << LL_ENDL; return NULL; } - if (root->hasName("menu")) + if (root->hasName("menu") || root->hasName("context_menu")) { menu = (LLMenuGL*)LLMenuGL::fromXML(root, parentp, this); } @@ -496,7 +496,7 @@ void LLUICtrlFactory::rebuild() panelp->setFocus(FALSE); panelp->deleteAllChildren(); - buildPanel(panelp, filename.c_str(), &panelp->getFactoryMap()); + buildPanel(panelp, filename, &panelp->getFactoryMap()); panelp->setVisible(visible); } diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp index fd9b3d9a6..74ce67e88 100644 --- a/indra/llui/llurlaction.cpp +++ b/indra/llui/llurlaction.cpp @@ -1,3 +1,5 @@ +// 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 llurlaction.cpp * @author Martin Reddy @@ -24,7 +26,6 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ - #include "linden_common.h" #include "llurlaction.h" @@ -32,6 +33,7 @@ #include "llwindow.h" #include "llurlregistry.h" + // global state for the callback functions LLUrlAction::url_callback_t LLUrlAction::sOpenURLCallback; LLUrlAction::url_callback_t LLUrlAction::sOpenURLInternalCallback; @@ -83,18 +85,19 @@ void LLUrlAction::openURLExternal(std::string url) } } -void LLUrlAction::executeSLURL(std::string url) +bool LLUrlAction::executeSLURL(std::string url, bool trusted_content) { if (sExecuteSLURLCallback) { - sExecuteSLURLCallback(url); + return sExecuteSLURLCallback(url, trusted_content); } + return false; } -void LLUrlAction::clickAction(std::string url) +void LLUrlAction::clickAction(std::string url, bool trusted_content) { // Try to handle as SLURL first, then http Url - if ( (sExecuteSLURLCallback) && !sExecuteSLURLCallback(url) ) + if ( (sExecuteSLURLCallback) && !sExecuteSLURLCallback(url, trusted_content) ) { if (sOpenURLCallback) { @@ -157,3 +160,86 @@ void LLUrlAction::showProfile(std::string url) } } } + +std::string LLUrlAction::getUserID(std::string url) +{ + LLURI uri(url); + LLSD path_array = uri.pathArray(); + std::string id_str; + if (path_array.size() == 4) + { + id_str = path_array.get(2).asString(); + } + return id_str; +} + +std::string LLUrlAction::getObjectId(std::string url) +{ + LLURI uri(url); + LLSD path_array = uri.pathArray(); + std::string id_str; + if (path_array.size() >= 3) + { + id_str = path_array.get(2).asString(); + } + return id_str; +} + +std::string LLUrlAction::getObjectName(std::string url) +{ + LLURI uri(url); + LLSD query_map = uri.queryMap(); + std::string name; + if (query_map.has("name")) + { + name = query_map["name"].asString(); + } + return name; +} + +void LLUrlAction::sendIM(std::string url) +{ + std::string id_str = getUserID(url); + if (LLUUID::validate(id_str)) + { + executeSLURL("secondlife:///app/agent/" + id_str + "/im"); + } +} + +void LLUrlAction::addFriend(std::string url) +{ + std::string id_str = getUserID(url); + if (LLUUID::validate(id_str)) + { + executeSLURL("secondlife:///app/agent/" + id_str + "/requestfriend"); + } +} + +void LLUrlAction::removeFriend(std::string url) +{ + std::string id_str = getUserID(url); + if (LLUUID::validate(id_str)) + { + executeSLURL("secondlife:///app/agent/" + id_str + "/removefriend"); + } +} + +void LLUrlAction::blockObject(std::string url) +{ + std::string object_id = getObjectId(url); + std::string object_name = getObjectName(url); + if (LLUUID::validate(object_id)) + { + executeSLURL("secondlife:///app/agent/" + object_id + "/block/" + LLURI::escape(object_name)); + } +} + +void LLUrlAction::unblockObject(std::string url) +{ + std::string object_id = getObjectId(url); + std::string object_name = getObjectName(url); + if (LLUUID::validate(object_id)) + { + executeSLURL("secondlife:///app/agent/" + object_id + "/unblock/" + object_name); + } +} diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h index 0747829b7..51ab8261b 100644 --- a/indra/llui/llurlaction.h +++ b/indra/llui/llurlaction.h @@ -28,11 +28,7 @@ #ifndef LL_LLURLACTION_H #define LL_LLURLACTION_H -#include -#ifndef BOOST_FUNCTION_HPP_INCLUDED -#include -#define BOOST_FUNCTION_HPP_INCLUDED -#endif +#include /// /// The LLUrlAction class provides a number of static functions that @@ -60,7 +56,7 @@ public: static void openURLExternal(std::string url); /// execute the given secondlife: SLURL - static void executeSLURL(std::string url); + static bool executeSLURL(std::string url, bool trusted_content = true); /// if the Url specifies an SL location, teleport there static void teleportToLocation(std::string url); @@ -69,7 +65,7 @@ public: static void showLocationOnMap(std::string url); /// perform the appropriate action for left-clicking on a Url - static void clickAction(std::string url); + static void clickAction(std::string url, bool trusted_content); /// copy the label for a Url to the clipboard static void copyLabelToClipboard(std::string url); @@ -79,10 +75,18 @@ public: /// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile static void showProfile(std::string url); + static std::string getUserID(std::string url); + static std::string getObjectName(std::string url); + static std::string getObjectId(std::string url); + static void sendIM(std::string url); + static void addFriend(std::string url); + static void removeFriend(std::string url); + static void blockObject(std::string url); + static void unblockObject(std::string url); /// specify the callbacks to enable this class's functionality - typedef boost::function url_callback_t; - typedef boost::function execute_url_callback_t; + typedef std::function url_callback_t; + typedef std::function execute_url_callback_t; static void setOpenURLCallback(url_callback_t cb); static void setOpenURLInternalCallback(url_callback_t cb); diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 54dc1819c..8bf38caeb 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -1,3 +1,5 @@ +// 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 @@ -31,6 +33,7 @@ #include "lluri.h" #include "llurlmatch.h" #include "llurlregistry.h" +#include "lluriparser.h" #include "llavatarnamecache.h" #include "llcachename.h" @@ -38,14 +41,18 @@ //#include "lluicolortable.h" #include "message.h" -#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" +#include // + +#define APP_HEADER_REGEX "((((x-grid-info://)|(x-grid-location-info://))[-\\w\\.]+(:\\d+)?/app)|(secondlife:///app))" +#define X_GRID_OR_SECONDLIFE_HEADER_REGEX "((((x-grid-info://)|(x-grid-location-info://))[-\\w\\.]+(:\\d+)?/)|(secondlife://))" // Utility functions std::string localize_slapp_label(const std::string& url, const std::string& full_name); LLUrlEntryBase::LLUrlEntryBase() -{} +{ +} LLUrlEntryBase::~LLUrlEntryBase() { @@ -62,14 +69,13 @@ std::string LLUrlEntryBase::getIcon(const std::string &url) return mIcon; } -/*LLStyle::Params LLUrlEntryBase::getStyle() const +LLStyleSP LLUrlEntryBase::getStyle() const { - LLStyle::Params style_params; - style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - style_params.font.style = "UNDERLINE"; + static 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; -}*/ +} std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const @@ -111,7 +117,7 @@ std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const { // return the label part from [http://www.example.org Label] const char *text = url.c_str(); - S32 start = 0; + size_t start = 0; while (! isspace(text[start])) { start++; @@ -127,7 +133,7 @@ std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const { // return the url part from [http://www.example.org Label] const char *text = string.c_str(); - S32 end = 0; + size_t end = 0; while (! isspace(text[end])) { end++; @@ -142,11 +148,14 @@ void LLUrlEntryBase::addObserver(const std::string &id, // add a callback to be notified when we have a label for the uuid LLUrlEntryObserver observer; observer.url = url; - observer.signal = new LLUrlLabelSignal(); - if (observer.signal) + try { + observer.signal = new LLUrlLabelSignal(); observer.signal->connect(cb); - mObservers.insert(std::pair(id, observer)); + mObservers.emplace(id, observer); + } + catch (...) + { } } @@ -178,12 +187,49 @@ bool LLUrlEntryBase::isLinkDisabled() const return globally_disabled; } -static std::string getStringAfterToken(const std::string str, const std::string token) +bool LLUrlEntryBase::isWikiLinkCorrect(const std::string& url) +{ + LLWString label = utf8str_to_wstring(getLabelFromWikiLink(url)); + label.erase(std::remove(label.begin(), label.end(), L'\u200B'), label.end()); + return (LLUrlRegistry::instance().hasUrl(wstring_to_utf8str(label))) ? false : true; +} + +std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const +{ + LLUriParser up(unescapeUrl(url)); + up.normalize(); + + std::string label; + up.extractParts(); + up.glueFirst(label); + + return label; +} + +std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const +{ + LLUriParser up(unescapeUrl(url)); + + std::string label; + up.extractParts(); + up.glueFirst(label, false); + + size_t pos = url.find(label); + if (pos == std::string::npos) + { + return ""; + } + pos += label.size(); + return url.substr(pos); +} + + +static std::string getStringAfterToken(const std::string& str, const std::string& token) { size_t pos = str.find(token); if (pos == std::string::npos) { - return ""; + return std::string(); } pos += token.size(); @@ -194,8 +240,9 @@ static std::string getStringAfterToken(const std::string str, const std::string // LLUrlEntryHTTP Describes generic http: and https: Urls // LLUrlEntryHTTP::LLUrlEntryHTTP() + : LLUrlEntryBase() { - mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*", + mPattern = boost::regex("https?://([^\\s/?\\.#]+\\.?)+\\.\\w+(:\\d+)?(/\\S*)?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_http.xml"; mTooltip = LLTrans::getString("TooltipHttpUrl"); @@ -206,6 +253,20 @@ std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCal return unescapeUrl(url); } +std::string LLUrlEntryHTTP::getUrl(const std::string &string) const +{ + if (string.find("://") == std::string::npos) + { + return "http://" + escapeUrl(string); + } + return escapeUrl(string); +} + +std::string LLUrlEntryHTTP::getTooltip(const std::string &url) const +{ + return mTooltip;; +} + // // LLUrlEntryHTTP Describes generic http: and https: Urls with custom label // We use the wikipedia syntax of [http://www.example.org Text] @@ -238,20 +299,22 @@ std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const // LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com // LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol() + : LLUrlEntryBase() { - mPattern = boost::regex("(" - "\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR - "|" // or - "(?]+\\.(?:com|net|edu|org)([/:][^[:space:]<]*)?\\b" // i.e. FOO.net - ")", - boost::regex::perl|boost::regex::icase); + mPattern = boost::regex("\\bwww\\.\\S+\\.([^\\s<]*)?\\b", // i.e. www.FOO.BAR + boost::regex::perl | boost::regex::icase); mMenuName = "menu_url_http.xml"; mTooltip = LLTrans::getString("TooltipHttpUrl"); } std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb) { - return unescapeUrl(url); + return urlToLabelWithGreyQuery(url); +} + +std::string LLUrlEntryHTTPNoProtocol::getQuery(const std::string &url) const +{ + return urlToGreyQuery(url); } std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const @@ -263,6 +326,95 @@ std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const return escapeUrl(string); } +std::string LLUrlEntryHTTPNoProtocol::getTooltip(const std::string &url) const +{ + return unescapeUrl(url); +} + +LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL() + : LLUrlEntryBase() +{ + mPattern = boost::regex("(http://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryInvalidSLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + + return escapeUrl(url); +} + +std::string LLUrlEntryInvalidSLURL::getUrl(const std::string &string) const +{ + return escapeUrl(string); +} + +std::string LLUrlEntryInvalidSLURL::getTooltip(const std::string &url) const +{ + return unescapeUrl(url); +} + +bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const +{ + S32 actual_parts; + + if(url.find(".com/secondlife/") != std::string::npos) + { + actual_parts = 5; + } + else if(url.find("/app/") != std::string::npos) + { + actual_parts = 6; + } + else + { + actual_parts = 3; + } + + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + S32 x,y,z; + + 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); + + if((x>= 0 && x<= 256) && (y>= 0 && y<= 256) && (z>= 0)) + { + return TRUE; + } + } + else if (path_parts == (actual_parts-1)) + { + // handle slurl with (X,Y) coordinates + + LLStringUtil::convertToS32(path_array[path_parts-2],x); + LLStringUtil::convertToS32(path_array[path_parts-1],y); + ; + if((x>= 0 && x<= 256) && (y>= 0 && y<= 256)) + { + return TRUE; + } + } + else if (path_parts == (actual_parts-2)) + { + // handle slurl with (X) coordinate + LLStringUtil::convertToS32(path_array[path_parts-1],x); + if(x>= 0 && x<= 256) + { + return TRUE; + } + } + + return FALSE; +} + // // LLUrlEntrySLURL Describes generic http: and https: Urls // @@ -284,6 +436,7 @@ std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCa // - http://slurl.com/secondlife/Place/X // - http://slurl.com/secondlife/Place // + LLURI uri(url); LLSD path_array = uri.pathArray(); S32 path_parts = path_array.size(); @@ -335,13 +488,62 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const return url.substr(pos, url.size() - pos); } +// +// LLUrlEntrySeconlifeURL Describes *secondlife.com/ and *lindenlab.com/ urls to substitute icon 'hand.png' before link +// +LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL() +{ + mPattern = boost::regex("\\b(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(:\\d{1,5})?(/\\S*)?", + boost::regex::perl|boost::regex::icase); + + mIcon = "Hand"; + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +/// Return the url from a string that matched the regex +std::string LLUrlEntrySecondlifeURL::getUrl(const std::string &string) const +{ + if (string.find("://") == std::string::npos) + { + return "https://" + escapeUrl(string); + } + return escapeUrl(string); +} + +std::string LLUrlEntrySecondlifeURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return urlToLabelWithGreyQuery(url); +} + +std::string LLUrlEntrySecondlifeURL::getQuery(const std::string &url) const +{ + return urlToGreyQuery(url); +} + +std::string LLUrlEntrySecondlifeURL::getTooltip(const std::string &url) const +{ + return url; +} + +// +// LLUrlEntrySimpleSecondlifeURL Describes *secondlife.com and *lindenlab.com urls to substitute icon 'hand.png' before link +// +LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL() + { + mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(?!\\S)", + boost::regex::perl|boost::regex::icase); + + mIcon = "Hand"; + mMenuName = "menu_url_http.xml"; +} + // // LLUrlEntryAgent Describes a Second Life agent Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about // -LLUrlEntryAgent::LLUrlEntryAgent() : - mAvatarNameCacheConnection() +LLUrlEntryAgent::LLUrlEntryAgent() { mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", boost::regex::perl|boost::regex::icase); @@ -372,7 +574,15 @@ void LLUrlEntryAgent::callObservers(const std::string &id, void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name) { - mAvatarNameCacheConnection.disconnect(); + auto range = mAvatarNameCacheConnections.equal_range(id); + for (auto it = range.first; it != range.second; ++it) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + } + mAvatarNameCacheConnections.erase(range.first, range.second); std::string label = av_name.getCompleteName(); @@ -459,23 +669,19 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa } else { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); + 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"); } } -/*LLStyle::Params LLUrlEntryAgent::getStyle() const +LLStyleSP LLUrlEntryAgent::getStyle() const { - LLStyle::Params style_params = LLUrlEntryBase::getStyle(); - style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + static LLUICachedControl color("HTMLAgentColor"); + LLStyleSP style_params(new LLStyle(true, color, LLStringUtil::null)); return style_params; -}*/ +} std::string localize_slapp_label(const std::string& url, const std::string& full_name) { @@ -522,16 +728,25 @@ std::string LLUrlEntryAgent::getIcon(const std::string &url) // // LLUrlEntryAgentName describes a Second Life agent name Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) // -LLUrlEntryAgentName::LLUrlEntryAgentName() : - mAvatarNameCacheConnection() -{} +LLUrlEntryAgentName::LLUrlEntryAgentName() +{ + mMenuName = "menu_url_agent.xml"; +} void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name) { - mAvatarNameCacheConnection.disconnect(); + auto range = mAvatarNameCacheConnections.equal_range(id); + for (auto it = range.first; it != range.second; ++it) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + } + mAvatarNameCacheConnections.erase(range.first, range.second); std::string label = getName(av_name); // received the agent name from the server - tell our observers @@ -566,26 +781,24 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab } else { - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); + 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"); } } -/*LLStyle::Params LLUrlEntryAgentName::getStyle() const +LLStyleSP LLUrlEntryAgentName::getStyle() const { - // don't override default colors - return LLStyle::Params().is_link(false); -}*/ + static LLUICachedControl color("HTMLAgentColor"); + LLStyleSP style_params(new LLStyle(true, color, LLStringUtil::null)); + return style_params; +} // // LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename // LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName() { @@ -598,10 +811,26 @@ std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name return avatar_name.getCompleteName(); } +// +// LLUrlEntryAgentLegacyName describes a Second Life agent legacy name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname +// +LLUrlEntryAgentLegacyName::LLUrlEntryAgentLegacyName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/legacyname", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentLegacyName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.getLegacyName(); +} + // // LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname // LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName() { @@ -617,7 +846,7 @@ std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name) // // LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username -// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// x-grid-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username // LLUrlEntryAgentUserName::LLUrlEntryAgentUserName() { @@ -634,7 +863,7 @@ std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name) // LLUrlEntryGroup Describes a Second Life group Url, e.g., // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect -// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// x-grid-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect // LLUrlEntryGroup::LLUrlEntryGroup() { @@ -696,13 +925,12 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa } } -/*LLStyle::Params LLUrlEntryGroup::getStyle() const +LLStyleSP LLUrlEntryGroup::getStyle() const { - LLStyle::Params style_params = LLUrlEntryBase::getStyle(); - style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); - style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + LLStyleSP style_params = LLUrlEntryBase::getStyle(); + //style_params->mUnderline = false; // Singu Note: We're not gonna bother here, underlining on hover return style_params; -}*/ +} // @@ -714,7 +942,7 @@ LLUrlEntryInventory::LLUrlEntryInventory() //*TODO: add supporting of inventory item names with whitespaces //this pattern cann't parse for example //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value - //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + //x-grid-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_inventory.xml"; @@ -732,7 +960,7 @@ std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLab // LLUrlEntryObjectIM::LLUrlEntryObjectIM() { - mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?.*", + mPattern = boost::regex(APP_HEADER_REGEX "/objectim/[\\da-f-]+\?\\S*\\w", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_objectim.xml"; } @@ -765,7 +993,7 @@ std::set LLUrlEntryParcel::sParcelInfoObservers; /// /// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., /// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about -/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// x-grid-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about /// LLUrlEntryParcel::LLUrlEntryParcel() { @@ -860,7 +1088,7 @@ void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data) // LLUrlEntryPlace::LLUrlEntryPlace() { - mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", + mPattern = boost::regex("((((x-grid-info://)|(x-grid-location-info://))[-\\w\\.]+(:\\d+)?/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slurl.xml"; mTooltip = LLTrans::getString("TooltipSLURL"); @@ -968,7 +1196,7 @@ std::string LLUrlEntryRegion::getLocation(const std::string &url) const // // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., // secondlife:///app/teleport/Ahern/50/50/50/ -// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ +// x-grid-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ // LLUrlEntryTeleport::LLUrlEntryTeleport() { @@ -1042,7 +1270,7 @@ std::string LLUrlEntryTeleport::getLocation(const std::string &url) const // LLUrlEntrySL::LLUrlEntrySL() { - mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+", + mPattern = boost::regex(X_GRID_OR_SECONDLIFE_HEADER_REGEX "(\\w+)?(:\\d+)?/\\S+", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slapp.xml"; mTooltip = LLTrans::getString("TooltipSLAPP"); @@ -1059,7 +1287,7 @@ std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallb // LLUrlEntrySLLabel::LLUrlEntrySLLabel() { - mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]", + mPattern = boost::regex("\\[" X_GRID_OR_SECONDLIFE_HEADER_REGEX "\\S+[ \t]+[^\\]]+\\]", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slapp.xml"; mTooltip = LLTrans::getString("TooltipSLAPP"); @@ -1164,11 +1392,12 @@ std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelC return getUrl(url); } -/*LLStyle::Params LLUrlEntryNoLink::getStyle() const +LLStyleSP LLUrlEntryNoLink::getStyle() const { // Don't render as URL (i.e. no context menu or hand cursor). - return LLStyle::Params().is_link(false); -}*/ + // Singu Note: What the heck? No, that's misleading!! + return LLUrlEntryBase::getStyle(); +} // @@ -1201,3 +1430,75 @@ std::string LLUrlEntryIcon::getIcon(const std::string &url) LLStringUtil::trim(mIcon); return mIcon; } + +// +// LLUrlEntryEmail Describes a generic mailto: Urls +// +LLUrlEntryEmail::LLUrlEntryEmail() + : LLUrlEntryBase() +{ + mPattern = boost::regex("(mailto:)?[\\w\\.\\-]+@[\\w\\.\\-]+\\.[a-z]{2,63}", + boost::regex::perl | boost::regex::icase); + mMenuName = "menu_url_email.xml"; + mTooltip = LLTrans::getString("TooltipEmail"); +} + +std::string LLUrlEntryEmail::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + size_t pos = url.find("mailto:"); + + if (pos == std::string::npos) + { + return escapeUrl(url); + } + + std::string ret = escapeUrl(url.substr(pos + 7, url.length() - pos + 8)); + return ret; +} + +std::string LLUrlEntryEmail::getUrl(const std::string &string) const +{ + if (string.find("mailto:") == std::string::npos) + { + return "mailto:" + escapeUrl(string); + } + return escapeUrl(string); +} + +// +// +// 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+)", + boost::regex::perl); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryJira::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +std::string LLUrlEntryJira::getTooltip(const std::string &string) const +{ + return getUrl(string); +} + +std::string LLUrlEntryJira::getUrl(const std::string &url) const +{ + return (boost::format( + (url.find("ALCH") != std::string::npos) ? + "http://alchemy.atlassian.net/browse/%1%" : + (url.find("SV") != std::string::npos) ? + "https://singularityviewer.atlassian.net/browse/%1%" : + (url.find("FIRE") != std::string::npos) ? + "http://jira.phoenixviewer.com/browse/%1%" : + "http://jira.secondlife.com/browse/%1%" + ) % url).str(); +} +// + + diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index e52fe8040..d526ea957 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -35,10 +35,7 @@ #include "llavatarname.h" #include "llhost.h" // for resolving parcel name by parcel id -#include #include -#include -#include class LLAvatarName; @@ -78,11 +75,14 @@ public: /// Given a matched Url, return a label for the Url virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; } + /// Return port, query and fragment parts for the Url + virtual std::string getQuery(const std::string &url) const { return ""; } + /// Return an icon that can be displayed next to Urls of this type virtual std::string getIcon(const std::string &url); /// Return the style to render the displayed text - //virtual LLStyle::Params getStyle() const; + virtual LLStyleSP getStyle() const; /// Given a matched Url, return a tooltip string for the hyperlink virtual std::string getTooltip(const std::string &string) const { return mTooltip; } @@ -96,10 +96,16 @@ public: /// Should this link text be underlined only when mouse is hovered over it? virtual bool underlineOnHoverOnly(const std::string &string) const { return true; } // + virtual bool isTrusted() const { return false; } + virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } bool isLinkDisabled() const; + bool isWikiLinkCorrect(const std::string& url); + + virtual bool isSLURLvalid(const std::string &url) const { return TRUE; }; + protected: std::string getIDStringFromUrl(const std::string &url) const; std::string escapeUrl(const std::string &url) const; @@ -107,6 +113,8 @@ protected: std::string getLabelFromWikiLink(const std::string &url) const; std::string getUrlFromWikiLink(const std::string &string) const; void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb); + std::string urlToLabelWithGreyQuery(const std::string &url) const; + std::string urlToGreyQuery(const std::string &url) const; virtual void callObservers(const std::string &id, const std::string &label, const std::string& icon); typedef struct { @@ -128,7 +136,9 @@ class LLUrlEntryHTTP : public LLUrlEntryBase { public: LLUrlEntryHTTP(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getTooltip(const std::string &url) const override; }; /// @@ -138,9 +148,9 @@ class LLUrlEntryHTTPLabel : public LLUrlEntryBase { public: LLUrlEntryHTTPLabel(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getTooltip(const std::string &string) const; - /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getTooltip(const std::string &string) const override; + /*virtual*/ std::string getUrl(const std::string &string) const override; }; /// @@ -150,8 +160,21 @@ class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase { public: LLUrlEntryHTTPNoProtocol(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; + std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + std::string getQuery(const std::string &url) const override; + std::string getUrl(const std::string &string) const override; + std::string getTooltip(const std::string &url) const override; +}; + +class LLUrlEntryInvalidSLURL : public LLUrlEntryBase +{ +public: + LLUrlEntryInvalidSLURL(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getTooltip(const std::string &url) const override; + + bool isSLURLvalid(const std::string &url) const override; }; /// @@ -161,8 +184,31 @@ class LLUrlEntrySLURL : public LLUrlEntryBase { public: LLUrlEntrySLURL(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; +}; + +/// +/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls +/// +class LLUrlEntrySecondlifeURL : public LLUrlEntryBase +{ +public: + LLUrlEntrySecondlifeURL(); + /*virtual*/ bool isTrusted() const override { return true; } + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getQuery(const std::string &url) const override; + /*virtual*/ std::string getTooltip(const std::string &url) const override; +}; + +/// +/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls +/// +class LLUrlEntrySimpleSecondlifeURL : public LLUrlEntrySecondlifeURL +{ +public: + LLUrlEntrySimpleSecondlifeURL(); }; /// @@ -174,22 +220,27 @@ public: LLUrlEntryAgent(); ~LLUrlEntryAgent() { - if (mAvatarNameCacheConnection.connected()) + for(const auto& conn_pair : mAvatarNameCacheConnections) { - mAvatarNameCacheConnection.disconnect(); + if (conn_pair.second.connected()) + { + conn_pair.second.disconnect(); + } } + mAvatarNameCacheConnections.clear(); } - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getIcon(const std::string &url); - /*virtual*/ std::string getTooltip(const std::string &string) const; - //*virtual*/ LLStyle::Params getStyle() const; - /*virtual*/ LLUUID getID(const std::string &string) const; - /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getIcon(const std::string &url) override; + /*virtual*/ std::string getTooltip(const std::string &string) const override; + /*virtual*/ LLStyleSP getStyle() const override; + /*virtual*/ LLUUID getID(const std::string &string) const override; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const override; protected: - /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); + /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon) override; private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - boost::signals2::connection mAvatarNameCacheConnection; + using avatar_name_cache_connection_map_t = std::multimap; + avatar_name_cache_connection_map_t mAvatarNameCacheConnections; }; /// @@ -203,19 +254,25 @@ public: LLUrlEntryAgentName(); ~LLUrlEntryAgentName() { - if (mAvatarNameCacheConnection.connected()) + for (const auto& conn_pair : mAvatarNameCacheConnections) { - mAvatarNameCacheConnection.disconnect(); + if (conn_pair.second.connected()) + { + conn_pair.second.disconnect(); + } } + mAvatarNameCacheConnections.clear(); + } - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - //*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ LLStyleSP getStyle() const override; protected: // override this to pull out relevant name fields virtual std::string getName(const LLAvatarName& avatar_name) = 0; private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - boost::signals2::connection mAvatarNameCacheConnection; + using avatar_name_cache_connection_map_t = std::multimap; + avatar_name_cache_connection_map_t mAvatarNameCacheConnections; }; @@ -229,7 +286,15 @@ class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName public: LLUrlEntryAgentCompleteName(); private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); + /*virtual*/ std::string getName(const LLAvatarName& avatar_name) override; +}; + +class LLUrlEntryAgentLegacyName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentLegacyName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name) override; }; /// @@ -242,7 +307,7 @@ class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName public: LLUrlEntryAgentDisplayName(); private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); + /*virtual*/ std::string getName(const LLAvatarName& avatar_name) override; }; /// @@ -255,7 +320,7 @@ class LLUrlEntryAgentUserName : public LLUrlEntryAgentName public: LLUrlEntryAgentUserName(); private: - /*virtual*/ std::string getName(const LLAvatarName& avatar_name); + /*virtual*/ std::string getName(const LLAvatarName& avatar_name) override; }; /// @@ -266,9 +331,9 @@ class LLUrlEntryGroup : public LLUrlEntryBase { public: LLUrlEntryGroup(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - //*virtual*/ LLStyle::Params getStyle() const; - /*virtual*/ LLUUID getID(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ LLStyleSP getStyle() const override; + /*virtual*/ LLUUID getID(const std::string &string) const override; private: void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group); }; @@ -281,7 +346,7 @@ class LLUrlEntryInventory : public LLUrlEntryBase { public: LLUrlEntryInventory(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; private: }; @@ -293,8 +358,8 @@ class LLUrlEntryObjectIM : public LLUrlEntryBase { public: LLUrlEntryObjectIM(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; private: }; @@ -317,7 +382,7 @@ public: LLUrlEntryParcel(); ~LLUrlEntryParcel(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; // Sends a parcel info request to sim. void sendParcelInfoRequest(const LLUUID& parcel_id); @@ -353,8 +418,8 @@ class LLUrlEntryPlace : public LLUrlEntryBase { public: LLUrlEntryPlace(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; }; /// @@ -365,8 +430,8 @@ class LLUrlEntryRegion : public LLUrlEntryBase { public: LLUrlEntryRegion(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; }; /// @@ -377,8 +442,8 @@ class LLUrlEntryTeleport : public LLUrlEntryBase { public: LLUrlEntryTeleport(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; }; /// @@ -389,7 +454,7 @@ class LLUrlEntrySL : public LLUrlEntryBase { public: LLUrlEntrySL(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; }; /// @@ -400,10 +465,10 @@ class LLUrlEntrySLLabel : public LLUrlEntryBase { public: LLUrlEntrySLLabel(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getTooltip(const std::string &string) const; - /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getTooltip(const std::string &string) const override; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const override; }; /// @@ -414,8 +479,8 @@ class LLUrlEntryWorldMap : public LLUrlEntryBase { public: LLUrlEntryWorldMap(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getLocation(const std::string &url) const override; }; /// @@ -425,9 +490,9 @@ class LLUrlEntryNoLink : public LLUrlEntryBase { public: LLUrlEntryNoLink(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getUrl(const std::string &string) const; - //*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ LLStyleSP getStyle() const override; }; /// @@ -437,10 +502,35 @@ class LLUrlEntryIcon : public LLUrlEntryBase { public: LLUrlEntryIcon(); - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getIcon(const std::string &url); + /*virtual*/ std::string getUrl(const std::string &string) const override; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getIcon(const std::string &url) override; }; +/// +/// LLUrlEntryEmail Describes a generic mailto: Urls +/// +class LLUrlEntryEmail : public LLUrlEntryBase +{ +public: + LLUrlEntryEmail(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getUrl(const std::string &string) const override; +}; + +// +/// +/// LLUrlEntryJira describes a Jira Issue +/// +class LLUrlEntryJira : public LLUrlEntryBase +{ +public: + LLUrlEntryJira(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override; + /*virtual*/ std::string getTooltip(const std::string &string) const override; + /*virtual*/ std::string getUrl(const std::string &string) const override; + +}; +// #endif diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp index af4955ac4..717db6280 100644 --- a/indra/llui/llurlmatch.cpp +++ b/indra/llui/llurlmatch.cpp @@ -1,3 +1,5 @@ +// 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 llurlmatch.cpp * @author Martin Reddy @@ -37,26 +39,29 @@ LLUrlMatch::LLUrlMatch() : mIcon(""), mMenuName(""), mLocation(""), - mUnderlineOnHoverOnly(false) + mUnderlineOnHoverOnly(false), + mTrusted(false) { } -void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, - const std::string &label, const std::string &tooltip, - const std::string &icon, /*const LLStyle::Params& style,*/ +void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, const std::string &label, + const std::string& query, const std::string &tooltip, + const std::string &icon, const LLStyleSP& style, const std::string &menu, const std::string &location, - const LLUUID& id, bool underline_on_hover_only) + const LLUUID& id, bool underline_on_hover_only, bool trusted) { mStart = start; mEnd = end; mUrl = url; mLabel = label; + mQuery = query; mTooltip = tooltip; mIcon = icon; - //mStyle = style; - //mStyle.link_href = url; + mStyle = style; + mStyle->setLinkHREF(url); mMenuName = menu; mLocation = location; mID = id; mUnderlineOnHoverOnly = underline_on_hover_only; + mTrusted = trusted; } diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h index af36e6925..fc0b505ec 100644 --- a/indra/llui/llurlmatch.h +++ b/indra/llui/llurlmatch.h @@ -30,8 +30,6 @@ //#include "linden_common.h" -#include -#include #include "llstyle.h" /// @@ -62,6 +60,9 @@ public: /// return a label that can be used for the display of this Url std::string getLabel() const { return mLabel; } + /// return a right part of url which should be drawn in grey + std::string getQuery() const { return mQuery; } + /// return a message that could be displayed in a tooltip or status bar std::string getTooltip() const { return mTooltip; } @@ -69,7 +70,7 @@ public: std::string getIcon() const { return mIcon; } /// Return the color to render the displayed text - //LLStyle::Params getStyle() const { return mStyle; } + LLStyleSP getStyle() const { return mStyle; } /// Return the name of a XUI file containing the context menu items std::string getMenuName() const { return mMenuName; } @@ -80,12 +81,15 @@ public: /// Should this link text be underlined only when mouse is hovered over it? bool underlineOnHoverOnly() const { return mUnderlineOnHoverOnly; } + /// Return true if Url is trusted. + bool isTrusted() const { return mTrusted; } + /// Change the contents of this match object (used by LLUrlRegistry) void setValues(U32 start, U32 end, const std::string &url, const std::string &label, - const std::string &tooltip, const std::string &icon, - /*const LLStyle::Params& style, */const std::string &menu, + const std::string& query, const std::string &tooltip, const std::string &icon, + const LLStyleSP& style, const std::string &menu, const std::string &location, const LLUUID& id, - bool underline_on_hover_only = false ); + bool underline_on_hover_only = false, bool trusted = false); const LLUUID& getID() const { return mID; } private: @@ -93,13 +97,15 @@ private: U32 mEnd; std::string mUrl; std::string mLabel; + std::string mQuery; std::string mTooltip; std::string mIcon; std::string mMenuName; std::string mLocation; LLUUID mID; - //LLStyle::Params mStyle; + LLStyleSP mStyle; bool mUnderlineOnHoverOnly; + bool mTrusted; }; #endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 235b002d0..908531b0b 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -1,3 +1,5 @@ +// 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 llurlregistry.cpp * @author Martin Reddy @@ -27,6 +29,7 @@ #include "linden_common.h" #include "llurlregistry.h" +#include "lluriparser.h" #include @@ -37,15 +40,26 @@ void LLUrlRegistryNullCallback(const std::string &url, const std::string &label, LLUrlRegistry::LLUrlRegistry() { - mUrlEntry.reserve(20); + mUrlEntry.reserve(23); // // Urls are matched in the order that they were registered registerUrl(new LLUrlEntryNoLink()); - registerUrl(new LLUrlEntryIcon()); + mUrlEntryIcon = new LLUrlEntryIcon(); + registerUrl(mUrlEntryIcon); + mLLUrlEntryInvalidSLURL = new LLUrlEntryInvalidSLURL(); + registerUrl(mLLUrlEntryInvalidSLURL); registerUrl(new LLUrlEntrySLURL()); + + // decorated links for host names like: secondlife.com and lindenlab.com + mUrlEntryTrusted = new LLUrlEntrySecondlifeURL(); + registerUrl(mUrlEntryTrusted); + registerUrl(new LLUrlEntrySimpleSecondlifeURL()); + registerUrl(new LLUrlEntryHTTP()); - registerUrl(new LLUrlEntryHTTPLabel()); + mUrlEntryHTTPLabel = new LLUrlEntryHTTPLabel(); + registerUrl(mUrlEntryHTTPLabel); registerUrl(new LLUrlEntryAgentCompleteName()); + registerUrl(new LLUrlEntryAgentLegacyName()); registerUrl(new LLUrlEntryAgentDisplayName()); registerUrl(new LLUrlEntryAgentUserName()); // LLUrlEntryAgent*Name must appear before LLUrlEntryAgent since @@ -59,14 +73,17 @@ LLUrlRegistry::LLUrlRegistry() registerUrl(new LLUrlEntryObjectIM()); registerUrl(new LLUrlEntryPlace()); registerUrl(new LLUrlEntryInventory()); - registerUrl(new LLUrlEntryObjectIM()); //LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern, //so it should be registered in the end of list registerUrl(new LLUrlEntrySL()); - registerUrl(new LLUrlEntrySLLabel()); - // most common pattern is a URL without any protocol, - // e.g., "secondlife.com" - registerUrl(new LLUrlEntryHTTPNoProtocol()); + mUrlEntrySLLabel = new LLUrlEntrySLLabel(); + registerUrl(mUrlEntrySLLabel); + registerUrl(new LLUrlEntryEmail()); + // Parse teh jiras! + registerUrl(new LLUrlEntryJira()); // + // most common pattern is a URL without any protocol starting with "www", + // e.g., "www.secondlife.com" + registerUrl(new LLUrlEntryHTTPNoProtocol()); } LLUrlRegistry::~LLUrlRegistry() @@ -90,7 +107,7 @@ void LLUrlRegistry::registerUrl(LLUrlEntryBase *url, bool force_front) } } -static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &end) +static bool matchRegex(const char *text, const boost::regex& regex, U32 &start, U32 &end) { boost::cmatch result; bool found; @@ -100,7 +117,7 @@ static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &en { found = boost::regex_search(text, result, regex); } - catch (std::runtime_error &) + catch (const std::runtime_error &) { return false; } @@ -127,6 +144,11 @@ static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &en end--; } + else if (text[end] == ']' && std::string(text+start, end-start).find('[') == std::string::npos) + { + end--; + } + return true; } @@ -142,32 +164,77 @@ static bool stringHasUrl(const std::string &text) text.find(".edu") != std::string::npos || text.find(".org") != std::string::npos || text.find("") != std::string::npos || - text.find(" +static bool stringHasJira(const std::string &text) +{ + // fast heuristic test for a URL in a string. This is used + // to avoid lots of costly regex calls, BUT it needs to be + // kept in sync with the LLUrlEntry regexes we support. + return (text.find("ALCH") != std::string::npos || + text.find("SV") != std::string::npos || + text.find("BUG") != std::string::npos || + text.find("CHOP") != std::string::npos || + text.find("FIRE") != std::string::npos || + text.find("MAINT") != std::string::npos || + text.find("OPEN") != std::string::npos || + text.find("SCR") != std::string::npos || + text.find("STORM") != std::string::npos || + text.find("SVC") != std::string::npos || + text.find("VWR") != std::string::npos || + text.find("WEB") != std::string::npos); +} +// + +bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb, bool is_content_trusted) { // avoid costly regexes if there is clearly no URL in the text - if (! stringHasUrl(text)) + if (!(stringHasUrl(text) || stringHasJira(text))) // { return false; } // find the first matching regex from all url entries in the registry U32 match_start = 0, match_end = 0; - LLUrlEntryBase *match_entry = NULL; + LLUrlEntryBase *match_entry = nullptr; std::vector::iterator it; for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it) { + //Skip for url entry icon if content is not trusted + if(!is_content_trusted && (mUrlEntryIcon == *it)) + { + continue; + } + LLUrlEntryBase *url_entry = *it; U32 start = 0, end = 0; if (matchRegex(text.c_str(), url_entry->getPattern(), start, end)) { // does this match occur in the string before any other match - if (start < match_start || match_entry == NULL) + if (start < match_start || match_entry == nullptr) { + + if (mLLUrlEntryInvalidSLURL == *it) + { + if(url_entry && url_entry->isSLURLvalid(text.substr(start, end - start + 1))) + { + continue; + } + } + + if((mUrlEntryHTTPLabel == *it) || (mUrlEntrySLLabel == *it)) + { + if(url_entry && !url_entry->isWikiLinkCorrect(text.substr(start, end - start + 1))) + { + continue; + } + } + match_start = start; match_end = end; match_entry = url_entry; @@ -178,18 +245,32 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL // did we find a match? if so, return its details in the match object if (match_entry) { + // Skip if link is an email with an empty username (starting with @). See MAINT-5371. + if (match_start > 0 && text.substr(match_start - 1, 1) == "@") + return false; + // fill in the LLUrlMatch object and return it std::string url = text.substr(match_start, match_end - match_start + 1); + + if (match_entry == mUrlEntryTrusted) + { + LLUriParser up(url); + up.normalize(); + url = up.normalizedUri(); + } + match.setValues(match_start, match_end, match_entry->getUrl(url), match_entry->getLabel(url, cb), + match_entry->getQuery(url), match_entry->getTooltip(url), match_entry->getIcon(url), - //match_entry->getStyle(), + match_entry->getStyle(), match_entry->getMenuName(), match_entry->getLocation(url), match_entry->getID(url), - match_entry->underlineOnHoverOnly(url)); + match_entry->underlineOnHoverOnly(url), + match_entry->isTrusted()); return true; } @@ -209,7 +290,7 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr // character encoding, so we need to update the start // and end values to be correct for the wide string. LLWString wurl = utf8str_to_wstring(match.getUrl()); - S32 start = text.find(wurl); + size_t start = text.find(wurl); if (start == std::string::npos) { return false; @@ -218,9 +299,10 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr match.setValues(start, end, match.getUrl(), match.getLabel(), + match.getQuery(), match.getTooltip(), match.getIcon(), - //match.getStyle(), + match.getStyle(), match.getMenuName(), match.getLocation(), match.getID(), diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h index da16171a9..90099c6d1 100644 --- a/indra/llui/llurlregistry.h +++ b/indra/llui/llurlregistry.h @@ -33,9 +33,6 @@ #include "llsingleton.h" #include "llstring.h" -#include -#include - /// This default callback for findUrl() simply ignores any label updates void LLUrlRegistryNullCallback(const std::string &url, const std::string &label, @@ -73,7 +70,8 @@ public: /// get the next Url in an input string, starting at a given character offset /// your callback is invoked if the matched Url's label changes in the future bool findUrl(const std::string &text, LLUrlMatch &match, - const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback); + const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback, + bool is_content_trusted = false); /// a slightly less efficient version of findUrl for wide strings bool findUrl(const LLWString &text, LLUrlMatch &match, @@ -92,6 +90,11 @@ private: friend class LLSingleton; std::vector mUrlEntry; + LLUrlEntryBase* mUrlEntryTrusted; + LLUrlEntryBase* mUrlEntryIcon; + LLUrlEntryBase* mLLUrlEntryInvalidSLURL; + LLUrlEntryBase* mUrlEntryHTTPLabel; + LLUrlEntryBase* mUrlEntrySLLabel; }; #endif diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp index 140532498..b98ff7768 100644 --- a/indra/llvfs/lldir_linux.cpp +++ b/indra/llvfs/lldir_linux.cpp @@ -102,7 +102,7 @@ LLDir_Linux::LLDir_Linux() LL_INFOS() << "Running in dev checkout with mSkinBaseDir " << mSkinBaseDir << LL_ENDL; else - mSkinBaseDir = ""; + mSkinBaseDir.clear(); } if (mSkinBaseDir.empty()) { diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp index 8547f1408..9f89641a3 100644 --- a/indra/llvfs/lldir_win32.cpp +++ b/indra/llvfs/lldir_win32.cpp @@ -140,7 +140,18 @@ LLDir_Win32::LLDir_Win32() // LL_INFOS() << "mAppRODataDir = " << mAppRODataDir << LL_ENDL; - mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins"; + auto build_dir_pos = mExecutableDir.rfind("build-"); + if (build_dir_pos != std::string::npos) + { + // ...we're in a dev checkout + mSkinBaseDir = mExecutableDir.substr(0, build_dir_pos) + "indra" + mDirDelimiter + "newview" + mDirDelimiter + "skins"; + if (LLFile::isdir(mSkinBaseDir)) + LL_INFOS() << "Running in dev checkout with mSkinBaseDir " << mSkinBaseDir << LL_ENDL; + else mSkinBaseDir.clear(); + } + + if (mSkinBaseDir.empty()) // ...normal installation running + mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins"; // Build the default cache directory mDefaultCacheDir = buildSLOSCacheDir(); diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 762155d8f..f64b48667 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -49,8 +49,8 @@ LLSplashScreen *gSplashScreenp = NULL; BOOL gDebugClicks = FALSE; BOOL gDebugWindowProc = FALSE; -const S32 gURLProtocolWhitelistCount = 4; -const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:"/*, "file:"*/ }; +const S32 gURLProtocolWhitelistCount = 5; +const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:", "mailto:" }; // CP: added a handler list - this is what's used to open the protocol and is based on registry entry // only meaningful difference currently is that file: protocols are opened using http: diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index d0cb3802d..cf92c3a51 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -66,7 +66,7 @@ public: }; public: virtual void postInitialized() {} - virtual void show() = 0; + virtual void show(bool focus = true) = 0; virtual void hide() = 0; virtual void close() = 0; virtual BOOL getVisible() = 0; diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h index 491f9afd2..ae3cd90f2 100644 --- a/indra/llwindow/llwindowheadless.h +++ b/indra/llwindow/llwindowheadless.h @@ -32,7 +32,7 @@ class LLWindowHeadless : public LLWindow { public: - /*virtual*/ void show() {}; + /*virtual*/ void show(bool) {}; /*virtual*/ void hide() {}; /*virtual*/ void close() {}; /*virtual*/ BOOL getVisible() {return FALSE;}; diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 72c9f7628..8c11a05d3 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -80,7 +80,7 @@ BOOL check_for_card(const char* RENDERER, const char* bad_card) "You can try to run Second Life, but it will probably crash or run\n" "very slowly. Try anyway?", bad_card); - S32 button = OSMessageBox(buffer.c_str(), "Unsupported video card", OSMB_YESNO); + S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO); if (OSBTN_YES == button) { return FALSE; diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h index 6c0e872e2..e85c27f87 100644 --- a/indra/llwindow/llwindowmesaheadless.h +++ b/indra/llwindow/llwindowmesaheadless.h @@ -35,7 +35,7 @@ class LLWindowMesaHeadless : public LLWindow { public: - /*virtual*/ void show() {}; + /*virtual*/ void show(bool) {}; /*virtual*/ void hide() {}; /*virtual*/ void close() {}; /*virtual*/ BOOL getVisible() {return FALSE;}; diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 2ca113f55..3d6919393 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -790,7 +790,7 @@ LLWindowSDL::~LLWindowSDL() } -void LLWindowSDL::show() +void LLWindowSDL::show(bool focus) { // *FIX: What to do with SDL? } diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index a180be319..9a80856c5 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -49,7 +49,7 @@ class LLWindowSDL : public LLWindow { public: - /*virtual*/ void show(); + /*virtual*/ void show(bool focus); /*virtual*/ void hide(); /*virtual*/ void close(); /*virtual*/ BOOL getVisible(); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index c6cf352f6..e4ce7e837 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -717,9 +717,10 @@ void LLWindowWin32::postInitialized() mCallbacks->handleDPIScaleChange(this, xDPIScale, yDPIScale); } -void LLWindowWin32::show() +void LLWindowWin32::show(bool take_focus) { ShowWindow(mWindowHandle, SW_SHOW); + if (!take_focus) return; SetForegroundWindow(mWindowHandle); SetFocus(mWindowHandle); } diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 4f8693dbe..ca82f7512 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -41,7 +41,7 @@ class LLWindowWin32 : public LLWindow { public: /*virtual*/ void postInitialized(); - /*virtual*/ void show(); + /*virtual*/ void show(bool focus = true); /*virtual*/ void hide(); /*virtual*/ void close(); /*virtual*/ BOOL getVisible(); diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt index d65bf3146..2245255cd 100644 --- a/indra/llxml/CMakeLists.txt +++ b/indra/llxml/CMakeLists.txt @@ -13,7 +13,7 @@ include_directories( ) set(llxml_SOURCE_FILES - #aixml.cpp + aixml.cpp llcontrol.cpp llxmlnode.cpp llxmlparser.cpp @@ -23,7 +23,7 @@ set(llxml_SOURCE_FILES set(llxml_HEADER_FILES CMakeLists.txt - #aixml.h + aixml.h llcontrol.h llcontrolgroupreader.h llxmlnode.h diff --git a/indra/llxml/aixml.cpp b/indra/llxml/aixml.cpp index dffe3d73a..02b972e80 100644 --- a/indra/llxml/aixml.cpp +++ b/indra/llxml/aixml.cpp @@ -32,7 +32,6 @@ #include "aixml.h" #include "llmd5.h" #include -#include "aifile.h" //============================================================================= // Overview @@ -311,11 +310,10 @@ void AIXMLElement::child(LLDate const& element) //----------------------------------------------------------------------------- // AIXMLStream -AIXMLStream::AIXMLStream(LLFILE* fp, bool standalone) : mOfs(fp) +AIXMLStream::AIXMLStream(const std::string& filename, bool standalone) : mOfs(filename) { - char const* sp = standalone ? " standalone=\"yes\"" : ""; - int rc = fprintf(fp, "\n", sp); - if (rc < 0 || ferror(fp)) + mOfs << "\n"; + if (!mOfs) { // I don't think that errno is set to anything else but EBADF here, // so there is not really any informative message to add here. @@ -342,7 +340,6 @@ AIXMLParser::AIXMLParser(std::string const& filename, char const* file_desc, std AIArgs args; if (!mXmlTree.parseFile(filename, TRUE)) { - AIFile dummy(filename, "rb"); // Check if the file can be opened at all (throws with a more descriptive error if not). error = "AIXMLParser_Cannot_parse_FILEDESC_FILENAME"; } else diff --git a/indra/llxml/aixml.h b/indra/llxml/aixml.h index 3ff971101..358eed756 100644 --- a/indra/llxml/aixml.h +++ b/indra/llxml/aixml.h @@ -151,7 +151,7 @@ void AIXMLElement::child(FWD_ITERATOR i1, FWD_ITERATOR const& i2) class AIXMLStream { protected: llofstream mOfs; - AIXMLStream(LLFILE* fp, bool standalone); + AIXMLStream(const std::string& filename, bool standalone); ~AIXMLStream(); }; @@ -159,7 +159,7 @@ class AIXMLStream { class AIXMLRootElement : public AIXMLStream, public AIXMLElement { public: - AIXMLRootElement(LLFILE* fp, char const* name, bool standalone = true) : AIXMLStream(fp, standalone), AIXMLElement(mOfs, name, 0) { } + AIXMLRootElement(const std::string& filename, char const* name, bool standalone = true) : AIXMLStream(filename, standalone), AIXMLElement(mOfs, name, 0) { } }; class AIXMLElementParser diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a15c9adc8..6f338aa4c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -13,7 +13,7 @@ include(FMODSTUDIO) include(GLOD) include(FindOpenGL) include(Hunspell) -include(JsonCpp) +include(Json) include(LLAddBuildTest) include(LLAppearance) include(LLAudio) @@ -43,6 +43,7 @@ include(UI) include(ViewerMiscLibs) include(WinManifest) include(ZLIB) +include(URIPARSER) if (MSVC) use_prebuilt_binary(vcredist) @@ -52,7 +53,7 @@ include_directories( ${STATEMACHINE_INCLUDE_DIRS} ${DBUSGLIB_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} - ${JSONCPP_INCLUDE_DIR} + ${JSON_INCLUDE_DIR} ${GLOD_INCLUDE_DIR} ${LLAUDIO_INCLUDE_DIRS} ${LLCHARACTER_INCLUDE_DIRS} @@ -77,7 +78,7 @@ include_directories( set(viewer_SOURCE_FILES NACLantispam.cpp aihttpview.cpp - #aixmllindengenepool.cpp + aixmllindengenepool.cpp alfloaterregiontracker.cpp aoremotectrl.cpp ascentfloatercontactgroups.cpp @@ -174,6 +175,7 @@ set(viewer_SOURCE_FILES llexternaleditor.cpp llface.cpp llfasttimerview.cpp + llfavoritesbar.cpp llfeaturemanager.cpp llfirstuse.cpp llflexibleobject.cpp @@ -231,6 +233,7 @@ set(viewer_SOURCE_FILES llfloaterlandholdings.cpp llfloaterlandmark.cpp llfloatermap.cpp + llfloatermarketplacelistings.cpp llfloatermediafilter.cpp llfloatermediasettings.cpp llfloatermemleak.cpp @@ -243,7 +246,6 @@ set(viewer_SOURCE_FILES llfloaterobjectiminfo.cpp llfloaterobjectweights.cpp llfloateropenobject.cpp - llfloateroutbox.cpp llfloaterparcel.cpp llfloaterpathfindingcharacters.cpp llfloaterpathfindinglinksets.cpp @@ -320,6 +322,7 @@ set(viewer_SOURCE_FILES llinventoryobserver.cpp llinventorypanel.cpp lljoystickbutton.cpp + lllandmarkactions.cpp lllandmarklist.cpp lllogchat.cpp llloginhandler.cpp @@ -387,7 +390,6 @@ set(viewer_SOURCE_FILES llpanellandoptions.cpp llpanellogin.cpp llpanelmaininventory.cpp - llpanelmarketplaceoutboxinventory.cpp llpanelmediasettingsgeneral.cpp llpanelmediasettingspermissions.cpp llpanelmediasettingssecurity.cpp @@ -613,7 +615,7 @@ set(viewer_HEADER_FILES NACLantispam.h aihttpview.h - #aixmllindengenepool.h + aixmllindengenepool.h alfloaterregiontracker.h aoremotectrl.h ascentfloatercontactgroups.h @@ -710,6 +712,7 @@ set(viewer_HEADER_FILES llexternaleditor.h llface.h llfasttimerview.h + llfavoritesbar.h llfeaturemanager.h llfirstuse.h llflexibleobject.h @@ -767,6 +770,7 @@ set(viewer_HEADER_FILES llfloaterlandholdings.h llfloaterlandmark.h llfloatermap.h + llfloatermarketplacelistings.h llfloatermediafilter.h llfloatermediasettings.h llfloatermemleak.h @@ -779,7 +783,6 @@ set(viewer_HEADER_FILES llfloaterobjectiminfo.h llfloaterobjectweights.h llfloateropenobject.h - llfloateroutbox.h llfloaterparcel.h llfloaterpathfindingcharacters.h llfloaterpathfindinglinksets.h @@ -855,6 +858,7 @@ set(viewer_HEADER_FILES llinventoryobserver.h llinventorypanel.h lljoystickbutton.h + lllandmarkactions.h lllandmarklist.h lllightconstants.h lllogchat.h @@ -924,7 +928,6 @@ set(viewer_HEADER_FILES llpanellandoptions.h llpanellogin.h llpanelmaininventory.h - llpanelmarketplaceoutboxinventory.h llpanelmediasettingsgeneral.h llpanelmediasettingspermissions.h llpanelmediasettingssecurity.h @@ -1642,6 +1645,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLCOMMON_LIBRARIES} ${NDOF_LIBRARY} ${NVAPI_LIBRARY} + ${URIPARSER_LIBRARY} ${viewer_LIBRARIES} ${Boost_CONTEXT_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} @@ -1655,7 +1659,6 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${GLOD_LIBRARIES} ${APRUTIL_LIBRARIES} ${OPENGL_LIBRARIES} - ${JSONCPP_LIBRARIES} ${SDL_LIBRARY} ${SMARTHEAP_LIBRARY} ${UI_LIBRARIES} diff --git a/indra/newview/aixmllindengenepool.cpp b/indra/newview/aixmllindengenepool.cpp index 2e62a7955..c9d0b37aa 100644 --- a/indra/newview/aixmllindengenepool.cpp +++ b/indra/newview/aixmllindengenepool.cpp @@ -49,12 +49,11 @@ #include "llviewerprecompiledheaders.h" #include "aixmllindengenepool.h" #include "hippogridmanager.h" +#include "llinventorymodel.h" #include "llvisualparam.h" #include "llviewerwearable.h" #include "llquantize.h" -extern void append_path_short(LLUUID const& id, std::string& path); - void AIXMLLindenGenepool::MetaData::toXML(std::ostream& os, int indentation) const { AIXMLElement tag(os, "meta", indentation); @@ -68,7 +67,7 @@ AIXMLLindenGenepool::MetaData::MetaData(AIXMLElementParser const& parser) parser.attribute(DEFAULT_LLDATE_NAME, mDate); } -AIXMLLindenGenepool::AIXMLLindenGenepool(LLFILE* fp) : AIXMLRootElement(fp, "linden_genepool") +AIXMLLindenGenepool::AIXMLLindenGenepool(const std::string& filename) : AIXMLRootElement(filename, "linden_genepool") { attribute("version", "1.0"); attribute("metaversion", "1.0"); @@ -145,7 +144,20 @@ AIArchetype::MetaData::MetaData(AIXMLElementParser const& parser) AIArchetype::MetaData::MetaData(LLViewerWearable const* wearable) : mName(wearable->getName()), mDescription(wearable->getDescription()) { - append_path_short(wearable->getItemID(), mPath); + // Path should already end on '/' if it is not empty. + const LLUUID& root_id = gInventory.getRootFolderID(); + LLUUID id = wearable->getItemID(); + for(const LLInventoryObject* obj = gInventory.getObject(id); obj && id != root_id; obj = gInventory.getCategory(id)) + { + std::string temp(mPath); + mPath = obj->getName(); + if (!temp.empty()) + { + mPath.append(1, '/'); + mPath.append(temp); + } + id = obj->getParentUUID(); + } } AIArchetype::AIArchetype(void) : mType(LLWearableType::WT_NONE) diff --git a/indra/newview/aixmllindengenepool.h b/indra/newview/aixmllindengenepool.h index c3a5a65d6..42aa5d35c 100644 --- a/indra/newview/aixmllindengenepool.h +++ b/indra/newview/aixmllindengenepool.h @@ -53,7 +53,7 @@ class AIXMLLindenGenepool : public AIXMLRootElement MetaData(AIXMLElementParser const& parser); }; - AIXMLLindenGenepool(LLFILE* fp); + AIXMLLindenGenepool(const std::string& filename); }; class AIVisualParamIDValuePair diff --git a/indra/newview/aoremotectrl.cpp b/indra/newview/aoremotectrl.cpp index d9b782fca..5f57bcd20 100644 --- a/indra/newview/aoremotectrl.cpp +++ b/indra/newview/aoremotectrl.cpp @@ -33,7 +33,6 @@ #include "aoremotectrl.h" #include "floaterao.h" -#include "llbutton.h" #include "lloverlaybar.h" #include "lluictrlfactory.h" #include "llviewercontrol.h" @@ -46,80 +45,23 @@ AORemoteCtrl::AORemoteCtrl() setFocusRoot(TRUE); } -AORemoteCtrl::~AORemoteCtrl() -{ -} - -void AORemoteCtrl::draw() -{ - LLButton* expand_button = getChild("popup_btn"); - if (expand_button) - { - if (expand_button->getToggleState()) - { - expand_button->setImageOverlay("arrow_down.tga"); - } - else - { - expand_button->setImageOverlay("arrow_up.tga"); - } - } - - LLPanel::draw(); -} - void AORemoteCtrl::build() { - if (gSavedSettings.getBOOL("ShowAOSitPopup")) - { - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_ao_remote_expanded.xml"); - } - else - { - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_ao_remote.xml"); - } + LLUICtrlFactory::getInstance()->buildPanel(this, gSavedSettings.getBOOL("ShowAOSitPopup") ? "panel_ao_remote_expanded.xml" : "panel_ao_remote.xml"); } BOOL AORemoteCtrl::postBuild() { - - childSetAction("ao_btn", onClickToggleAO, this); - if (gSavedSettings.getBOOL("ShowAOSitPopup")) - { - childSetAction("ao_sit_btn", onClickToggleAOSit, this); - //childSetAction("ao_show_btn", onClickShowAO, this); - } - childSetAction("popup_btn", onClickPopupBtn, this); + /*if (LLUICtrl* ctrl = findChild("ao_show_btn")) + ctrl->setCommitCallback(boost::bind(&LLFloaterAO::showInstance, LLSD()));*/ + getChild("popup_btn")->setCommitCallback(boost::bind(&AORemoteCtrl::onClickPopupBtn, this)); return TRUE; } -// static -void AORemoteCtrl::onClickToggleAO(void* data) +void AORemoteCtrl::onClickPopupBtn() { - BOOL ao_enable = gSavedSettings.getBOOL("AOEnabled"); - gSavedSettings.setBOOL("AOEnabled", !ao_enable); -} - -//static -void AORemoteCtrl::onClickToggleAOSit(void* data) -{ - BOOL sit_enable = gSavedSettings.getBOOL("AOSitsEnabled"); - gSavedSettings.setBOOL("AOSitsEnabled", !sit_enable); -} - -//static -void AORemoteCtrl::onClickShowAO(void* data) -{ - LLFloaterAO::show(NULL); -} - -//static -void AORemoteCtrl::onClickPopupBtn(void* data) -{ - AORemoteCtrl* remotep = (AORemoteCtrl*)data; - - remotep->deleteAllChildren(); - remotep->build(); + deleteAllChildren(); + build(); gOverlayBar->layoutButtons(); } diff --git a/indra/newview/aoremotectrl.h b/indra/newview/aoremotectrl.h index ca74e0de6..417f7b329 100644 --- a/indra/newview/aoremotectrl.h +++ b/indra/newview/aoremotectrl.h @@ -37,18 +37,12 @@ class AORemoteCtrl : public LLPanel { public: AORemoteCtrl(); - virtual ~AORemoteCtrl(); - /*virtual*/ BOOL postBuild(); - /*virtual*/ void draw(); + BOOL postBuild() override; private: void build(); - - static void onClickToggleAO(void* data); - static void onClickToggleAOSit(void* data); - static void onClickShowAO(void* data); - static void onClickPopupBtn(void* data); + void onClickPopupBtn(); }; #endif // AOREMOTECTRL_H diff --git a/indra/newview/app_settings/ao_template.ini b/indra/newview/app_settings/ao_template.ini index 03f02e305..ab4c68cac 100644 --- a/indra/newview/app_settings/ao_template.ini +++ b/indra/newview/app_settings/ao_template.ini @@ -63,6 +63,7 @@ [ Running ] [ Turning Right ] [ Turning Left ] +[ Typing ] [ Floating ] [ Swimming Forward ] [ Swimming Up ] diff --git a/indra/newview/app_settings/ca-bundle.crt b/indra/newview/app_settings/ca-bundle.crt index eff0eda08..e049f41fb 100644 --- a/indra/newview/app_settings/ca-bundle.crt +++ b/indra/newview/app_settings/ca-bundle.crt @@ -16,6 +16,301 @@ ## Conversion done with mk-ca-bundle.pl version 1.27. ## SHA256: a3ac15b98179dd2f3c5de076d10b1d53048754372f7207c2f327510cdd78fbd8 ## +LetsEncrypt Root CA2 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 03:74:f1:c1:0b:b8:be:e9:c5:30:d3:e4:43:db:f8:ff:05:0d + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 + Validity + Not Before: Mar 10 21:26:03 2019 GMT + Not After : Jun 8 21:26:03 2019 GMT + Subject: CN = login.virtual-nexus.xyz + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (4096 bit) + Modulus: + 00:aa:ea:b2:94:3e:9e:bf:08:c7:9a:3b:93:9d:4a: + a4:8e:18:f1:60:a1:26:d1:78:c1:72:11:8e:da:3e: + fa:16:3a:f4:a4:b2:de:ee:ec:eb:86:f1:88:2e:78: + 1a:04:90:44:e4:c2:72:c3:85:cf:8e:3b:9a:cd:f6: + 2c:4e:cb:39:06:18:2a:b9:70:dd:a5:d0:d6:71:01: + e8:e6:cc:f4:86:dd:88:2b:d5:de:28:a4:6c:a2:c9: + ab:dd:8b:0f:12:2a:1d:67:0f:fe:02:f9:33:1e:0c: + 84:be:16:d2:f2:b0:4d:b3:54:3d:db:df:83:8d:9a: + 1e:16:46:0b:f5:42:fb:91:4b:6a:95:7e:6b:48:e3: + 86:d8:60:5d:4d:9a:4e:a6:82:e7:05:02:83:78:72: + b4:62:8b:27:30:3a:20:8f:72:95:58:56:45:43:db: + 4e:e3:4a:87:0a:c9:58:f9:ec:63:65:d4:eb:f7:1d: + cc:2b:5c:8d:7d:61:54:0f:83:78:9a:b3:bf:db:87: + 45:b9:99:9b:54:75:e8:1c:52:29:f8:37:ad:24:5a: + ca:2b:1a:b2:11:8e:d4:34:bb:00:a8:e2:ae:ed:34: + 14:30:13:e1:06:c5:ee:d4:b2:9b:94:8d:f5:dc:2b: + a0:d3:ab:35:32:8b:e5:cf:d5:44:68:69:5d:3f:22: + 86:c9:2d:14:b6:b1:82:0c:fe:87:4d:a0:0a:76:1d: + c3:be:b9:ff:6e:c4:9e:f7:2d:d2:f5:ad:4d:aa:7f: + 4e:44:d6:7b:b4:6a:d0:71:52:48:2c:30:bf:11:b5: + df:70:40:30:cc:1a:e4:c0:a0:90:86:56:bf:36:92: + 2a:00:a5:0d:3b:2d:42:29:c1:4c:d6:9f:47:83:ff: + c1:c4:cc:04:08:9f:8b:2c:ca:4c:f5:d4:a5:38:81: + 6a:d0:4d:68:d9:fb:fa:1e:de:a1:c2:47:ea:d4:a8: + a3:b2:ba:47:50:b4:0e:15:a7:a7:ac:e2:a4:21:7c: + 20:c3:6b:ef:4f:3d:57:d0:91:ce:19:b2:da:4d:cd: + 7f:2d:2e:12:9d:e8:55:fc:02:ad:ea:57:68:d6:f9: + 96:9f:85:98:56:10:a2:bb:b6:7b:24:34:ab:9d:d7: + c9:56:47:40:b5:b1:22:80:b1:ae:37:73:6c:94:93: + 6c:db:97:47:b1:86:3c:87:5d:ad:52:2a:44:16:ce: + 0c:5c:94:1d:c2:cd:17:76:89:9a:ec:80:78:b9:a9: + c7:90:d2:c6:de:22:bb:95:1e:f6:ad:04:a0:46:c9: + 4b:12:2e:8d:ff:89:6c:c8:69:91:78:a0:f3:cf:f2: + 22:7d:68:4e:ee:32:0f:61:17:09:ba:94:bd:88:6d: + a8:65:cd + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Subject Key Identifier: + 2D:36:69:83:B8:E4:7D:38:D1:8F:8B:92:9E:A8:FD:6B:E9:25:29:97 + X509v3 Authority Key Identifier: + keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1 + + Authority Information Access: + OCSP - URI:http://ocsp.int-x3.letsencrypt.org + CA Issuers - URI:http://cert.int-x3.letsencrypt.org/ + + X509v3 Subject Alternative Name: + DNS:login.virtual-nexus.xyz + X509v3 Certificate Policies: + Policy: 2.23.140.1.2.1 + Policy: 1.3.6.1.4.1.44947.1.1.1 + CPS: http://cps.letsencrypt.org + + CT Precertificate SCTs: + Signed Certificate Timestamp: + Version : v1 (0x0) + Log ID : 74:7E:DA:83:31:AD:33:10:91:21:9C:CE:25:4F:42:70: + C2:BF:FD:5E:42:20:08:C6:37:35:79:E6:10:7B:CC:56 + Timestamp : Mar 10 22:26:03.415 2019 GMT + Extensions: none + Signature : ecdsa-with-SHA256 + 30:45:02:21:00:98:49:3F:B0:C4:66:2D:CF:CE:91:36: + 15:AF:75:94:5B:FD:C2:CB:8F:F1:E2:8D:FA:C0:61:8E: + E6:F3:7B:A8:05:02:20:62:39:0F:66:B2:79:48:A8:49: + 41:2A:D4:59:86:C3:FA:AC:C1:5D:26:02:AC:E3:D8:C8: + 0F:40:34:64:F1:D1:36 + Signed Certificate Timestamp: + Version : v1 (0x0) + Log ID : 29:3C:51:96:54:C8:39:65:BA:AA:50:FC:58:07:D4:B7: + 6F:BF:58:7A:29:72:DC:A4:C3:0C:F4:E5:45:47:F4:78 + Timestamp : Mar 10 22:26:03.442 2019 GMT + Extensions: none + Signature : ecdsa-with-SHA256 + 30:46:02:21:00:C7:09:49:5A:96:E5:B5:15:2A:F1:EA: + 03:A4:7D:10:89:23:9A:C2:C9:9F:35:85:AD:87:11:58: + A8:95:5C:0A:C6:02:21:00:88:14:C8:48:C7:6B:96:DB: + A2:AB:91:BD:F7:3D:AD:FA:37:F6:AD:BF:D7:24:DC:F0: + 11:CF:0C:D4:42:E0:72:A5 + Signature Algorithm: sha256WithRSAEncryption + 42:55:9f:ea:ae:79:1d:9d:e9:06:23:a1:d0:a9:0f:d2:bc:08: + 92:b6:05:30:29:c2:00:fc:96:6e:b1:ff:9b:00:d7:c3:7b:34: + 47:02:81:2a:44:46:85:6c:7a:f5:c3:09:e2:9c:fd:1d:d3:2c: + a4:e9:a5:99:ec:47:bd:fe:39:d2:c9:d6:ac:dd:48:40:0b:9a: + 24:f1:0b:4e:02:62:aa:63:e4:dd:8f:ad:9a:6c:5a:0a:de:d0: + a2:67:b2:ea:fa:68:ff:18:ea:e1:73:35:89:45:dd:b7:ac:2d: + 58:8a:a3:6d:eb:41:f6:fa:60:8f:39:38:33:a0:d5:fd:56:5d: + 02:08:80:40:f6:f0:c1:68:e1:15:c8:fc:c3:64:a5:43:85:18: + ed:44:5d:f8:b9:20:43:9f:3d:8b:20:e0:55:cd:78:89:d4:db: + 80:23:88:0e:6f:f5:ef:66:52:a9:c6:b1:f1:5e:6c:d6:8e:18: + 7a:93:14:65:4b:1b:a7:55:7d:9d:a5:d2:15:a8:7d:a1:aa:dd: + ef:93:94:d5:3e:2f:ce:4b:bf:ac:e5:61:da:66:61:bd:42:08: + 53:58:73:a7:80:64:78:17:60:11:71:7f:75:17:bc:24:2f:57: + 8c:61:37:87:4e:0a:41:82:74:b0:69:14:5c:89:58:55:32:1f: + 06:1b:e2:24 +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1 +WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX +NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf +89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl +Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc +Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz +uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB +AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU +BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB +FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo +SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js +LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF +BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG +AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD +VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB +ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx +A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM +UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2 +DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1 +eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu +OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw +p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY +2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0 +ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR +PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b +rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt +-----END CERTIFICATE----- + +LetsEncrypt Root CA + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 03:74:f1:c1:0b:b8:be:e9:c5:30:d3:e4:43:db:f8:ff:05:0d + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 + Validity + Not Before: Mar 10 21:26:03 2019 GMT + Not After : Jun 8 21:26:03 2019 GMT + Subject: CN = login.virtual-nexus.xyz + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (4096 bit) + Modulus: + 00:aa:ea:b2:94:3e:9e:bf:08:c7:9a:3b:93:9d:4a: + a4:8e:18:f1:60:a1:26:d1:78:c1:72:11:8e:da:3e: + fa:16:3a:f4:a4:b2:de:ee:ec:eb:86:f1:88:2e:78: + 1a:04:90:44:e4:c2:72:c3:85:cf:8e:3b:9a:cd:f6: + 2c:4e:cb:39:06:18:2a:b9:70:dd:a5:d0:d6:71:01: + e8:e6:cc:f4:86:dd:88:2b:d5:de:28:a4:6c:a2:c9: + ab:dd:8b:0f:12:2a:1d:67:0f:fe:02:f9:33:1e:0c: + 84:be:16:d2:f2:b0:4d:b3:54:3d:db:df:83:8d:9a: + 1e:16:46:0b:f5:42:fb:91:4b:6a:95:7e:6b:48:e3: + 86:d8:60:5d:4d:9a:4e:a6:82:e7:05:02:83:78:72: + b4:62:8b:27:30:3a:20:8f:72:95:58:56:45:43:db: + 4e:e3:4a:87:0a:c9:58:f9:ec:63:65:d4:eb:f7:1d: + cc:2b:5c:8d:7d:61:54:0f:83:78:9a:b3:bf:db:87: + 45:b9:99:9b:54:75:e8:1c:52:29:f8:37:ad:24:5a: + ca:2b:1a:b2:11:8e:d4:34:bb:00:a8:e2:ae:ed:34: + 14:30:13:e1:06:c5:ee:d4:b2:9b:94:8d:f5:dc:2b: + a0:d3:ab:35:32:8b:e5:cf:d5:44:68:69:5d:3f:22: + 86:c9:2d:14:b6:b1:82:0c:fe:87:4d:a0:0a:76:1d: + c3:be:b9:ff:6e:c4:9e:f7:2d:d2:f5:ad:4d:aa:7f: + 4e:44:d6:7b:b4:6a:d0:71:52:48:2c:30:bf:11:b5: + df:70:40:30:cc:1a:e4:c0:a0:90:86:56:bf:36:92: + 2a:00:a5:0d:3b:2d:42:29:c1:4c:d6:9f:47:83:ff: + c1:c4:cc:04:08:9f:8b:2c:ca:4c:f5:d4:a5:38:81: + 6a:d0:4d:68:d9:fb:fa:1e:de:a1:c2:47:ea:d4:a8: + a3:b2:ba:47:50:b4:0e:15:a7:a7:ac:e2:a4:21:7c: + 20:c3:6b:ef:4f:3d:57:d0:91:ce:19:b2:da:4d:cd: + 7f:2d:2e:12:9d:e8:55:fc:02:ad:ea:57:68:d6:f9: + 96:9f:85:98:56:10:a2:bb:b6:7b:24:34:ab:9d:d7: + c9:56:47:40:b5:b1:22:80:b1:ae:37:73:6c:94:93: + 6c:db:97:47:b1:86:3c:87:5d:ad:52:2a:44:16:ce: + 0c:5c:94:1d:c2:cd:17:76:89:9a:ec:80:78:b9:a9: + c7:90:d2:c6:de:22:bb:95:1e:f6:ad:04:a0:46:c9: + 4b:12:2e:8d:ff:89:6c:c8:69:91:78:a0:f3:cf:f2: + 22:7d:68:4e:ee:32:0f:61:17:09:ba:94:bd:88:6d: + a8:65:cd + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Subject Key Identifier: + 2D:36:69:83:B8:E4:7D:38:D1:8F:8B:92:9E:A8:FD:6B:E9:25:29:97 + X509v3 Authority Key Identifier: + keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1 + + Authority Information Access: + OCSP - URI:http://ocsp.int-x3.letsencrypt.org + CA Issuers - URI:http://cert.int-x3.letsencrypt.org/ + + X509v3 Subject Alternative Name: + DNS:login.virtual-nexus.xyz + X509v3 Certificate Policies: + Policy: 2.23.140.1.2.1 + Policy: 1.3.6.1.4.1.44947.1.1.1 + CPS: http://cps.letsencrypt.org + + CT Precertificate SCTs: + Signed Certificate Timestamp: + Version : v1 (0x0) + Log ID : 74:7E:DA:83:31:AD:33:10:91:21:9C:CE:25:4F:42:70: + C2:BF:FD:5E:42:20:08:C6:37:35:79:E6:10:7B:CC:56 + Timestamp : Mar 10 22:26:03.415 2019 GMT + Extensions: none + Signature : ecdsa-with-SHA256 + 30:45:02:21:00:98:49:3F:B0:C4:66:2D:CF:CE:91:36: + 15:AF:75:94:5B:FD:C2:CB:8F:F1:E2:8D:FA:C0:61:8E: + E6:F3:7B:A8:05:02:20:62:39:0F:66:B2:79:48:A8:49: + 41:2A:D4:59:86:C3:FA:AC:C1:5D:26:02:AC:E3:D8:C8: + 0F:40:34:64:F1:D1:36 + Signed Certificate Timestamp: + Version : v1 (0x0) + Log ID : 29:3C:51:96:54:C8:39:65:BA:AA:50:FC:58:07:D4:B7: + 6F:BF:58:7A:29:72:DC:A4:C3:0C:F4:E5:45:47:F4:78 + Timestamp : Mar 10 22:26:03.442 2019 GMT + Extensions: none + Signature : ecdsa-with-SHA256 + 30:46:02:21:00:C7:09:49:5A:96:E5:B5:15:2A:F1:EA: + 03:A4:7D:10:89:23:9A:C2:C9:9F:35:85:AD:87:11:58: + A8:95:5C:0A:C6:02:21:00:88:14:C8:48:C7:6B:96:DB: + A2:AB:91:BD:F7:3D:AD:FA:37:F6:AD:BF:D7:24:DC:F0: + 11:CF:0C:D4:42:E0:72:A5 + Signature Algorithm: sha256WithRSAEncryption + 42:55:9f:ea:ae:79:1d:9d:e9:06:23:a1:d0:a9:0f:d2:bc:08: + 92:b6:05:30:29:c2:00:fc:96:6e:b1:ff:9b:00:d7:c3:7b:34: + 47:02:81:2a:44:46:85:6c:7a:f5:c3:09:e2:9c:fd:1d:d3:2c: + a4:e9:a5:99:ec:47:bd:fe:39:d2:c9:d6:ac:dd:48:40:0b:9a: + 24:f1:0b:4e:02:62:aa:63:e4:dd:8f:ad:9a:6c:5a:0a:de:d0: + a2:67:b2:ea:fa:68:ff:18:ea:e1:73:35:89:45:dd:b7:ac:2d: + 58:8a:a3:6d:eb:41:f6:fa:60:8f:39:38:33:a0:d5:fd:56:5d: + 02:08:80:40:f6:f0:c1:68:e1:15:c8:fc:c3:64:a5:43:85:18: + ed:44:5d:f8:b9:20:43:9f:3d:8b:20:e0:55:cd:78:89:d4:db: + 80:23:88:0e:6f:f5:ef:66:52:a9:c6:b1:f1:5e:6c:d6:8e:18: + 7a:93:14:65:4b:1b:a7:55:7d:9d:a5:d2:15:a8:7d:a1:aa:dd: + ef:93:94:d5:3e:2f:ce:4b:bf:ac:e5:61:da:66:61:bd:42:08: + 53:58:73:a7:80:64:78:17:60:11:71:7f:75:17:bc:24:2f:57: + 8c:61:37:87:4e:0a:41:82:74:b0:69:14:5c:89:58:55:32:1f: + 06:1b:e2:24 +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- + + GlobalSign Root CA @@ -2943,106 +3238,6 @@ JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ -----END CERTIFICATE----- -DST ACES CA X6 -# Issuer: CN=DST ACES CA X6,OU=DST ACES,O=Digital Signature Trust,C=US -# Serial Number:0d:5e:99:0a:d6:9d:b7:78:ec:d8:07:56:3b:86:15:d9 -# Subject: CN=DST ACES CA X6,OU=DST ACES,O=Digital Signature Trust,C=US -# Not Valid Before: Thu Nov 20 21:19:58 2003 -# Not Valid After : Mon Nov 20 21:19:58 2017 -# Fingerprint (MD5): 21:D8:4C:82:2B:99:09:33:A2:EB:14:24:8D:8E:5F:E8 -# Fingerprint (SHA1): 40:54:DA:6F:1C:3F:40:74:AC:ED:0F:EC:CD:DB:79:D1:53:FB:90:1D -TRUSTED_DELEGATOR: SERVER_AUTH -MUST_VERIFY_TRUST: EMAIL_PROTECTION, CODE_SIGNING -================================================= -MD5 Fingerprint=21:D8:4C:82:2B:99:09:33:A2:EB:14:24:8D:8E:5F:E8 -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 0d:5e:99:0a:d6:9d:b7:78:ec:d8:07:56:3b:86:15:d9 - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Digital Signature Trust, OU=DST ACES, CN=DST ACES CA X6 - Validity - Not Before: Nov 20 21:19:58 2003 GMT - Not After : Nov 20 21:19:58 2017 GMT - Subject: C=US, O=Digital Signature Trust, OU=DST ACES, CN=DST ACES CA X6 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:b9:3d:f5:2c:c9:94:dc:75:8a:95:5d:63:e8:84: - 77:76:66:b9:59:91:5c:46:dd:92:3e:9f:f9:0e:03: - b4:3d:61:92:bd:23:26:b5:63:ee:92:d2:9e:d6:3c: - c8:0d:90:5f:64:81:b1:a8:08:0d:4c:d8:f9:d3:05: - 28:52:b4:01:25:c5:95:1c:0c:7e:3e:10:84:75:cf: - c1:19:91:63:cf:e8:a8:91:88:b9:43:52:bb:80:b1: - 55:89:8b:31:fa:d0:b7:76:be:41:3d:30:9a:a4:22: - 25:17:73:e8:1e:e2:d3:ac:2a:bd:5b:38:21:d5:2a: - 4b:d7:55:7d:e3:3a:55:bd:d7:6d:6b:02:57:6b:e6: - 47:7c:08:c8:82:ba:de:a7:87:3d:a1:6d:b8:30:56: - c2:b3:02:81:5f:2d:f5:e2:9a:30:18:28:b8:66:d3: - cb:01:96:6f:ea:8a:45:55:d6:e0:9d:ff:67:2b:17: - 02:a6:4e:1a:6a:11:0b:7e:b7:7b:e7:98:d6:8c:76: - 6f:c1:3b:db:50:93:7e:e5:d0:8e:1f:37:b8:bd:ba: - c6:9f:6c:e9:7c:33:f2:32:3c:26:47:fa:27:24:02: - c9:7e:1d:5b:88:42:13:6a:35:7c:7d:35:e9:2e:66: - 91:72:93:d5:32:26:c4:74:f5:53:a3:b3:5d:9a:f6: - 09:cb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Key Usage: critical - Digital Signature, Non Repudiation, Certificate Sign, CRL Sign - X509v3 Subject Alternative Name: - email:pki-ops@trustdst.com - X509v3 Certificate Policies: - Policy: 2.16.840.1.101.3.2.1.1.1 - CPS: http://www.trustdst.com/certificates/policy/ACES-index.html - - X509v3 Subject Key Identifier: - 09:72:06:4E:18:43:0F:E5:D6:CC:C3:6A:8B:31:7B:78:8F:A8:83:B8 - Signature Algorithm: sha1WithRSAEncryption - a3:d8:8e:d6:b2:db:ce:05:e7:32:cd:01:d3:04:03:e5:76:e4: - 56:2b:9c:99:90:e8:08:30:6c:df:7d:3d:ee:e5:bf:b5:24:40: - 84:49:e1:d1:28:ae:c4:c2:3a:53:30:88:f1:f5:77:6e:51:ca: - fa:ff:99:af:24:5f:1b:a0:fd:f2:ac:84:ca:df:a9:f0:5f:04: - 2e:ad:16:bf:21:97:10:81:3d:e3:ff:87:8d:32:dc:94:e5:47: - 8a:5e:6a:13:c9:94:95:3d:d2:ee:c8:34:95:d0:80:d4:ad:32: - 08:80:54:3c:e0:bd:52:53:d7:52:7c:b2:69:3f:7f:7a:cf:6a: - 74:ca:fa:04:2a:9c:4c:5a:06:a5:e9:20:ad:45:66:0f:69:f1: - dd:bf:e9:e3:32:8b:fa:e0:c1:86:4d:72:3c:2e:d8:93:78:0a: - 2a:f8:d8:d2:27:3d:19:89:5f:5a:7b:8a:3b:cc:0c:da:51:ae: - c7:0b:f7:2b:b0:37:05:ec:bc:57:23:e2:38:d2:9b:68:f3:56: - 12:88:4f:42:7c:b8:31:c4:b5:db:e4:c8:21:34:e9:48:11:35: - ee:fa:c7:92:57:c5:9f:34:e4:c7:f6:f7:0e:0b:4c:9c:68:78: - 7b:71:31:c7:eb:1e:e0:67:41:f3:b7:a0:a7:cd:e5:7a:33:36: - 6a:fa:9a:2b ------BEGIN CERTIFICATE----- -MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb -MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx -ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w -MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD -VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx -FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu -ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7 -gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH -fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a -ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT -ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF -MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk -c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto -dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt -aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI -hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk -QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/ -h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq -nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR -rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2 -9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis= ------END CERTIFICATE----- - SwissSign Gold CA - G2 # Issuer: CN=SwissSign Gold CA - G2,O=SwissSign AG,C=CH # Serial Number:00:bb:40:1c:43:f5:5e:4f:b0 diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml index 253b0ad6e..fbb2bb10f 100644 --- a/indra/newview/app_settings/cmd_line.xml +++ b/indra/newview/app_settings/cmd_line.xml @@ -211,12 +211,6 @@ NoHardwareProbe - noquicktime - - map-to - NoQuickTime - - nosound map-to diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 79316b2f4..ad8787a7e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -454,6 +454,8 @@ Boolean Value 0 + IsCOA + 1 AOAdvanced @@ -476,6 +478,21 @@ Boolean Value 1 + IsCOA + 1 + + AOSwimEnabled + + Comment + Overrides fly animations with swims when underwater. + Persist + 1 + Type + Boolean + Value + 1 + IsCOA + 1 AONoStandsInMouselook @@ -827,7 +844,7 @@ Type Boolean Value - 0 + 1 LiruMiniBuildFloater @@ -1159,17 +1176,6 @@ Found in Advanced->Rendering->Info Displays Value 0 - FloaterUnfocusedBackgroundOpaque - - Comment - Disables floaters going transparent when not in focus, may conflict with some skins, though. - Persist - 1 - Type - Boolean - Value - 0 - FSSynchronizeTextureMaps Comment @@ -1190,7 +1196,7 @@ Found in Advanced->Rendering->Info Displays Type Boolean Value - 0 + 1 InstantMessageLogPathAnyAccount @@ -1532,15 +1538,6 @@ This should be as low as possible, but too low may break functionality Value 0 - InventorySortOrder - - Comment - Specifies sort key for inventory items (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) - Type - U32 - Value - 3 - Blacklist.Settings Comment @@ -2560,6 +2557,17 @@ This should be as low as possible, but too low may break functionality Value 300.0 + ActiveFloaterTransparency + + Comment + Transparency of active floaters (floaters that have focus) + Persist + 1 + Type + F32 + Value + 1 + AdvanceSnapshot Comment @@ -5468,6 +5476,17 @@ This should be as low as possible, but too low may break functionality Value 1 + DebugHideEmptySystemFolders + + Comment + Hide empty system folders when on + Persist + 1 + Type + Boolean + Value + 1 + DebugInventoryFilters Comment @@ -7683,6 +7702,17 @@ This should be as low as possible, but too low may break functionality Value 24 + RadarColumnNotesWidth + + Comment + Width for radar's notes column + Persist + 1 + Type + S32 + Value + 24 + RadarColumnAgeWidth Comment @@ -7771,6 +7801,17 @@ This should be as low as possible, but too low may break functionality Value 0 + RadarColumnNotesHidden + + Comment + Hide radar's notes column + Persist + 1 + Type + Boolean + Value + 1 + RadarColumnAgeHidden Comment @@ -7839,6 +7880,28 @@ This should be as low as possible, but too low may break functionality Value 1 + RadarAlertFlood + + Comment + Show the flood of people arriving and (if RadarAlertFloodLeaving) leaving when you login and when you open/close the Radar with RadarKeepOpen off. + Persist + 1 + Type + Boolean + Value + 0 + + RadarAlertFloodLeaving + + Comment + When activated along with RadarAlertFlood shows the flood of people leaving when you close the Radar with RadarKeepOpen off. + Persist + 1 + Type + Boolean + Value + 0 + RadarAlertShowDist Comment @@ -8109,10 +8172,26 @@ This should be as low as possible, but too low may break functionality 0 - FloaterOutboxRect + FloaterMarketplaceListingsRect Comment - Rectangle for merchant outbox window + Rectangle for Marketplace Listings window + Persist + 1 + Type + Rect + Value + + 0 + 342 + 310 + 52 + + + FloaterMarketplaceValidationRect + + Comment + Rectangle for Marketplace Validation window Persist 1 Type @@ -9099,6 +9178,22 @@ This should be as low as possible, but too low may break functionality Value 400 + HTMLAgentColor + + Comment + Color of hyperlinked user/object names + Persist + 1 + Type + Color4 + Value + + 0.600000023842 + 0.600000023842 + 1.0 + 1.0 + + HTMLLinkColor Comment @@ -9339,6 +9434,17 @@ This should be as low as possible, but too low may break functionality Value 1 + InactiveFloaterTransparency + + Comment + Transparency of inactive floaters (floaters that have no focus) + Persist + 1 + Type + F32 + Value + 1 + InBandwidth Comment @@ -9416,12 +9522,12 @@ This should be as low as possible, but too low may break functionality Value 0 - InventoryDisplayOutbox + InventoryOutboxDisplayBoth Comment - Override merchant inventory outbox display + Show the legacy Merchant Outbox UI as well as the Marketplace Listings UI Persist - 0 + 1 Type Boolean Value @@ -9438,6 +9544,17 @@ This should be as low as possible, but too low may break functionality Value 0 + InventoryOutboxMakeVisible + + Comment + Enable making the Merchant Outbox and Inbox visible in the inventory for debug purposes. + Persist + 1 + Type + Boolean + Value + 1 + InventoryOutboxMaxFolderCount Comment @@ -9447,7 +9564,7 @@ This should be as low as possible, but too low may break functionality Type U32 Value - 21 + 20 InventoryOutboxMaxFolderDepth @@ -9471,6 +9588,17 @@ This should be as low as possible, but too low may break functionality Value 200 + InventoryOutboxMaxStockItemCount + + Comment + Maximum number of items allowed in a stock folder. + Persist + 0 + Type + U32 + Value + 200 + InventorySortOrder Comment @@ -9482,6 +9610,17 @@ This should be as low as possible, but too low may break functionality Value 7 + MarketplaceListingsSortOrder + + Comment + Specifies sort for marketplace listings + Persist + 1 + Type + U32 + Value + 2 + InvertMouse Comment @@ -10323,6 +10462,17 @@ This should be as low as possible, but too low may break functionality Value + MarketplaceListingsLogging + + Comment + Enable debug output associated with the Marketplace Listings (SLM) API. + Persist + 1 + Type + Boolean + Value + 0 + MaxDragDistance Comment @@ -13838,7 +13988,7 @@ This should be as low as possible, but too low may break functionality Type Boolean Value - 1 + 0 RenderHideGroupTitle diff --git a/indra/newview/app_settings/settings_ascent.xml b/indra/newview/app_settings/settings_ascent.xml index dd373885d..a11c8a970 100644 --- a/indra/newview/app_settings/settings_ascent.xml +++ b/indra/newview/app_settings/settings_ascent.xml @@ -818,6 +818,17 @@ Value /regionsay + SinguCmdLineSetting + + Comment + Set or toggle debug settings from the chatbar. + Persist + 1 + Type + String + Value + /setdebug + SinguCmdLineURL Comment @@ -862,17 +873,6 @@ Value 0 - SinguMotionResetsCamera - - Comment - Allow camera to reset when moving. - Persist - 1 - Type - Boolean - Value - 1 - SinguLastKnownReleaseBuild Comment @@ -895,6 +895,54 @@ Value 0 + SinguMotionResetsCamera + + Comment + Allow camera to reset when moving. + Persist + 1 + Type + Boolean + Value + 1 + + SinguMotionResetsCameraReset + + Comment + Disable for the behavior Liru intended SinguMotionResetsCamera to have, wherein it doesn't reset every time you press escape, teleport, or whatever else! So you can walk around and your camera won't reset at all like it was meant to be. +RIP Latif Khalifa. + Persist + 1 + Type + Boolean + Value + 1 + + SinguPostProcessDefault + + Comment + Default post process preset, changing this will change your active preset + Persist + 1 + Type + String + Value + Default + + SinguReplaceLinks + + Comment + Whether or not to visually replace special links like SLURLs with labels where applicable. +While having this on allows people to mislead you with links that look like other links, it also allows you to use SLURLs that appear as people's names and increasingly more scripts use this to run faster. +You can always select the text, right click and select Copy Raw to copy the hidden contents of the link. +Changing this setting only affects new text. + Persist + 1 + Type + Boolean + Value + 1 + WarnRecommendedUpdate Comment @@ -1006,6 +1054,28 @@ Value 3.5 + EmergencyTeleportSeconds + + Comment + The remaining time left in seconds when to execute an emergency teleport out of a restarting region. + Persist + 1 + Type + U32 + Value + 20 + + LiruEnableWIPUI + + Comment + Whether or not to enable UI components that probably don't work yet. + Persist + 1 + Type + Boolean + Value + 0 + LiruFlashWhenMinimized Comment @@ -1713,6 +1783,17 @@ IsCOA 1 + ToolbarVisibleMarketplaceListings + + Comment + Whether or not the button for marketplace listings is on the toolbar + Persist + 1 + Type + Boolean + Value + 0 + ToolbarVisibleMeanEvents Comment @@ -1843,10 +1924,10 @@ IsCOA 1 - ToolbarVisibleOutbox + ToolbarVisibleMarketplaceListings Comment - Whether or not the button for outbox is on the toolbar + Whether or not the button for Marketplace Listings is on the toolbar Persist 1 Type @@ -1869,19 +1950,6 @@ IsCOA 1 - ToolbarVisibleOutbox - - Comment - Whether or not the button for the merchant outbox is on the toolbar - Persist - 1 - Type - Boolean - Value - 0 - IsCOA - 1 - ToolbarVisibleOutfit Comment diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 2dd4904f4..27a01079c 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -186,6 +186,61 @@ Value + AODefaultTyping + + Comment + Default typing anim + Persist + 1 + Type + String + Value + + + AODefaultFloat + + Comment + Default float anim + Persist + 1 + Type + String + Value + + + AODefaultSwim + + Comment + Default swim anim + Persist + 1 + Type + String + Value + + + AODefaultSwimUp + + Comment + Default swimup anim + Persist + 1 + Type + String + Value + + + AODefaultSwimDown + + Comment + Default swimdown anim + Persist + 1 + Type + String + Value + + AODefaultStandUp Comment @@ -715,6 +770,17 @@ Value -%Y-%m + ShowFavoritesOnLogin + + Comment + Determines whether favorites of last logged in user will be saved on exit from viewer and shown on login screen + Persist + 1 + Type + Boolean + Value + 1 + KeywordsChangeColor diff --git a/indra/newview/app_settings/windlight/postprocesseffects.xml b/indra/newview/app_settings/windlight/postprocesseffects.xml index 1f3ee7755..bf917ab33 100644 --- a/indra/newview/app_settings/windlight/postprocesseffects.xml +++ b/indra/newview/app_settings/windlight/postprocesseffects.xml @@ -6,6 +6,8 @@ 4.5799999237060547 bloom_width 12.539999961853027 + gamma + 1.0 brightness 0.89999997615814209 brightness_multiplier @@ -42,6 +44,8 @@ 1.5 bloom_width 2.25 + gamma + 1.0 brightness 1 brightness_multiplier @@ -144,7 +148,7 @@ saturation 1 - default + Default bloom_strength 1.5 @@ -173,12 +177,12 @@ 0 enable_gauss_blur 0 - enable_posterize - 0 - enable_motionblur - 0 - enable_vignette - 0 + enable_posterize + 0 + enable_motionblur + 0 + enable_vignette + 0 gauss_blur_passes 2 extract_high @@ -191,82 +195,20 @@ 0.40000000000000002 saturation 1 - posterize_layers - 10 - blur_strength - 10 - vignette_strength - 0.85 - vignette_radius - 0.7 - vignette_darkness - 1.0 - vignette_desaturation - 1.0 - vignette_chromatic_aberration - 0.01 - - PyFx None - - bloom_strength - 1.5 - bloom_width - 2.25 - blur_strength - 10 - brightness - 1 - brightness_multiplier - 3 - contrast - 1 - contrast_base - - 1 - 1 - 1 - 0.5 - - enable_bloom - 0 - enable_color_filter - 0 - enable_gauss_blur - 0 - enable_motionblur - 0 - enable_night_vision - 0 - enable_posterize - 0 - enable_vignette - 0 - extract_high - 1 - extract_low - 0.949999999999999955591079 - gamma - 1 - gauss_blur_passes - 2 - noise_size - 25 - noise_strength - 0.4000000000000000222044605 posterize_layers 10 - saturation - 1 - vignette_chromatic_aberration - 0.01000000000000000020816682 - vignette_darkness - 1 - vignette_desaturation - 1 - vignette_radius - 0.699999999999999955591079 + blur_strength + 10 vignette_strength - 0.8499999999999999777955395 + 0.85 + vignette_radius + 0.7 + vignette_darkness + 1.0 + vignette_desaturation + 1.0 + vignette_chromatic_aberration + 0.01 PyFX CatVision v1 @@ -826,7 +768,7 @@ vignette_strength 1 - PyFX v1 + PyFX Twilight v1 Cold bloom_strength 1.5 @@ -835,15 +777,77 @@ blur_strength 30 brightness - 1.19999992847442626953125 + 1.25 brightness_multiplier 3 contrast - 1.25 + 1.0299999713897705078125 contrast_base - 0.7999999523162841796875 - 0.89999997615814208984375 + 1 + 0.329999983310699462890625 + 0 + 1 + + enable_bloom + 0 + enable_color_filter + 1 + enable_gauss_blur + 0 + enable_motionblur + 1 + enable_night_vision + 0 + enable_posterize + 0 + enable_vignette + 1 + extract_high + 1 + extract_low + 0.949999999999999955591079 + gamma + 1.5 + gauss_blur_passes + 2 + noise_size + 25 + noise_strength + 0.4000000000000000222044605 + posterize_layers + 10 + saturation + 0.5 + vignette_chromatic_aberration + 0.005000000353902578353881836 + vignette_darkness + 1 + vignette_desaturation + 0.4000000059604644775390625 + vignette_radius + 5 + vignette_strength + 1 + + PyFX Twilight v1 Warm + + bloom_strength + 1.5 + bloom_width + 2.25 + blur_strength + 30 + brightness + 1.25 + brightness_multiplier + 3 + contrast + 1.0299999713897705078125 + contrast_base + + 0 + 0.66999995708465576171875 1 1 @@ -866,7 +870,7 @@ extract_low 0.949999999999999955591079 gamma - 2 + 1.5 gauss_blur_passes 2 noise_size @@ -876,15 +880,15 @@ posterize_layers 10 saturation - 0.7999999523162841796875 + 0.5 vignette_chromatic_aberration - 0.004500000271946191787719727 + 0.005000000353902578353881836 vignette_darkness 1 vignette_desaturation - 0 + 0.4000000059604644775390625 vignette_radius - 1.5 + 5 vignette_strength 1 @@ -950,7 +954,69 @@ vignette_strength 1 - PyFx default + PyFX Vivid v1 Cold + + bloom_strength + 1.5 + bloom_width + 2.25 + blur_strength + 30 + brightness + 1.19999992847442626953125 + brightness_multiplier + 3 + contrast + 1.25 + contrast_base + + 1 + 0.89999997615814208984375 + 0.7999999523162841796875 + 1 + + enable_bloom + 0 + enable_color_filter + 1 + enable_gauss_blur + 0 + enable_motionblur + 1 + enable_night_vision + 0 + enable_posterize + 0 + enable_vignette + 1 + extract_high + 1 + extract_low + 0.949999999999999955591079 + gamma + 2 + gauss_blur_passes + 2 + noise_size + 25 + noise_strength + 0.4000000000000000222044605 + posterize_layers + 10 + saturation + 1.19999980926513671875 + vignette_chromatic_aberration + 0.004500000271946191787719727 + vignette_darkness + 1 + vignette_desaturation + 0 + vignette_radius + 2.9999997615814208984375 + vignette_strength + 1 + + PyFX v1 bloom_strength 1.5 @@ -972,7 +1038,7 @@ 1 enable_bloom - 1 + 0 enable_color_filter 1 enable_gauss_blur @@ -1012,5 +1078,191 @@ vignette_strength 1 + PyFX v2 + + bloom_strength + 1.5 + bloom_width + 2.25 + blur_strength + 30 + brightness + 1 + brightness_multiplier + 3 + contrast + 2 + contrast_base + + 0.949999988079071044921875 + 0.969999969005584716796875 + 1 + 1 + + enable_bloom + 1 + enable_color_filter + 1 + enable_gauss_blur + 0 + enable_motionblur + 1 + enable_night_vision + 0 + enable_posterize + 0 + enable_vignette + 1 + extract_high + 1 + extract_low + 0.949999999999999955591079 + gamma + 5 + gauss_blur_passes + 2 + noise_size + 25 + noise_strength + 0.4000000000000000222044605 + posterize_layers + 10 + saturation + 1 + vignette_chromatic_aberration + 0.004500000271946191787719727 + vignette_darkness + 1 + vignette_desaturation + 0 + vignette_radius + 1.5 + vignette_strength + 1 + + PyFX v3 + + bloom_strength + 1.5 + bloom_width + 2.25 + blur_strength + 30 + brightness + 1 + brightness_multiplier + 3 + contrast + 1.75 + contrast_base + + 0.949999988079071044921875 + 0.969999969005584716796875 + 1 + 1 + + enable_bloom + 1 + enable_color_filter + 1 + enable_gauss_blur + 0 + enable_motionblur + 1 + enable_night_vision + 0 + enable_posterize + 0 + enable_vignette + 1 + extract_high + 1 + extract_low + 0.949999999999999955591079 + gamma + 4 + gauss_blur_passes + 2 + noise_size + 25 + noise_strength + 0.4000000000000000222044605 + posterize_layers + 10 + saturation + 1.099999904632568359375 + vignette_chromatic_aberration + 0.004500000271946191787719727 + vignette_darkness + 0.66999995708465576171875 + vignette_desaturation + 0 + vignette_radius + 2.9999997615814208984375 + vignette_strength + 1 + + PyFX Default + + bloom_strength + 1.5 + bloom_width + 2.25 + blur_strength + 30 + brightness + 1 + brightness_multiplier + 3 + contrast + 1.75 + contrast_base + + 0.949999988079071044921875 + 0.969999969005584716796875 + 1 + 1 + + enable_bloom + 1 + enable_color_filter + 1 + enable_gauss_blur + 0 + enable_motionblur + 1 + enable_night_vision + 0 + enable_posterize + 0 + enable_vignette + 1 + extract_high + 1 + extract_low + 0.949999999999999955591079 + gamma + 4 + gauss_blur_passes + 2 + noise_size + 25 + noise_strength + 0.4000000000000000222044605 + posterize_layers + 10 + saturation + 1.099999904632568359375 + vignette_chromatic_aberration + 0.004500000271946191787719727 + vignette_darkness + 0.66999995708465576171875 + vignette_desaturation + 0 + vignette_radius + 2.9999997615814208984375 + vignette_strength + 1 + diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Black.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Black.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Black.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Black.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Dark%20Blue.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Dark%20Blue.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Dark%20Blue.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Dark%20Blue.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Dark.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Dark.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Dark.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Dark.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Gray%20Blue.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Gray%20Blue.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Gray%20Blue.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Gray%20Blue.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Gray.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Gray.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Gray.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Gray.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Underwater.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Underwater.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20Underwater.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20Underwater.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White%20Wash.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White%20Wash.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White%20Wash.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White%20Wash.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White.xml similarity index 95% rename from indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White.xml index b33f7ad69..c91588466 100644 --- a/indra/newview/app_settings/windlight/skies/Ambient%20%2BFog%20White.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20%2BFog%20White.xml @@ -2,10 +2,10 @@ ambient - 3 - 3 - 3 - 1 + 1.5 + 1.5 + 1.5 + 0.5 blue_density @@ -63,14 +63,14 @@ density_multiplier - 4.999999873689375817775726e-05 + 0.0008999999845400452613830566 0 0 1 distance_multiplier - 50 + 1 0 0 1 diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Black.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Black.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Black.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Black.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Dark%20H%2DBomb.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Dark%20H%2DBomb.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Dark%20H%2DBomb.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Dark%20H%2DBomb.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Not%20As%20Dark.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Dark.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Not%20As%20Dark.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Dark.xml diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray%20Background.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray%20Background.xml new file mode 100644 index 000000000..fe1c9f8c0 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray%20Background.xml @@ -0,0 +1,141 @@ + + + ambient + + 1.5 + 1.5 + 1.5 + 0.5 + + blue_density + + 0.449999993698308176082179 + 0.4499999939833594919669224 + 0.4499999944309046040480382 + 1 + + blue_horizon + + 0.89999997615814208984375 + 0.89999997615814208984375 + 0.89999997615814208984375 + 0.449999988079071044921875 + + cloud_color + + 0.2261540024157503436796901 + 0.2261540024157503436796901 + 0.2261540024157503436796901 + 1 + + cloud_pos_density1 + + 0 + 0 + 0 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0.125 + 1 + + cloud_scale + + 0.4199999868869781494140625 + 0 + 0 + 1 + + cloud_scroll_rate + + 10 + 10 + + cloud_shadow + + 0.36000001430511474609375 + 0 + 0 + 1 + + density_multiplier + + 0.0004499999922700226306915283 + 0 + 0 + 1 + + distance_multiplier + + 0 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 5 + 0.001000000047497451305389404 + -0 + 1 + + haze_density + + 4 + 0 + 0 + 1 + + haze_horizon + + 0 + 0.1991560043746730401803546 + 0.1991560043746730401803546 + 1 + + lightnorm + + -0 + 1 + 1.148381556959066074341536e-06 + 0 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 26 + star_brightness + 0 + sun_angle + 1.57079517841339111328125 + sunlight_color + + 0 + 0 + 0 + 0 + + + diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Gray.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Gray.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20Gray.xml diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Not%20As%20White.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20White.xml similarity index 100% rename from indra/newview/app_settings/windlight/skies/Ambient%20Not%20As%20White.xml rename to indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Ambient%20White.xml diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Afternoon.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Afternoon.xml index e839d6245..233a3b354 100644 --- a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Afternoon.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Afternoon.xml @@ -37,14 +37,14 @@ cloud_pos_density2 - 1.6884100437164306640625 + 1 1 0 1 cloud_scale - 0.07000000029802322387695313 + 0.2000000029802322387695313 0 0 1 @@ -56,7 +56,7 @@ cloud_shadow - 0.66999995708465576171875 + 0.75 0 0 1 @@ -105,7 +105,7 @@ haze_horizon - 0 + 0.1499999910593032836914063 0.1991560012102127075195313 0.1991560012102127075195313 1 @@ -119,7 +119,7 @@ max_y - 150 + 1 0 0 1 diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Evening.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Evening.xml index 291e4691f..4039d2ee5 100644 --- a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Evening.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Evening.xml @@ -23,10 +23,10 @@ cloud_color - 0.25 - 0.25 - 0.25 - 0.25 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 cloud_pos_density1 @@ -44,7 +44,7 @@ cloud_scale - 0.07000000029802322387695313 + 0.2000000029802322387695313 0 0 1 @@ -56,7 +56,7 @@ cloud_shadow - 0.66999995708465576171875 + 0.75 0 0 1 @@ -119,7 +119,7 @@ max_y - 150 + 1 0 0 1 diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Noon.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Noon.xml new file mode 100644 index 000000000..60882441d --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Noon.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.2999999821186065673828125 + 0.44999992847442626953125 + 0.599999964237213134765625 + 0.199999988079071044921875 + + blue_density + + 2 + 2 + 2 + 1 + + blue_horizon + + 0.599999964237213134765625 + 0.599999964237213134765625 + 0.599999964237213134765625 + 0.2999999821186065673828125 + + cloud_color + + 0.329999983310699462890625 + 0.329999983310699462890625 + 0.329999983310699462890625 + 0.329999983310699462890625 + + cloud_pos_density1 + + 1 + 1 + 0.25 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 1 + 0 + 1 + + cloud_scale + + 0.2000000029802322387695313 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.3999996185302734375 + 10 + + cloud_shadow + + 0.75 + 0 + 0 + 1 + + density_multiplier + + 9.999999747378751635551453e-05 + 0 + 0 + 1 + + distance_multiplier + + 5 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 0.89999997615814208984375 + 0 + 0 + 1 + + glow + + 0.200002193450927734375 + 0.001000000047497451305389404 + -0.199999988079071044921875 + 1 + + haze_density + + 4 + 0 + 0 + 1 + + haze_horizon + + 0.2099999934434890747070313 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 1 + -4.371138828673792886547744e-08 + 0 + + max_y + + 1 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 1.57079637050628662109375 + sunlight_color + + 1.0499999523162841796875 + 1.0499999523162841796875 + 1.0499999523162841796875 + 0.3499999940395355224609375 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy%20Bright.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy%20Bright.xml new file mode 100644 index 000000000..409cea7e3 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy%20Bright.xml @@ -0,0 +1,141 @@ + + + ambient + + 0 + 0 + 0 + 0 + + blue_density + + 2 + 2 + 2 + 2 + + blue_horizon + + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.199999988079071044921875 + + cloud_color + + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + + cloud_pos_density1 + + 0.5 + 0.5 + 0.1700000017881393432617188 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 1 + 0 + 1 + + cloud_scale + + 0.2000000029802322387695313 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.3999996185302734375 + 10 + + cloud_shadow + + 0.75 + 0 + 0 + 1 + + density_multiplier + + 0.0005999999702908098697662354 + 0 + 0 + 1 + + distance_multiplier + + 3.5 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1.599999904632568359375 + 0 + 0 + 1 + + glow + + 0.200002193450927734375 + 0.001000000047497451305389404 + -0.1499999910593032836914063 + 1 + + haze_density + + 4 + 0 + 0 + 1 + + haze_horizon + + 0.04999999701976776123046875 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 0.876306593418121337890625 + -0.4817537963390350341796875 + 0 + + max_y + + 26 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.0734512805938720703125 + sunlight_color + + 0.599999964237213134765625 + 0.75 + 0.749999940395355224609375 + 0.75 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy%20HalfBright.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy%20HalfBright.xml new file mode 100644 index 000000000..eee637388 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy%20HalfBright.xml @@ -0,0 +1,141 @@ + + + ambient + + 0 + 0 + 0 + 0 + + blue_density + + 2 + 2 + 2 + 2 + + blue_horizon + + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.199999988079071044921875 + + cloud_color + + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + + cloud_pos_density1 + + 0.5 + 0.5 + 0.1700000017881393432617188 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 1 + 0 + 1 + + cloud_scale + + 0.2000000029802322387695313 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.3999996185302734375 + 10 + + cloud_shadow + + 0.75 + 0 + 0 + 1 + + density_multiplier + + 0.0005999999702908098697662354 + 0 + 0 + 1 + + distance_multiplier + + 3.5 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1.19999992847442626953125 + 0 + 0 + 1 + + glow + + 0.200002193450927734375 + 0.001000000047497451305389404 + -0.1499999910593032836914063 + 1 + + haze_density + + 4 + 0 + 0 + 1 + + haze_horizon + + 0.04999999701976776123046875 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 0.876306593418121337890625 + -0.4817537963390350341796875 + 0 + + max_y + + 100 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.0734512805938720703125 + sunlight_color + + 0.599999964237213134765625 + 0.75 + 0.749999940395355224609375 + 0.75 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy.xml index 9ba62cce2..1b1fd7488 100644 --- a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Cloudy%20Rainy.xml @@ -30,9 +30,9 @@ cloud_pos_density1 - 1 - 1 - 0.2999999821186065673828125 + 0.5 + 0.5 + 0.1700000017881393432617188 1 cloud_pos_density2 @@ -44,33 +44,33 @@ cloud_scale - 0.07000000029802322387695313 + 0.2000000029802322387695313 0 0 1 cloud_scroll_rate - 11 + 10.3999996185302734375 10 cloud_shadow - 0.66999995708465576171875 + 0.75 0 0 1 density_multiplier - 0.0003999999898951500654220581 + 0.0005999999702908098697662354 0 0 1 distance_multiplier - 6 + 3.5 0 0 1 @@ -93,7 +93,7 @@ 0.200002193450927734375 0.001000000047497451305389404 - -0.2499999850988388061523438 + -0.1499999910593032836914063 1 haze_density @@ -105,7 +105,7 @@ haze_horizon - 0 + 0.04999999701976776123046875 0.1991560012102127075195313 0.1991560012102127075195313 1 @@ -119,7 +119,7 @@ max_y - 70 + 100 0 0 1 diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Default%20.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Default%20.xml index 722a18847..b26c41ae1 100644 --- a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Default%20.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Default%20.xml @@ -2,49 +2,49 @@ ambient + 0.2880000174045562744140625 + 0.449999988079071044921875 + 0.599999964237213134765625 0.599999964237213134765625 - 1.0499999523162841796875 - 1.19999992847442626953125 - 1.19999992847442626953125 blue_density 0.199999988079071044921875 0.2999999821186065673828125 - 0.39999997615814208984375 - 0.699999988079071044921875 + 0.599999964237213134765625 + 0.2999999821186065673828125 blue_horizon - 0.39999997615814208984375 - 0.5 - 1 - 0.5 + 0 + 0.199999988079071044921875 + 0.2999999821186065673828125 + 0.2999999821186065673828125 cloud_color - 0.2399999797344207763671875 - 0.2399999797344207763671875 - 0.2399999797344207763671875 - 0.2399999946355819702148438 + 1 + 1 + 1 + 1 cloud_pos_density1 1 1 - 0.329999983310699462890625 + 0.08999999612569808959960938 1 cloud_pos_density2 1.6884100437164306640625 0 - 0.04999999701976776123046875 + 0.75 1 cloud_scale - 0.0999999940395355224609375 + 0.4999999701976776123046875 0 0 1 @@ -63,14 +63,14 @@ density_multiplier - 9.999999747378751635551453e-05 + 0.0002500000118743628263473511 0 0 1 distance_multiplier - 6 + 1 0 0 1 @@ -84,28 +84,28 @@ gamma - 0.5 + 0.75 0 0 1 glow - 5 + 10 0.001000000047497451305389404 - -0.4799999892711639404296875 + -0.4999999701976776123046875 1 haze_density - 1 + 0.199999988079071044921875 0 0 1 haze_horizon - 0.199999988079071044921875 + 0.4699999988079071044921875 0.1991560012102127075195313 0.1991560012102127075195313 1 @@ -113,13 +113,13 @@ lightnorm 0 - 0.876306593418121337890625 - -0.4817537963390350341796875 + 0.867070615291595458984375 + -0.49818527698516845703125 0 max_y - 4000 + 2000 0 0 1 @@ -129,13 +129,13 @@ star_brightness 0 sun_angle - 2.0734512805938720703125 + 2.092300891876220703125 sunlight_color - 1.7999999523162841796875 - 1.61999976634979248046875 - 1.43999946117401123046875 - 0.599999964237213134765625 + 1.5 + 1.5 + 1.5 + 0.5 diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Fine%20Afternoon.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Fine%20Afternoon.xml index fc2d3ea8e..438cfe90b 100644 --- a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Fine%20Afternoon.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Fine%20Afternoon.xml @@ -3,55 +3,55 @@ ambient 0.599999964237213134765625 - 1.0499999523162841796875 - 1.19999992847442626953125 + 1.049999237060546875 1.19999992847442626953125 + 0.39999997615814208984375 blue_density - 0.199999988079071044921875 + 0.0999999940395355224609375 0.2999999821186065673828125 0.39999997615814208984375 - 0.699999988079071044921875 + 0.560000002384185791015625 blue_horizon 0.39999997615814208984375 - 0.5 - 1 - 0.5 + 0.39999997615814208984375 + 0.7999999523162841796875 + 0.39999997615814208984375 cloud_color - 0.2399999797344207763671875 - 0.2399999797344207763671875 - 0.2399999797344207763671875 - 0.2399999946355819702148438 + 0.39999997615814208984375 + 0.2800000011920928955078125 + 0.1999999433755874633789063 + 0.39999997615814208984375 cloud_pos_density1 1 1 - 0.329999983310699462890625 + 0.0999999940395355224609375 1 cloud_pos_density2 1.6884100437164306640625 0 - 0.04999999701976776123046875 + 0.0999999940395355224609375 1 cloud_scale - 0.0999999940395355224609375 + 0.05999999493360519409179688 0 0 1 cloud_scroll_rate - 10.19999980926513671875 + 10.5 10.0109996795654296875 cloud_shadow @@ -84,28 +84,28 @@ gamma - 0.5 + 0.599999964237213134765625 0 0 1 glow - 21.8000011444091796875 + 20 0.001000000047497451305389404 - -0.2499999850988388061523438 + -1.25 1 haze_density - 1 + 0.5 0 0 1 haze_horizon - 0.199999988079071044921875 + 0.2999999821186065673828125 0.1991560012102127075195313 0.1991560012102127075195313 1 @@ -132,10 +132,10 @@ 2.51327419281005859375 sunlight_color - 2.25 - 1.68749940395355224609375 - 1.125 - 0.75 + 1.5 + 1.19999992847442626953125 + 0.89999997615814208984375 + 1.5 diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Flamesight.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Flamesight.xml new file mode 100644 index 000000000..aafbc189f --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Flamesight.xml @@ -0,0 +1,141 @@ + + + ambient + + 0 + 0 + 0 + 0 + + blue_density + + 0 + 0 + 1.5 + 1.5 + + blue_horizon + + 0 + 0 + 2 + 2 + + cloud_color + + 0.5 + 0.5 + 0.5 + 0.5 + + cloud_pos_density1 + + 1.6884100437164306640625 + 0 + 0.5 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 0.526096999645233154296875 + 0 + 1 + + cloud_scale + + 0.0999999940395355224609375 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.19999980926513671875 + 10.0109996795654296875 + + cloud_shadow + + 0.5 + 0 + 0 + 1 + + density_multiplier + + 0.0003299999807495623826980591 + 0 + 0 + 1 + + distance_multiplier + + 50 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 0.329999983310699462890625 + 0 + 0 + 1 + + glow + + 30 + 0.001000000047497451305389404 + -0.4999999701976776123046875 + 1 + + haze_density + + 1 + 0 + 0 + 1 + + haze_horizon + + 0.5 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 0.867070615291595458984375 + -0.49818527698516845703125 + 0 + + max_y + + 2000 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.092300891876220703125 + sunlight_color + + 3 + 0.990000188350677490234375 + 0 + 1 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Bright.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Bright.xml new file mode 100644 index 000000000..16ce75e17 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Bright.xml @@ -0,0 +1,141 @@ + + + ambient + + 2.25 + 2.25 + 2.25 + 0.75 + + blue_density + + 2 + 2 + 2 + 2 + + blue_horizon + + 0.1999999731779098510742188 + 0.1999999731779098510742188 + 0.1999999731779098510742188 + 0.0999999940395355224609375 + + cloud_color + + 1 + 1 + 1 + 1 + + cloud_pos_density1 + + 1 + 1 + 0.08999999612569808959960938 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 0 + 0.75 + 1 + + cloud_scale + + 0.4999999701976776123046875 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.19999980926513671875 + 10.0109996795654296875 + + cloud_shadow + + 1 + 0 + 0 + 1 + + density_multiplier + + 9.999999747378751635551453e-06 + 0 + 0 + 1 + + distance_multiplier + + 100 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 0.66999995708465576171875 + 0 + 0 + 1 + + glow + + 10 + 0.001000000047497451305389404 + -0.4999999701976776123046875 + 1 + + haze_density + + 1 + 0 + 0 + 1 + + haze_horizon + + 1 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 0.867070615291595458984375 + -0.49818527698516845703125 + 0 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.092300891876220703125 + sunlight_color + + 3 + 2.4000003337860107421875 + 2.099999904632568359375 + 1 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Cloudy.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Cloudy.xml new file mode 100644 index 000000000..40bbf3b88 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Cloudy.xml @@ -0,0 +1,141 @@ + + + ambient + + 1.5 + 1.7999999523162841796875 + 2.099999904632568359375 + 2.099999904632568359375 + + blue_density + + 2 + 2 + 2 + 2 + + blue_horizon + + 0.1999999731779098510742188 + 0.1999999731779098510742188 + 0.1999999731779098510742188 + 0.0999999940395355224609375 + + cloud_color + + 1 + 1 + 1 + 1 + + cloud_pos_density1 + + 1 + 1 + 0.08999999612569808959960938 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 0 + 0.75 + 1 + + cloud_scale + + 0.4999999701976776123046875 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.19999980926513671875 + 10.0109996795654296875 + + cloud_shadow + + 1 + 0 + 0 + 1 + + density_multiplier + + 1.999999949475750327110291e-05 + 0 + 0 + 1 + + distance_multiplier + + 100 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 0.66999995708465576171875 + 0 + 0 + 1 + + glow + + 10 + 0.001000000047497451305389404 + -0.4999999701976776123046875 + 1 + + haze_density + + 1 + 0 + 0 + 1 + + haze_horizon + + 1 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 0.867070615291595458984375 + -0.49818527698516845703125 + 0 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.092300891876220703125 + sunlight_color + + 1.230000019073486328125 + 1.230000019073486328125 + 1.230000019073486328125 + 0.4099999964237213134765625 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Evening.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Evening.xml new file mode 100644 index 000000000..c10d7dc87 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Evening.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.2999999821186065673828125 + 0.599999964237213134765625 + 0.89999997615814208984375 + 0.89999997615814208984375 + + blue_density + + 2 + 2 + 2 + 2 + + blue_horizon + + 0.1999999731779098510742188 + 0.1999999731779098510742188 + 0.1999999731779098510742188 + 0.0999999940395355224609375 + + cloud_color + + 0.5 + 0.5 + 0.5 + 0.5 + + cloud_pos_density1 + + 1 + 1 + 0.08999999612569808959960938 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 0 + 0.75 + 1 + + cloud_scale + + 0.4999999701976776123046875 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.19999980926513671875 + 10.0109996795654296875 + + cloud_shadow + + 1 + 0 + 0 + 1 + + density_multiplier + + 1.999999949475750327110291e-05 + 0 + 0 + 1 + + distance_multiplier + + 100 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 0.66999995708465576171875 + 0 + 0 + 1 + + glow + + 10 + 0.001000000047497451305389404 + -0.4999999701976776123046875 + 1 + + haze_density + + 1 + 0 + 0 + 1 + + haze_horizon + + 1 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 0 + -1 + 0 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 3.1415927410125732421875 + sunlight_color + + 0 + 0 + 0 + 0 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Night.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Night.xml new file mode 100644 index 000000000..2958cad7e --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Indoors%20Night.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.599999964237213134765625 + 0.75 + 1.19999992847442626953125 + 0.39999997615814208984375 + + blue_density + + 1.99999988079071044921875 + 1.99999988079071044921875 + 1.99999988079071044921875 + 1 + + blue_horizon + + 0 + 0 + 0 + 0 + + cloud_color + + 0.5 + 0.5 + 0.5 + 0.5 + + cloud_pos_density1 + + 1 + 1 + 0.08999999612569808959960938 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 0 + 0.75 + 1 + + cloud_scale + + 0.4999999701976776123046875 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.19999980926513671875 + 10.0109996795654296875 + + cloud_shadow + + 0 + 0 + 0 + 1 + + density_multiplier + + 0.0002500000118743628263473511 + 0 + 0 + 1 + + distance_multiplier + + 100 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 0.66999995708465576171875 + 0 + 0 + 1 + + glow + + 10 + 0.001000000047497451305389404 + -0.4999999701976776123046875 + 1 + + haze_density + + 1 + 0 + 0 + 1 + + haze_horizon + + 0 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 0 + -1 + 0 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 3.1415927410125732421875 + sunlight_color + + 0 + 0 + 0 + 0 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20Bright.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20Bright.xml new file mode 100644 index 000000000..7b4cebdb9 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20Bright.xml @@ -0,0 +1,141 @@ + + + ambient + + 0 + 0.1499999910593032836914063 + 0.4500000178813934326171875 + 0.1499999910593032836914063 + + blue_density + + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.199999988079071044921875 + + blue_horizon + + 0.4000000059604644775390625 + 0.599999725818634033203125 + 0.800000011920928955078125 + 0.39999997615814208984375 + + cloud_color + + 0.329999983310699462890625 + 0.329999983310699462890625 + 0.329999983310699462890625 + 0.329999983310699462890625 + + cloud_pos_density1 + + 0.5 + 0.5 + 0.0999999940395355224609375 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0 + 1 + + cloud_scale + + 0.07000000029802322387695313 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.1399993896484375 + 10.0109996795654296875 + + cloud_shadow + + 0.39999997615814208984375 + 0 + 0 + 1 + + density_multiplier + + 4.999999873689375817775726e-05 + 0 + 0 + 1 + + distance_multiplier + + 10 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 2 + 0 + 0 + 1 + + glow + + 20 + 0.001000000047497451305389404 + -1.25 + 1 + + haze_density + + 2 + 0 + 0 + 1 + + haze_horizon + + 0.04999999701976776123046875 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + -0 + 0.3090172111988067626953125 + 0.951056420803070068359375 + 1 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 10 + star_brightness + 0.5 + sun_angle + 3.4557521343231201171875 + sunlight_color + + 0.08999999612569808959960938 + 0.1799999922513961791992188 + 0.2999999821186065673828125 + 0.0999999940395355224609375 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20City%20Bright.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20City%20Bright.xml new file mode 100644 index 000000000..19951bad6 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20City%20Bright.xml @@ -0,0 +1,141 @@ + + + ambient + + 0 + 0.1499999910593032836914063 + 0.449999988079071044921875 + 0.449999988079071044921875 + + blue_density + + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.199999988079071044921875 + + blue_horizon + + 0.7999999523162841796875 + 0.599999964237213134765625 + 0.39999997615814208984375 + 0.39999997615814208984375 + + cloud_color + + 0.699999988079071044921875 + 0.39999997615814208984375 + 0.2999999821186065673828125 + 0.699999988079071044921875 + + cloud_pos_density1 + + 0.5 + 0.5 + 0.04999999701976776123046875 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0 + 1 + + cloud_scale + + 0.07000000029802322387695313 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.1399993896484375 + 10.0109996795654296875 + + cloud_shadow + + 0.5 + 0 + 0 + 1 + + density_multiplier + + 4.999999873689375817775726e-05 + 0 + 0 + 1 + + distance_multiplier + + 10 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 2 + 0 + 0 + 1 + + glow + + 20 + 0.001000000047497451305389404 + -1.25 + 1 + + haze_density + + 2 + 0 + 0 + 1 + + haze_horizon + + 0.04999999701976776123046875 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + -0 + 0.3090172111988067626953125 + 0.951056420803070068359375 + 1 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 10 + star_brightness + 0.5 + sun_angle + 3.4557521343231201171875 + sunlight_color + + 0.1799999922513961791992188 + 0.1799999922513961791992188 + 0.1799999922513961791992188 + 0.1799999922513961791992188 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20City.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20City.xml new file mode 100644 index 000000000..8fa2e039f --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20City.xml @@ -0,0 +1,141 @@ + + + ambient + + 0 + 0.1499999910593032836914063 + 0.449999988079071044921875 + 0.449999988079071044921875 + + blue_density + + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.39999997615814208984375 + 0.199999988079071044921875 + + blue_horizon + + 0.7999999523162841796875 + 0.599999964237213134765625 + 0.39999997615814208984375 + 0.39999997615814208984375 + + cloud_color + + 0.699999988079071044921875 + 0.39999997615814208984375 + 0.2999999821186065673828125 + 0.699999988079071044921875 + + cloud_pos_density1 + + 0.5 + 0.5 + 0.04999999701976776123046875 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0 + 1 + + cloud_scale + + 0.07000000029802322387695313 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.1399993896484375 + 10.0109996795654296875 + + cloud_shadow + + 0.5 + 0 + 0 + 1 + + density_multiplier + + 4.999999873689375817775726e-05 + 0 + 0 + 1 + + distance_multiplier + + 10 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 20 + 0.001000000047497451305389404 + -1.25 + 1 + + haze_density + + 2 + 0 + 0 + 1 + + haze_horizon + + 0.04999999701976776123046875 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + -0 + 0.3090172111988067626953125 + 0.951056420803070068359375 + 1 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 10 + star_brightness + 0.5 + sun_angle + 3.4557521343231201171875 + sunlight_color + + 0.1799999922513961791992188 + 0.1799999922513961791992188 + 0.1799999922513961791992188 + 0.1799999922513961791992188 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20Rainy.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20Rainy.xml new file mode 100644 index 000000000..522c66109 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night%20Rainy.xml @@ -0,0 +1,141 @@ + + + ambient + + 0 + 0 + 0 + 0 + + blue_density + + 2 + 2 + 2 + 1 + + blue_horizon + + 0.1999999731779098510742188 + 0.1999999731779098510742188 + 0.1999999731779098510742188 + 0.0999999940395355224609375 + + cloud_color + + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + + cloud_pos_density1 + + 1 + 1 + 0.2999999821186065673828125 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 1 + 0 + 1 + + cloud_scale + + 0.07000000029802322387695313 + 0 + 0 + 1 + + cloud_scroll_rate + + 11 + 10 + + cloud_shadow + + 0.66999995708465576171875 + 0 + 0 + 1 + + density_multiplier + + 0.0008999999845400452613830566 + 0 + 0 + 1 + + distance_multiplier + + 32.200000762939453125 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 0.39999997615814208984375 + 0 + 0 + 1 + + glow + + 0.200002193450927734375 + 0.001000000047497451305389404 + -0.2499999850988388061523438 + 1 + + haze_density + + 0 + 0 + 0 + 1 + + haze_horizon + + 0.0999999940395355224609375 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 0.876306593418121337890625 + -0.4817537963390350341796875 + 0 + + max_y + + 314 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.0734512805938720703125 + sunlight_color + + 0.08999998867511749267578125 + 0.1799999773502349853515625 + 0.269999980926513671875 + 0.08999999612569808959960938 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night.xml index 3b4c08934..a1be61d2f 100644 --- a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Night.xml @@ -2,10 +2,10 @@ ambient - 0.2999999821186065673828125 - 0.449999988079071044921875 - 0.599999964237213134765625 - 0.199999988079071044921875 + 0 + 0.1499999910593032836914063 + 0.4500000178813934326171875 + 0.1499999910593032836914063 blue_density @@ -16,23 +16,23 @@ blue_horizon - 0.199999988079071044921875 - 0.2999999821186065673828125 + 0.4000000059604644775390625 + 0.599999725818634033203125 + 0.800000011920928955078125 0.39999997615814208984375 - 0.199999988079071044921875 cloud_color - 0.09411763399839401245117188 - 0.1499999910593032836914063 - 0.199999988079071044921875 - 0.199999988079071044921875 + 0.329999983310699462890625 + 0.329999983310699462890625 + 0.329999983310699462890625 + 0.329999983310699462890625 cloud_pos_density1 0.5 0.5 - 0.07000000029802322387695313 + 0.0999999940395355224609375 1 cloud_pos_density2 @@ -56,21 +56,21 @@ cloud_shadow - 0.5 + 0.39999997615814208984375 0 0 1 density_multiplier - 0.0001999999949475750327110291 + 4.999999873689375817775726e-05 0 0 1 distance_multiplier - 8 + 10 0 0 1 @@ -84,21 +84,21 @@ gamma - 0.699999988079071044921875 + 1 0 0 1 glow - 11.6000003814697265625 + 20 0.001000000047497451305389404 - -0.749999940395355224609375 + -1.25 1 haze_density - 0.5 + 2 0 0 1 @@ -119,7 +119,7 @@ max_y - 500 + 4000 0 0 1 @@ -127,15 +127,15 @@ preset_num 10 star_brightness - 0.3599999845027923583984375 + 0.5 sun_angle 3.4557521343231201171875 sunlight_color - 0.1499999910593032836914063 - 0.1499999910593032836914063 - 0.1499999910593032836914063 - 0.04999999701976776123046875 + 0.08999999612569808959960938 + 0.1799999922513961791992188 + 0.2999999821186065673828125 + 0.0999999940395355224609375 diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Studio%201.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Studio%201.xml new file mode 100644 index 000000000..b299d5c02 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Studio%201.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.599999964237213134765625 + 0.599999964237213134765625 + 0.599999964237213134765625 + 0.199999988079071044921875 + + blue_density + + 2 + 2 + 2 + 1 + + blue_horizon + + 0.599999964237213134765625 + 0.599999964237213134765625 + 0.599999964237213134765625 + 0.2999999821186065673828125 + + cloud_color + + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + + cloud_pos_density1 + + 1 + 1 + 0.25 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 1 + 0 + 1 + + cloud_scale + + 0.07000000029802322387695313 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.3999996185302734375 + 10 + + cloud_shadow + + 0.61000001430511474609375 + 0 + 0 + 1 + + density_multiplier + + 9.999999747378751635551453e-05 + 0 + 0 + 1 + + distance_multiplier + + 5 + 0 + 0 + 1 + + east_angle + 1.06814157962799072265625 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 0.200002193450927734375 + 0.001000000047497451305389404 + -0.199999988079071044921875 + 1 + + haze_density + + 4 + 0 + 0 + 1 + + haze_horizon + + 0.2099999934434890747070313 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0.38304769992828369140625 + 0.89940512180328369140625 + -0.2105822265148162841796875 + 0 + + max_y + + 0 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.0231859683990478515625 + sunlight_color + + 1.5 + 1.5 + 1.5 + 0.5 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Studio%20Spotlight.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Studio%20Spotlight.xml new file mode 100644 index 000000000..7e919afff --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Studio%20Spotlight.xml @@ -0,0 +1,141 @@ + + + ambient + + 0 + 0 + 0 + 0 + + blue_density + + 0 + 0 + 0 + 0 + + blue_horizon + + 0 + 0 + 0 + 0 + + cloud_color + + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + 0.2999999821186065673828125 + + cloud_pos_density1 + + 1 + 1 + 0.25 + 1 + + cloud_pos_density2 + + 1.6884100437164306640625 + 1 + 0 + 1 + + cloud_scale + + 0.07000000029802322387695313 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.3999996185302734375 + 10 + + cloud_shadow + + 0 + 0 + 0 + 1 + + density_multiplier + + 0 + 0 + 0 + 1 + + distance_multiplier + + 100 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 0.200002193450927734375 + 0.001000000047497451305389404 + -0.199999988079071044921875 + 1 + + haze_density + + 1 + 0 + 0 + 1 + + haze_horizon + + 0.2099999934434890747070313 + 0.1991560012102127075195313 + 0.1991560012102127075195313 + 1 + + lightnorm + + 0 + 0.587785184383392333984375 + -0.80901706218719482421875 + 0 + + max_y + + 0 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.51327419281005859375 + sunlight_color + + 2.009999752044677734375 + 2.009999752044677734375 + 2.009999752044677734375 + 0.66999995708465576171875 + + + diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Sunrise.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Sunrise.xml index 5276d856e..114564084 100644 --- a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Sunrise.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Sunrise.xml @@ -2,10 +2,10 @@ ambient + 0 0.449999988079071044921875 - 0.75 - 1.0499999523162841796875 - 0.3499999940395355224609375 + 0.89999997615814208984375 + 0.2999999821186065673828125 blue_density @@ -23,16 +23,16 @@ cloud_color - 0.199999988079071044921875 - 0.199999988079071044921875 - 0.199999988079071044921875 - 0.199999988079071044921875 + 0.25 + 0.25 + 0.25 + 0.25 cloud_pos_density1 1 1 - 0.25 + 0.189999997615814208984375 1 cloud_pos_density2 @@ -44,7 +44,7 @@ cloud_scale - 0.0999999940395355224609375 + 0.07000000029802322387695313 0 0 1 @@ -91,21 +91,21 @@ glow - 4.00000095367431640625 + 38 0.001000000047497451305389404 - -0.39999997615814208984375 + -0.2499999850988388061523438 1 haze_density - 0.75 + 0.199999988079071044921875 0 0 1 haze_horizon - 0.199999988079071044921875 + 0.5 0.1991560012102127075195313 0.1991560012102127075195313 1 @@ -132,10 +132,10 @@ 0.31415927410125732421875 sunlight_color - 3 + 2.9999997615814208984375 1.19999992847442626953125 - 0.2999999821186065673828125 - 3 + 0.449999988079071044921875 + 1 diff --git a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Sunset.xml b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Sunset.xml index 05556e43a..3fe84a505 100644 --- a/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Sunset.xml +++ b/indra/newview/app_settings/windlight/skies/%5BPyFX%5D%20Sunset.xml @@ -23,16 +23,16 @@ cloud_color - 0.09999998658895492553710938 - 0.09999998658895492553710938 - 0.09999998658895492553710938 - 0.0999999940395355224609375 + 0 + 0.02999999932944774627685547 + 0.05999999865889549255371094 + 0.05999999865889549255371094 cloud_pos_density1 1 1 - 0.1499999910593032836914063 + 0.1199999973177909851074219 1 cloud_pos_density2 @@ -44,7 +44,7 @@ cloud_scale - 0.0999999940395355224609375 + 0.07000000029802322387695313 0 0 1 diff --git a/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ice.xml b/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ice.xml new file mode 100644 index 000000000..a5cdf76ae --- /dev/null +++ b/indra/newview/app_settings/windlight/water/%5BPyFX%5D%20Ice.xml @@ -0,0 +1,43 @@ + + + blurMultiplier + 0.1600000113248825073242188 + fresnelOffset + 0.5 + fresnelScale + 1 + normScale + + 5 + 5 + 5 + + normalMap + f92da839-f302-c813-668d-601ab1957af1 + scaleAbove + 0.199999988079071044921875 + scaleBelow + 0.2000000029802322387695313 + underWaterFogMod + 1 + waterFogColor + + 0.02851562201976776123046875 + 0.07148437201976776123046875 + 0.06242064759135246276855469 + 1 + + waterFogDensity + 2 + wave1Dir + + 0 + 0 + + wave2Dir + + 0 + 0 + + + diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Default%20%2B%20Texture.xml b/indra/newview/app_settings/windlight/water/Py%27s%20Default%20%2B%20Texture.xml index 1129333af..b945a41d8 100644 --- a/indra/newview/app_settings/windlight/water/Py%27s%20Default%20%2B%20Texture.xml +++ b/indra/newview/app_settings/windlight/water/Py%27s%20Default%20%2B%20Texture.xml @@ -1,16 +1,16 @@ blurMultiplier - 0.08000000566244125366210938 + 0.01000000070780515670776367 fresnelOffset - 0.550000011920928955078125 + 0.5 fresnelScale - 0.75 + 0.5 normScale - 5 - 5 - 5 + 4 + 4 + 1 normalMap 7f132b94-6c9c-5af3-5a17-3412ad653da0 @@ -19,25 +19,25 @@ scaleBelow 0.2000000029802322387695313 underWaterFogMod - 1 + 0.66999995708465576171875 waterFogColor - 0.02851562201976776123046875 - 0.07148437201976776123046875 - 0.06242064759135246276855469 + 0.1960784494876861572265625 + 0.2196078598499298095703125 + 0.1764705926179885864257813 1 waterFogDensity - 16 + 103.96833038330078125 wave1Dir - -0.1000001430511474609375 - -0.1000001430511474609375 + -0.25 + 0 wave2Dir - -0.5 - -0.5 + -1 + 0 diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Default.xml b/indra/newview/app_settings/windlight/water/Py%27s%20Default.xml index 4b3fa5a4f..1c443156c 100644 --- a/indra/newview/app_settings/windlight/water/Py%27s%20Default.xml +++ b/indra/newview/app_settings/windlight/water/Py%27s%20Default.xml @@ -1,43 +1,43 @@ blurMultiplier - 0.010000000707805157 + 0.04500000178813934326171875 fresnelOffset 0.5 fresnelScale - 0.39999997615814209 + 0.5 normScale - 2 - 2 - 2 + 1.10000002384185791015625 + 1 + 1 normalMap 822ded49-9a6c-f61c-cb89-6df54f42cdf4 scaleAbove - 0.029999999329447746 + 0.02999999932944774627685547 scaleBelow - 0.099999994039535522 + 0.2000000029802322387695313 underWaterFogMod - 0.25 + 0.66999995708465576171875 waterFogColor - 0 - 0.039215687662363052 - 0.039215687662363052 + 0.199999988079071044921875 + 0.2199999988079071044921875 + 0.1799999773502349853515625 1 waterFogDensity - 32 + 103.96833038330078125 wave1Dir - 1.0499997138977051 - -0.42000007629394531 + -1 + 0 wave2Dir - 1.1099996566772461 - -1.1600000858306885 + -1 + 0 diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%20%2B%20Texture.xml b/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%20%2B%20Texture.xml new file mode 100644 index 000000000..edd3e9a97 --- /dev/null +++ b/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%20%2B%20Texture.xml @@ -0,0 +1,43 @@ + + + blurMultiplier + 0.05000000074505805969238281 + fresnelOffset + 0.5 + fresnelScale + 0.5 + normScale + + 5 + 5 + 5 + + normalMap + 7f132b94-6c9c-5af3-5a17-3412ad653da0 + scaleAbove + 0.04999999701976776123046875 + scaleBelow + 0.2000000029802322387695313 + underWaterFogMod + 1 + waterFogColor + + 0.194549560546875 + 0.602325439453125 + 0.50675296783447265625 + 1 + + waterFogDensity + 8 + wave1Dir + + -0.1000001430511474609375 + -0.1000001430511474609375 + + wave2Dir + + -0.5 + -0.5 + + + diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202%20%2B%20Texture.xml b/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202%20%2B%20Texture.xml new file mode 100644 index 000000000..5eaa00845 --- /dev/null +++ b/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202%20%2B%20Texture.xml @@ -0,0 +1,43 @@ + + + blurMultiplier + 0.005000000353902578353881836 + fresnelOffset + 0.599999964237213134765625 + fresnelScale + 0.2999999821186065673828125 + normScale + + 1 + 1 + 1 + + normalMap + 7f132b94-6c9c-5af3-5a17-3412ad653da0 + scaleAbove + 0.02999999932944774627685547 + scaleBelow + 0.2000000029802322387695313 + underWaterFogMod + 0.5 + waterFogColor + + 0.1882353127002716064453125 + 0.2509804069995880126953125 + 0.3137255012989044189453125 + 1 + + waterFogDensity + 32 + wave1Dir + + -0.5 + 0 + + wave2Dir + + -2 + 0 + + + diff --git a/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202.xml b/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202.xml new file mode 100644 index 000000000..6866eacad --- /dev/null +++ b/indra/newview/app_settings/windlight/water/Py%27s%20Ocean%202.xml @@ -0,0 +1,43 @@ + + + blurMultiplier + 0.04500000178813934326171875 + fresnelOffset + 0.599999964237213134765625 + fresnelScale + 0.2999999821186065673828125 + normScale + + 1 + 1 + 1 + + normalMap + 822ded49-9a6c-f61c-cb89-6df54f42cdf4 + scaleAbove + 0.02999999932944774627685547 + scaleBelow + 0.2000000029802322387695313 + underWaterFogMod + 0.5 + waterFogColor + + 0.1882353127002716064453125 + 0.2509804069995880126953125 + 0.3137255012989044189453125 + 1 + + waterFogDensity + 32 + wave1Dir + + -2 + 0 + + wave2Dir + + -4 + 0 + + + diff --git a/indra/newview/ascentprefssys.cpp b/indra/newview/ascentprefssys.cpp index a2c577d0d..5c8ddc9e9 100644 --- a/indra/newview/ascentprefssys.cpp +++ b/indra/newview/ascentprefssys.cpp @@ -77,7 +77,6 @@ LLPrefsAscentSys::LLPrefsAscentSys() getChild("AlchemyChatCommandHoverHeight")->setCommitCallback(lineEditorControl); //Security ---------------------------------------------------------------------------- - getChild("UISndRestart")->setCommitCallback(lineEditorControl); //Build ------------------------------------------------------------------------------- getChild("next_owner_copy")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); @@ -207,8 +206,7 @@ void LLPrefsAscentSys::refreshValues() mDisableClickSitOtherOwner = gSavedSettings.getBOOL("DisableClickSitOtherOwner"); mDisplayScriptJumps = gSavedSettings.getBOOL("AscentDisplayTotalScriptJumps"); mNumScriptDiff = gSavedSettings.getF32("Ascentnumscriptdiff"); - mRestartMinimized = gSavedSettings.getBOOL("LiruRegionRestartMinimized"); - mRestartSound = gSavedSettings.getString("UISndRestart"); + mReplaceLinks = gSavedSettings.getBOOL("SinguReplaceLinks"); mLandmark = gSavedPerAccountSettings.getString("EmergencyTeleportLandmark"); mLandmarkBackup = gSavedPerAccountSettings.getString("EmergencyTeleportLandmarkBackup"); @@ -266,8 +264,6 @@ void LLPrefsAscentSys::refresh() childSetValue("AlchemyChatCommandHoverHeight", mCmdLineHover); //Security ---------------------------------------------------------------------------- - getChildView("UISndRestart")->setValue(mRestartSound); - if (LLComboBox* combo = getChild("lookat_namesystem_combobox")) combo->setValue(mLookAtNames); @@ -356,8 +352,7 @@ void LLPrefsAscentSys::cancel() gSavedSettings.setBOOL("DisableClickSitOtherOwner", mDisableClickSitOtherOwner); gSavedSettings.setBOOL("AscentDisplayTotalScriptJumps", mDisplayScriptJumps); gSavedSettings.setF32("Ascentnumscriptdiff", mNumScriptDiff); - gSavedSettings.setBOOL("LiruRegionRestartMinimized", mRestartMinimized); - gSavedSettings.setString("UISndRestart", mRestartSound); + gSavedSettings.setBOOL("SinguReplaceLinks", mReplaceLinks); gSavedPerAccountSettings.setString("EmergencyTeleportLandmark", mLandmark); gSavedPerAccountSettings.setString("EmergencyTeleportLandmarkBackup", mLandmarkBackup); diff --git a/indra/newview/ascentprefssys.h b/indra/newview/ascentprefssys.h index ae7a07cab..187212b38 100644 --- a/indra/newview/ascentprefssys.h +++ b/indra/newview/ascentprefssys.h @@ -116,9 +116,8 @@ private: bool mDisableClickSit; bool mDisableClickSitOtherOwner; bool mDisplayScriptJumps; - bool mRestartMinimized; + bool mReplaceLinks; F32 mNumScriptDiff; - std::string mRestartSound; std::string mLandmark; std::string mLandmarkBackup; diff --git a/indra/newview/ascentprefsvan.cpp b/indra/newview/ascentprefsvan.cpp index 6a51b580f..0c6216647 100644 --- a/indra/newview/ascentprefsvan.cpp +++ b/indra/newview/ascentprefsvan.cpp @@ -62,6 +62,8 @@ LLPrefsAscentVan::LLPrefsAscentVan() getChild("tag_spoofing_combobox")->setCommitCallback(boost::bind(&LLPrefsAscentVan::onCommitClientTag, this, _1)); getChild("custom_tag_label_box")->setCommitCallback(boost::bind(&LLControlGroup::setString, boost::ref(gSavedSettings), "AscentCustomTagLabel", _2)); + commit_callback_t lineEditorControl(boost::bind(&LLControlGroup::setString, boost::ref(gSavedSettings), boost::bind(&LLUICtrl::getName, _1), _2)); + getChild("UISndRestart")->setCommitCallback(lineEditorControl); getChild("update_clientdefs")->setCommitCallback(boost::bind(LLPrefsAscentVan::onManualClientUpdate)); @@ -126,10 +128,13 @@ void LLPrefsAscentVan::refreshValues() mCustomizeAnim = gSavedSettings.getBOOL("LiruCustomizeAnim"); mAnnounceSnapshots = gSavedSettings.getBOOL("AnnounceSnapshots"); mAnnounceStreamMetadata = gSavedSettings.getBOOL("AnnounceStreamMetadata"); - mUnfocusedFloatersOpaque = gSavedSettings.getBOOL("FloaterUnfocusedBackgroundOpaque"); + mInactiveFloaterTransparency = gSavedSettings.getF32("InactiveFloaterTransparency"); + mActiveFloaterTransparency = gSavedSettings.getF32("ActiveFloaterTransparency"); mCompleteNameProfiles = gSavedSettings.getBOOL("SinguCompleteNameProfiles"); mScriptErrorsStealFocus = gSavedSettings.getBOOL("LiruScriptErrorsStealFocus"); mConnectToNeighbors = gSavedSettings.getBOOL("AlchemyConnectToNeighbors"); + mRestartMinimized = gSavedSettings.getBOOL("LiruRegionRestartMinimized"); + mRestartSound = gSavedSettings.getString("UISndRestart"); //Tags\Colors ---------------------------------------------------------------------------- mAscentBroadcastTag = gSavedSettings.getBOOL("AscentBroadcastTag"); @@ -169,6 +174,7 @@ void LLPrefsAscentVan::refreshValues() void LLPrefsAscentVan::refresh() { //Main ----------------------------------------------------------------------------------- + getChildView("UISndRestart")->setValue(mRestartSound); //Tags\Colors ---------------------------------------------------------------------------- LLComboBox* combo = getChild("tag_spoofing_combobox"); @@ -197,10 +203,13 @@ void LLPrefsAscentVan::cancel() gSavedSettings.setBOOL("LiruCustomizeAnim", mCustomizeAnim); gSavedSettings.setBOOL("AnnounceSnapshots", mAnnounceSnapshots); gSavedSettings.setBOOL("AnnounceStreamMetadata", mAnnounceStreamMetadata); - gSavedSettings.setBOOL("FloaterUnfocusedBackgroundOpaque", mUnfocusedFloatersOpaque); + gSavedSettings.setF32("InactiveFloaterTransparency", mInactiveFloaterTransparency); + gSavedSettings.setF32("ActiveFloaterTransparency", mActiveFloaterTransparency); gSavedSettings.setBOOL("SinguCompleteNameProfiles", mCompleteNameProfiles); gSavedSettings.setBOOL("LiruScriptErrorsStealFocus", mScriptErrorsStealFocus); gSavedSettings.setBOOL("AlchemyConnectToNeighbors", mConnectToNeighbors); + gSavedSettings.setBOOL("LiruRegionRestartMinimized", mRestartMinimized); + gSavedSettings.setString("UISndRestart", mRestartSound); //Tags\Colors ---------------------------------------------------------------------------- gSavedSettings.setBOOL("AscentBroadcastTag", mAscentBroadcastTag); diff --git a/indra/newview/ascentprefsvan.h b/indra/newview/ascentprefsvan.h index b45adf652..d8e27338e 100644 --- a/indra/newview/ascentprefsvan.h +++ b/indra/newview/ascentprefsvan.h @@ -62,10 +62,12 @@ private: bool mCustomizeAnim; bool mAnnounceSnapshots; bool mAnnounceStreamMetadata; - bool mUnfocusedFloatersOpaque; + F32 mInactiveFloaterTransparency, mActiveFloaterTransparency; bool mCompleteNameProfiles; bool mScriptErrorsStealFocus; bool mConnectToNeighbors; + bool mRestartMinimized; + std::string mRestartSound; //Tags\Colors bool mAscentBroadcastTag; std::string mReportClientUUID; diff --git a/indra/newview/chatbar_as_cmdline.cpp b/indra/newview/chatbar_as_cmdline.cpp index c03d8424c..2863cfe92 100644 --- a/indra/newview/chatbar_as_cmdline.cpp +++ b/indra/newview/chatbar_as_cmdline.cpp @@ -42,6 +42,7 @@ #include "llagentui.h" #include "llavataractions.h" #include "llnotificationsutil.h" +#include "llsdserialize.h" #include "llviewerregion.h" #include "llworld.h" #include "lleventtimer.h" @@ -242,11 +243,11 @@ bool cmd_line_chat(std::string data, EChatType type) static LLCachedControl enableChatCmd(gSavedSettings, "AscentCmdLine", true); if (enableChatCmd) { - data = utf8str_tolower(data); std::istringstream input(data); std::string cmd; if (!(input >> cmd)) return true; + cmd = utf8str_tolower(cmd); static LLCachedControl sDrawDistanceCommand(gSavedSettings, "AscentCmdLineDrawDistance"); static LLCachedControl sHeightCommand(gSavedSettings, "AscentCmdLineHeight"); @@ -266,6 +267,7 @@ bool cmd_line_chat(std::string data, EChatType type) static LLCachedControl sTP2Command(gSavedSettings, "AscentCmdLineTP2"); static LLCachedControl sAwayCommand(gSavedSettings, "SinguCmdLineAway"); static LLCachedControl sURLCommand(gSavedSettings, "SinguCmdLineURL"); + static LLCachedControl sSettingCommand(gSavedSettings, "SinguCmdLineSetting"); if (cmd == utf8str_tolower(sDrawDistanceCommand)) // dd { @@ -396,7 +398,7 @@ bool cmd_line_chat(std::string data, EChatType type) slurl = LLSLURL(slurl.getRegion(), LLVector3(fmod(agentPos.mdV[VX], (F64)REGION_WIDTH_METERS), fmod(agentPos.mdV[VY], (F64)REGION_WIDTH_METERS), agentPos.mdV[VZ])); } - LLUrlAction::teleportToLocation(LLWeb::escapeURL(std::string("secondlife:///app/teleport/")+slurl.getLocationString())); + LLUrlAction::teleportToLocation(LLWeb::escapeURL("secondlife:///app/teleport/"+slurl.getLocationString())); } return false; } @@ -535,7 +537,55 @@ bool cmd_line_chat(std::string data, EChatType type) LLAvatarActions::showProfile(id); return false; } - LLUrlAction::clickAction(sub); + LLUrlAction::clickAction(sub, true); + } + return false; + } + else if (cmd == utf8str_tolower(sSettingCommand)) + { + std::string control_name; + if (input >> control_name) + { + // Find out if this control even exists. + auto control = gSavedSettings.getControl(control_name); + if (!control) gSavedPerAccountSettings.getControl(control_name); + if (!control) return false; + + LLSD val; + // Toggle if we weren't given a value, + if (input.eof()) val = !control->get(); + else // otherwise use what we were given. + { + switch (control->type()) // Convert to the right type, we can't be a string forever. + { + case TYPE_U32: // Integer is Integer, sucks but whatever + case TYPE_S32: + { + LLSD::Integer i; + input >> i; + val = i; + break; + } + case TYPE_F32: + { + LLSD::Float f; + input >> f; + val = f; + break; + } + case TYPE_STRING: + { + LLSD::String str; + std::getline(input, str, '\0'); // Read the entire rest of the stream into str + LLStringUtil::trim(str); // There's no setting where a space would be necessary here. + val = str; + break; + } + default: // Just parse it like JSON (or accept one of the many boolean value options! + LLSDSerialize::fromNotation(val, input, LLSDSerialize::SIZE_UNLIMITED); + } + } + control->set(val); } return false; } @@ -586,10 +636,10 @@ LLUUID cmdline_partial_name2key(std::string partial_name) std::string av_name; LLStringUtil::toLower(partial_name); LLWorld::getInstance()->getAvatars(&avatars); - auto instance = (LLFloaterAvatarList::instanceExists() ? &LLFloaterAvatarList::instance() : nullptr); + auto radar = (LLFloaterAvatarList::instanceExists() ? LLFloaterAvatarList::getInstance() : nullptr); for(const auto& id : avatars) { - if (LLAvatarListEntry* entry = instance ? instance->getAvatarEntry(id) : nullptr) + if (LLAvatarListEntry* entry = radar ? radar->getAvatarEntry(id) : nullptr) av_name = entry->getName(); else if (gCacheName->getFullName(id, av_name)); else if (LLVOAvatar* avatarp = gObjectList.findAvatar(id)) diff --git a/indra/newview/floaterao.cpp b/indra/newview/floaterao.cpp index 0d584e6e5..2038aaa13 100644 --- a/indra/newview/floaterao.cpp +++ b/indra/newview/floaterao.cpp @@ -16,13 +16,8 @@ #include "llstartup.h" #include "llpreviewnotecard.h" #include "llviewertexteditor.h" -#include "llcheckboxctrl.h" #include "llcombobox.h" -#include "llspinctrl.h" -#include "chatbar_as_cmdline.h" -//#include "llfloaterchat.h" #include "llfirstuse.h" -#include "lltrans.h" #include "llinventory.h" #include "llinventoryfunctions.h" @@ -31,15 +26,24 @@ #include "roles_constants.h" #include "llviewerregion.h" -#include "llpanelobjectinventory.h" #include "llinventorybridge.h" -#include "llboost.h" #include // Uncomment and use instead if we ever add the chatbar as a command line - MC void cmdline_printchat(const std::string& message); +namespace +{ + bool sSwimming = false; + bool enable_swim() + { + static const LLCachedControl swim(gSavedSettings, "AOSwimEnabled", false); + return swim; + } + bool is_underwater() { return enable_swim() && gAgentAvatarp && gAgentAvatarp->mBelowWater; } +} + class AONotecardCallback : public LLInventoryCallback { public: @@ -52,28 +56,32 @@ public: { if (!mFileName.empty()) { - LLPreviewNotecard* nc; - nc = (LLPreviewNotecard*)LLPreview::find(inv_item); - if(nc) + LLPreviewNotecard* nc = (LLPreviewNotecard*)LLPreview::show(inv_item); + if (!nc) { - nc->open(); - LLTextEditor *text = nc->getEditor(); - if (text) + auto item = gInventory.getItem(inv_item); + open_notecard(item, "Note: " + item->getName(), LLUUID::null, false); + nc = (LLPreviewNotecard*)LLPreview::find(inv_item); + } + + if (nc) + { + if (LLTextEditor *text = nc->getEditor()) { text->clear(); text->makePristine(); std::ifstream file(mFileName.c_str()); - + std::string line; while (!file.eof()) - { + { getline(file, line); - line = line + "\n"; + line += '\n'; text->insertText(line); } file.close(); - + nc->saveIfNeeded(); } } @@ -84,7 +92,7 @@ private: std::string mFileName; }; -AOInvTimer* gAOInvTimer = NULL; +AOInvTimer* gAOInvTimer = nullptr; // ------------------------------------------------------- @@ -93,7 +101,7 @@ AOStandTimer* mAOStandTimer; AOStandTimer::AOStandTimer() : LLEventTimer( gSavedSettings.getF32("AOStandInterval") ) { - AOStandTimer::tick(); + tick(); } AOStandTimer::~AOStandTimer() { @@ -110,13 +118,13 @@ BOOL AOStandTimer::tick() LLFloaterAO::stand_iterator++; // LL_INFOS() << "tick" << LL_ENDL; LLFloaterAO::ChangeStand(); - return FALSE; + return false; // return LLFloaterAO::ChangeStand(); //timer is always active now .. } // ------------------------------------------------------- -AOInvTimer::AOInvTimer() : LLEventTimer( (F32)1.0 ) +AOInvTimer::AOInvTimer() : LLEventTimer( 1.0f ) { } AOInvTimer::~AOInvTimer() @@ -124,22 +132,21 @@ AOInvTimer::~AOInvTimer() } BOOL AOInvTimer::tick() { - if (!(gSavedSettings.getBOOL("AOEnabled"))) return TRUE; if(LLStartUp::getStartupState() >= STATE_INVENTORY_SEND) { if(LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) { // cmdline_printchat("Inventory fetched, loading AO."); - LLFloaterAO::init(); - return TRUE; + LLFloaterAO::getInstance(); // Initializes everything + return true; } } - return FALSE; + return !gSavedSettings.getBOOL("AOEnabled"); } // STUFF ------------------------------------------------------- -int LLFloaterAO::mAnimationState = 0; +AOState LLFloaterAO::mAnimationState = STATE_AGENT_IDLE; int LLFloaterAO::stand_iterator = 0; LLUUID LLFloaterAO::invfolderid = LLUUID::null; @@ -160,14 +167,19 @@ LLComboBox* mcomboBox_flyslows; LLComboBox* mcomboBox_flyups; LLComboBox* mcomboBox_flydowns; LLComboBox* mcomboBox_lands; +LLComboBox* mcomboBox_typings; +LLComboBox* mcomboBox_floats; +LLComboBox* mcomboBox_swims; +LLComboBox* mcomboBox_swimups; +LLComboBox* mcomboBox_swimdowns; LLComboBox* mcomboBox_standups; LLComboBox* mcomboBox_prejumps; struct struct_overrides { LLUUID orig_id; - LLUUID ao_id; - int state; + LLUUID ao_id = LLUUID::null; + AOState state; }; std::vector mAOOverrides; @@ -178,139 +190,101 @@ struct struct_stands }; std::vector mAOStands; -struct struct_tokens +LLFloaterAO::LLFloaterAO(const LLSD&) : LLFloater("floater_ao") { - std::string token; - int state; -}; -std::vector mAOTokens; + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_ao.xml", nullptr, false); + init(); + gSavedSettings.getControl("AOEnabled")->getSignal()->connect(boost::bind(&LLFloaterAO::run)); + gSavedSettings.getControl("AOEnabled")->getSignal()->connect(std::bind([](const LLSD& enabled) + { + // Toggle typing AO the moment we toggle AO + const bool typing = gAgent.getRenderState() & AGENT_STATE_TYPING; + gAgent.sendAnimationRequest(GetAnimIDFromState(STATE_AGENT_TYPING), enabled && typing ? ANIM_REQUEST_START : ANIM_REQUEST_STOP); + }, std::placeholders::_2)); + gSavedSettings.getControl("AOSitsEnabled")->getSignal()->connect(boost::bind(&LLFloaterAO::run)); + sSwimming = is_underwater(); + gSavedSettings.getControl("AOSwimEnabled")->getSignal()->connect(boost::bind(&LLFloaterAO::toggleSwim, boost::bind(is_underwater))); +} -LLFloaterAO* LLFloaterAO::sInstance = NULL; - -LLFloaterAO::LLFloaterAO() -:LLFloater(std::string("floater_ao")) +void LLFloaterAO::onOpen() { -// init(); - llassert_always(sInstance == NULL); - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_ao.xml"); - sInstance = this; + LLFirstUse::useAO(); } LLFloaterAO::~LLFloaterAO() { - sInstance=NULL; - mcomboBox_stands = 0; - mcomboBox_walks = 0; - mcomboBox_runs = 0; - mcomboBox_jumps = 0; - mcomboBox_sits = 0; - mcomboBox_gsits = 0; - mcomboBox_crouchs = 0; - mcomboBox_cwalks = 0; - mcomboBox_falls = 0; - mcomboBox_hovers = 0; - mcomboBox_flys = 0; - mcomboBox_flyslows = 0; - mcomboBox_flyups = 0; - mcomboBox_flydowns = 0; - mcomboBox_lands = 0; - mcomboBox_standups = 0; - mcomboBox_prejumps = 0; + mcomboBox_stands = nullptr; + mcomboBox_walks = nullptr; + mcomboBox_runs = nullptr; + mcomboBox_jumps = nullptr; + mcomboBox_sits = nullptr; + mcomboBox_gsits = nullptr; + mcomboBox_crouchs = nullptr; + mcomboBox_cwalks = nullptr; + mcomboBox_falls = nullptr; + mcomboBox_hovers = nullptr; + mcomboBox_flys = nullptr; + mcomboBox_flyslows = nullptr; + mcomboBox_flyups = nullptr; + mcomboBox_flydowns = nullptr; + mcomboBox_lands = nullptr; + mcomboBox_typings = nullptr; + mcomboBox_floats = nullptr; + mcomboBox_swims = nullptr; + mcomboBox_swimups = nullptr; + mcomboBox_swimdowns = nullptr; + mcomboBox_standups = nullptr; + mcomboBox_prejumps = nullptr; // LL_INFOS() << "floater destroyed" << LL_ENDL; } -void LLFloaterAO::show(void*) -{ - if (!sInstance) - { - sInstance = new LLFloaterAO(); - updateLayout(sInstance); - init(); - - sInstance->open(); - } - else - { - sInstance->close(); - } - LLFirstUse::useAO(); -} - -bool LLFloaterAO::getInstance() -{ - if (sInstance) - return true; - else - return false; -} - BOOL LLFloaterAO::postBuild() { - childSetAction("more_btn", onClickMore, this); - childSetAction("less_btn", onClickLess, this); + gSavedSettings.getControl("AOAdvanced")->getSignal()->connect(boost::bind(&LLFloaterAO::updateLayout, this, _2)); - childSetAction("reloadcard",onClickReloadCard,this); - childSetAction("opencard",onClickOpenCard,this); - childSetAction("newcard",onClickNewCard,this); - childSetAction("prevstand",onClickPrevStand,this); - childSetAction("nextstand",onClickNextStand,this); - getChild("AOEnabled")->setCommitCallback(boost::bind(&LLFloaterAO::onClickToggleAO)); - getChild("AOSitsEnabled")->setCommitCallback(boost::bind(&LLFloaterAO::onClickToggleSits)); - getChild("standtime")->setCommitCallback(boost::bind(&LLFloaterAO::onSpinnerCommit,_1)); - mcomboBox_stands = getChild("stands"); - mcomboBox_walks = getChild("walks"); - mcomboBox_runs = getChild("runs"); - mcomboBox_jumps = getChild("jumps"); - mcomboBox_sits = getChild("sits"); - mcomboBox_gsits = getChild("gsits"); - mcomboBox_crouchs = getChild("crouchs"); - mcomboBox_cwalks = getChild("cwalks"); - mcomboBox_falls = getChild("falls"); - mcomboBox_hovers = getChild("hovers"); - mcomboBox_flys = getChild("flys"); - mcomboBox_flyslows = getChild("flyslows"); - mcomboBox_flyups = getChild("flyups"); - mcomboBox_flydowns = getChild("flydowns"); - mcomboBox_lands = getChild("lands"); - mcomboBox_standups = getChild("standups"); - mcomboBox_prejumps = getChild("prejumps"); - getChild("stands")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("walks")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("runs")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("jumps")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("sits")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("gsits")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("crouchs")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("cwalks")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("falls")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("hovers")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("flys")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("flyslows")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("flyups")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("flydowns")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("lands")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("standups")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); - getChild("prejumps")->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + getChild("reloadcard")->setCommitCallback(boost::bind(&LLFloaterAO::onClickReloadCard)); + getChild("opencard")->setCommitCallback(boost::bind(&LLFloaterAO::onClickOpenCard)); + getChild("newcard")->setCommitCallback(boost::bind(&LLFloaterAO::onClickNewCard)); + getChild("prevstand")->setCommitCallback(boost::bind(&LLFloaterAO::onClickCycleStand, false)); + getChild("nextstand")->setCommitCallback(boost::bind(&LLFloaterAO::onClickCycleStand, true)); + getChild("standtime")->setCommitCallback(boost::bind(&LLFloaterAO::onSpinnerCommit)); - return TRUE; + (mcomboBox_stands = getChild("stands"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + (mcomboBox_walks = getChild("walks"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + (mcomboBox_runs = getChild("runs"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + (mcomboBox_jumps = getChild("jumps"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + (mcomboBox_sits = getChild("sits"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + (mcomboBox_gsits = getChild("gsits"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + (mcomboBox_crouchs = getChild("crouchs"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + (mcomboBox_cwalks = getChild("cwalks"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + (mcomboBox_falls = getChild("falls"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_hovers = getChild("hovers"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_flys = getChild("flys"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_flyslows = getChild("flyslows"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_flyups = getChild("flyups"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_flydowns = getChild("flydowns"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_lands = getChild("lands"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_typings = getChild("typings"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_floats = getChild("floats"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_swims = getChild("swims"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_swimups = getChild("swimups"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_swimdowns = getChild("swimdowns"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit, _1)); + (mcomboBox_standups = getChild("standups"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + (mcomboBox_prejumps = getChild("prejumps"))->setCommitCallback(boost::bind(&LLFloaterAO::onComboBoxCommit,_1)); + + updateLayout(gSavedSettings.getBOOL("AOAdvanced")); + + return true; } -void LLFloaterAO::onSpinnerCommit(LLUICtrl* ctrl) +void LLFloaterAO::onSpinnerCommit() { - LLSpinCtrl* spin = (LLSpinCtrl*) ctrl; - if(spin) - { - if (spin->getName() == "standtime") - { - if (mAOStandTimer) mAOStandTimer->reset(); - } - } + if (mAOStandTimer) mAOStandTimer->reset(); } void LLFloaterAO::onComboBoxCommit(LLUICtrl* ctrl) { - LLComboBox* box = (LLComboBox*)ctrl; - if(box) + if (LLComboBox* box = (LLComboBox*)ctrl) { if (box->getName() == "stands") { @@ -320,250 +294,81 @@ void LLFloaterAO::onComboBoxCommit(LLUICtrl* ctrl) } else { - int state = STATE_AGENT_IDLE; + AOState state = getStateFromCombo(box); std::string stranim = box->getValue().asString(); -// LL_INFOS() << "state " << (gAgentAvatarp->isSitting()) << " - " << getAnimationState() << LL_ENDL; - if (box->getName() == "walks") +// LL_INFOS() << "state " << state << " - " << getAnimationState() << LL_ENDL; + gAgent.sendAnimationRequest(GetAnimIDFromState(state), ANIM_REQUEST_STOP); + bool start_cond = getAnimationState() == state && gSavedSettings.getBOOL("AOEnabled"); + switch (state) { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_WALK), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultWalk",stranim); - state = STATE_AGENT_WALK; + case STATE_AGENT_SIT: + case STATE_AGENT_GROUNDSIT: + start_cond &= gAgentAvatarp && gAgentAvatarp->isSitting() && gSavedSettings.getBOOL("AOSitsEnabled"); + break; + default: break; } - else if (box->getName() == "runs") + LLUUID anim = getAssetIDByName(stranim); + if (start_cond) gAgent.sendAnimationRequest(anim, ANIM_REQUEST_START); + gSavedPerAccountSettings.setString(ctrl->getControlName(), stranim); + for (auto& ao : mAOOverrides) { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_RUN), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultRun",stranim); - state = STATE_AGENT_RUN; - } - else if (box->getName() == "jumps") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_JUMP), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultJump",stranim); - state = STATE_AGENT_JUMP; - } - else if (box->getName() == "sits") - { - if (gAgentAvatarp && (gSavedSettings.getBOOL("AOEnabled")) && (gSavedSettings.getBOOL("AOSitsEnabled"))) + if (state == ao.state) { - if ((gAgentAvatarp->isSitting()) && (getAnimationState() == STATE_AGENT_SIT)) - { -// LL_INFOS() << "sitting " << GetAnimID(ANIM_AGENT_SIT) << " " << getAssetIDByName(stranim) << LL_ENDL; - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_SIT), ANIM_REQUEST_STOP); - gAgent.sendAnimationRequest(getAssetIDByName(stranim), ANIM_REQUEST_START); - } - } - gSavedPerAccountSettings.setString("AODefaultSit",stranim); - state = STATE_AGENT_SIT; - } - else if (box->getName() == "gsits") - { -// LL_INFOS() << "gsitting " << GetAnimID(ANIM_AGENT_SIT_GROUND) << " " << getAssetIDByName(stranim) << LL_ENDL; - if (gAgentAvatarp) - { - if ((gAgentAvatarp->isSitting()) && (getAnimationState() == STATE_AGENT_GROUNDSIT)) - { -// LL_INFOS() << "gsitting " << GetAnimID(ANIM_AGENT_SIT_GROUND) << " " << getAssetIDByName(stranim) << LL_ENDL; - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_SIT_GROUND), ANIM_REQUEST_STOP); - gAgent.sendAnimationRequest(getAssetIDByName(stranim), ANIM_REQUEST_START); - } - } - gSavedPerAccountSettings.setString("AODefaultGroundSit",stranim); - state = STATE_AGENT_GROUNDSIT; - } - else if (box->getName() == "crouchs") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_CROUCH), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultCrouch",stranim); - state = STATE_AGENT_CROUCH; - } - else if (box->getName() == "cwalks") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_CROUCHWALK), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultCrouchWalk",stranim); - state = STATE_AGENT_CROUCHWALK; - } - else if (box->getName() == "falls") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_FALLDOWN), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultFall",stranim); - state = STATE_AGENT_FALLDOWN; - } - else if (box->getName() == "hovers") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_HOVER), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultHover",stranim); - state = STATE_AGENT_HOVER; - } - else if (box->getName() == "flys") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_FLY), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultFly",stranim); - state = STATE_AGENT_FLY; - } - else if (box->getName() == "flyslows") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_FLYSLOW), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultFlySlow",stranim); - state = STATE_AGENT_FLYSLOW; - } - else if (box->getName() == "flyups") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_HOVER_UP), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultFlyUp",stranim); - state = STATE_AGENT_HOVER_UP; - } - else if (box->getName() == "flydowns") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_HOVER_DOWN), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultFlyDown",stranim); - state = STATE_AGENT_HOVER_DOWN; - } - else if (box->getName() == "lands") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_LAND), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultLand",stranim); - state = STATE_AGENT_LAND; - } - else if (box->getName() == "standups") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_STAND), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultStandUp",stranim); - state = STATE_AGENT_STAND; - } - else if (box->getName() == "prejumps") - { - gAgent.sendAnimationRequest(GetAnimID(ANIM_AGENT_PRE_JUMP), ANIM_REQUEST_STOP); - gSavedPerAccountSettings.setString("AODefaultPreJump",stranim); - state = STATE_AGENT_PRE_JUMP; - } - for (std::vector::iterator iter = mAOOverrides.begin(); iter != mAOOverrides.end(); ++iter) - { - if (state == iter->state) - { - iter->ao_id = getAssetIDByName(stranim); + ao.ao_id = anim; + break; } } } } } -void LLFloaterAO::updateLayout(LLFloaterAO* floater) +void LLFloaterAO::updateLayout(bool advanced) { - if (floater) - { - BOOL advanced = gSavedSettings.getBOOL( "AOAdvanced"); - if (advanced) - { - floater->reshape(610,380); //view->getRect().getWidth(), view->getUIWinHeightLong()); - } - else - { - floater->reshape(200,380); //view->getRect().getWidth(), view->getUIWinHeightShort()); - } - - floater->childSetVisible("more_btn", !advanced); - floater->childSetVisible("less_btn", advanced); - - floater->childSetVisible("tabcontainer", advanced); - floater->childSetVisible("tabdefaultanims", advanced); - - floater->childSetVisible("textdefaultwalk", advanced); - floater->childSetVisible("textdefaultrun", advanced); - floater->childSetVisible("textdefaultjump", advanced); - floater->childSetVisible("textdefaultsit", advanced); - floater->childSetVisible("textdefaultgsit", advanced); - floater->childSetVisible("textdefaultcrouch", advanced); - floater->childSetVisible("textdefaultcrouchwalk", advanced); - floater->childSetVisible("textdefaultfall", advanced); - floater->childSetVisible("textdefaulthover", advanced); - floater->childSetVisible("textdefaultfly", advanced); - floater->childSetVisible("textdefaultflyslow", advanced); - floater->childSetVisible("textdefaultflyup", advanced); - floater->childSetVisible("textdefaultflydown", advanced); - floater->childSetVisible("textdefaultland", advanced); - floater->childSetVisible("textdefaultstandup", advanced); - floater->childSetVisible("textdefaultprejump", advanced); - - - floater->childSetVisible("walks", advanced); - floater->childSetVisible("runs", advanced); - floater->childSetVisible("jumps", advanced); - floater->childSetVisible("sits", advanced); - floater->childSetVisible("gsits", advanced); - floater->childSetVisible("crouchs", advanced); - floater->childSetVisible("falls", advanced); - floater->childSetVisible("hovers", advanced); - floater->childSetVisible("flys", advanced); - floater->childSetVisible("flyslows", advanced); - floater->childSetVisible("flyups", advanced); - floater->childSetVisible("flydowns", advanced); - floater->childSetVisible("lands", advanced); - floater->childSetVisible("standups", advanced); - floater->childSetVisible("prejumps", advanced); - } + reshape(advanced ? 800 : 200, getRect().getHeight()); + childSetVisible("tabcontainer", advanced); } void LLFloaterAO::init() { mAOStands.clear(); - mAOTokens.clear(); mAOOverrides.clear(); - struct_tokens tokenloader; - tokenloader.token = - tokenloader.token = "[ Sitting On Ground ]"; tokenloader.state = STATE_AGENT_GROUNDSIT; mAOTokens.push_back(tokenloader); // 0 - tokenloader.token = "[ Sitting ]"; tokenloader.state = STATE_AGENT_SIT; mAOTokens.push_back(tokenloader); // 1 - tokenloader.token = "[ Crouching ]"; tokenloader.state = STATE_AGENT_CROUCH; mAOTokens.push_back(tokenloader); // 3 - tokenloader.token = "[ Crouch Walking ]"; tokenloader.state = STATE_AGENT_CROUCHWALK; mAOTokens.push_back(tokenloader); // 4 - tokenloader.token = "[ Standing Up ]"; tokenloader.state = STATE_AGENT_STANDUP; mAOTokens.push_back(tokenloader); // 6 - tokenloader.token = "[ Falling ]"; tokenloader.state = STATE_AGENT_FALLDOWN; mAOTokens.push_back(tokenloader); // 7 - tokenloader.token = "[ Flying Down ]"; tokenloader.state = STATE_AGENT_HOVER_DOWN; mAOTokens.push_back(tokenloader); // 8 - tokenloader.token = "[ Flying Up ]"; tokenloader.state = STATE_AGENT_HOVER_UP; mAOTokens.push_back(tokenloader); // 9 - tokenloader.token = "[ Flying Slow ]"; tokenloader.state = STATE_AGENT_FLYSLOW; mAOTokens.push_back(tokenloader); // 10 - tokenloader.token = "[ Flying ]"; tokenloader.state = STATE_AGENT_FLY; mAOTokens.push_back(tokenloader); // 11 - tokenloader.token = "[ Hovering ]"; tokenloader.state = STATE_AGENT_HOVER; mAOTokens.push_back(tokenloader); // 12 - tokenloader.token = "[ Jumping ]"; tokenloader.state = STATE_AGENT_JUMP; mAOTokens.push_back(tokenloader); // 13 - tokenloader.token = "[ Pre Jumping ]"; tokenloader.state = STATE_AGENT_PRE_JUMP; mAOTokens.push_back(tokenloader); // 14 - tokenloader.token = "[ Running ]"; tokenloader.state = STATE_AGENT_RUN; mAOTokens.push_back(tokenloader); // 15 - tokenloader.token = "[ Turning Right ]"; tokenloader.state = STATE_AGENT_TURNRIGHT; mAOTokens.push_back(tokenloader); // 16 - tokenloader.token = "[ Turning Left ]"; tokenloader.state = STATE_AGENT_TURNLEFT; mAOTokens.push_back(tokenloader); // 17 - tokenloader.token = "[ Walking ]"; tokenloader.state = STATE_AGENT_WALK; mAOTokens.push_back(tokenloader); // 18 - tokenloader.token = "[ Landing ]"; tokenloader.state = STATE_AGENT_LAND; mAOTokens.push_back(tokenloader); // 19 - tokenloader.token = "[ Standing ]"; tokenloader.state = STATE_AGENT_STAND; mAOTokens.push_back(tokenloader); // 20 - tokenloader.token = "[ Swimming Down ]"; tokenloader.state = 999; mAOTokens.push_back(tokenloader); // 21 - tokenloader.token = "[ Swimming Up ]"; tokenloader.state = 999; mAOTokens.push_back(tokenloader); // 22 - tokenloader.token = "[ Swimming Forward ]"; tokenloader.state = 999; mAOTokens.push_back(tokenloader); // 23 - tokenloader.token = "[ Floating ]"; tokenloader.state = 999; mAOTokens.push_back(tokenloader); // 24 - struct_overrides overrideloader; - overrideloader.orig_id = ANIM_AGENT_WALK; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_WALK; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_RUN; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_RUN; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_PRE_JUMP; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_PRE_JUMP; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_JUMP; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_JUMP; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_TURNLEFT; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_TURNLEFT; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_TURNRIGHT; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_TURNRIGHT; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_WALK; overrideloader.state = STATE_AGENT_WALK; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_RUN; overrideloader.state = STATE_AGENT_RUN; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_PRE_JUMP; overrideloader.state = STATE_AGENT_PRE_JUMP; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_JUMP; overrideloader.state = STATE_AGENT_JUMP; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_TURNLEFT; overrideloader.state = STATE_AGENT_TURNLEFT; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_TURNRIGHT; overrideloader.state = STATE_AGENT_TURNRIGHT; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_SIT; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_SIT; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_SIT_FEMALE; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_SIT; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_SIT_GENERIC; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_SIT; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_SIT_GROUND; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_GROUNDSIT; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_SIT_GROUND_CONSTRAINED; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_GROUNDSIT; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_SIT; overrideloader.state = STATE_AGENT_SIT; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_SIT_FEMALE; overrideloader.state = STATE_AGENT_SIT; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_SIT_GENERIC; overrideloader.state = STATE_AGENT_SIT; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_SIT_GROUND; overrideloader.state = STATE_AGENT_GROUNDSIT; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_SIT_GROUND_CONSTRAINED; overrideloader.state = STATE_AGENT_GROUNDSIT; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_HOVER; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_HOVER; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_HOVER_DOWN; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_HOVER_DOWN; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_HOVER_UP; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_HOVER_UP; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_HOVER; overrideloader.state = STATE_AGENT_HOVER; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_HOVER_DOWN; overrideloader.state = STATE_AGENT_HOVER_DOWN; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_HOVER_UP; overrideloader.state = STATE_AGENT_HOVER_UP; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_CROUCH; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_CROUCH; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_CROUCHWALK; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_CROUCHWALK; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_CROUCH; overrideloader.state = STATE_AGENT_CROUCH; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_CROUCHWALK; overrideloader.state = STATE_AGENT_CROUCHWALK; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_FALLDOWN; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_FALLDOWN; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_STANDUP; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_STANDUP; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_LAND; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_LAND; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_FALLDOWN; overrideloader.state = STATE_AGENT_FALLDOWN; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_STANDUP; overrideloader.state = STATE_AGENT_STANDUP; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_LAND; overrideloader.state = STATE_AGENT_LAND; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_TYPE; overrideloader.state = STATE_AGENT_TYPING; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_FLY; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_FLY; mAOOverrides.push_back(overrideloader); - overrideloader.orig_id = ANIM_AGENT_FLYSLOW; overrideloader.ao_id = LLUUID::null; overrideloader.state = STATE_AGENT_FLYSLOW; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_FLY; overrideloader.state = STATE_AGENT_FLY; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_FLYSLOW; overrideloader.state = STATE_AGENT_FLYSLOW; mAOOverrides.push_back(overrideloader); - BOOL success = TRUE; + overrideloader.orig_id = ANIM_AGENT_HOVER; overrideloader.state = STATE_AGENT_FLOAT; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_HOVER_DOWN; overrideloader.state = STATE_AGENT_SWIM_DOWN; mAOOverrides.push_back(overrideloader); + overrideloader.orig_id = ANIM_AGENT_HOVER_UP; overrideloader.state = STATE_AGENT_SWIM_UP; mAOOverrides.push_back(overrideloader); + + overrideloader.orig_id = ANIM_AGENT_FLY; overrideloader.state = STATE_AGENT_SWIM; mAOOverrides.push_back(overrideloader); + + bool success = true; if(LLStartUp::getStartupState() >= STATE_INVENTORY_SEND) { @@ -572,7 +377,7 @@ void LLFloaterAO::init() LLUUID configncitem = (LLUUID)gSavedPerAccountSettings.getString("AOConfigNotecardID"); if (configncitem.notNull()) { - success = FALSE; + success = false; const LLInventoryItem* item = gInventory.getItem(configncitem); if(item) { @@ -593,8 +398,8 @@ void LLFloaterAO::init() item->getType(), &onNotecardLoadComplete, (void*)new_uuid, - TRUE); - success = TRUE; + true); + success = true; } } } @@ -607,41 +412,20 @@ void LLFloaterAO::init() cmdline_printchat("Could not read the specified Config Notecard"); } -// mAnimationState = 0; +// mAnimationState = STATE_AGENT_IDLE; // mCurrentStandId = LLUUID::null; // setAnimationState(STATE_AGENT_IDLE); - -} - -void LLFloaterAO::onClickMore(void* data) -{ - gSavedSettings.setBOOL( "AOAdvanced", TRUE ); - updateLayout(sInstance); -} -void LLFloaterAO::onClickLess(void* data) -{ - gSavedSettings.setBOOL( "AOAdvanced", FALSE ); - updateLayout(sInstance); -} - -void LLFloaterAO::onClickToggleAO() -{ - run(); -} - -void LLFloaterAO::onClickToggleSits() -{ - run(); } void LLFloaterAO::run() { setAnimationState(STATE_AGENT_IDLE); // reset state - int state = getAnimationState(); // check if sitting or hovering - if ((state == STATE_AGENT_IDLE) || (state == STATE_AGENT_STAND)) + AOState state = getAnimationState(); // check if sitting or hovering + bool enabled = gSavedSettings.getBOOL("AOEnabled"); + if (state == STATE_AGENT_IDLE || state == STATE_AGENT_STAND) { - if (gSavedSettings.getBOOL("AOEnabled")) + if (enabled) { if (mAOStandTimer) { @@ -655,18 +439,63 @@ void LLFloaterAO::run() } else { - stopMotion(getCurrentStandId(), FALSE, TRUE); //stop stand first then set state + stopMotion(getCurrentStandId(), true); //stop stand first then set state setAnimationState(STATE_AGENT_IDLE); } } else { - if (state == STATE_AGENT_SIT) gAgent.sendAnimationRequest(GetAnimIDFromState(state), (gSavedSettings.getBOOL("AOEnabled") && gSavedSettings.getBOOL("AOSitsEnabled")) ? ANIM_REQUEST_START : ANIM_REQUEST_STOP); - else gAgent.sendAnimationRequest(GetAnimIDFromState(state), gSavedSettings.getBOOL("AOEnabled") ? ANIM_REQUEST_START : ANIM_REQUEST_STOP); + bool sit = false; + switch (state) + { + case STATE_AGENT_SIT: + case STATE_AGENT_GROUNDSIT: + sit = true; + stopMotion(getCurrentStandId(), true); // Stop the current stand + break; + default: break; + } + gAgent.sendAnimationRequest(GetAnimIDFromState(state), (enabled && (!sit || gSavedSettings.getBOOL("AOSitsEnabled"))) ? ANIM_REQUEST_START : ANIM_REQUEST_STOP); } } -int LLFloaterAO::getAnimationState() +void LLFloaterAO::typing(bool start) +{ + uuid_vec_t anims; + // If we're stopping, stop regardless, just in case the setting was toggled during (e.g.: keyboard shortcut) + if (!start || gSavedSettings.getBOOL("PlayTypingAnim")) // Linden typing + anims.push_back(ANIM_AGENT_TYPE); + if (gSavedSettings.getBOOL("AOEnabled")) // Typing override + anims.push_back(GetAnimIDFromState(STATE_AGENT_TYPING)); + if (anims.empty()) return; + gAgent.sendAnimationRequests(anims, start ? ANIM_REQUEST_START : ANIM_REQUEST_STOP); +} + +AOState LLFloaterAO::flyToSwimState(const AOState& state) +{ + switch (state) + { + case STATE_AGENT_HOVER: return STATE_AGENT_FLOAT; + case STATE_AGENT_FLY: return STATE_AGENT_SWIM; + case STATE_AGENT_HOVER_UP: return STATE_AGENT_SWIM_UP; + case STATE_AGENT_HOVER_DOWN: return STATE_AGENT_SWIM_DOWN; + default: return state; + } +} + +AOState LLFloaterAO::swimToFlyState(const AOState& state) +{ + switch (state) + { + case STATE_AGENT_FLOAT: return STATE_AGENT_HOVER; + case STATE_AGENT_SWIM: return STATE_AGENT_FLY; + case STATE_AGENT_SWIM_UP: return STATE_AGENT_HOVER_UP; + case STATE_AGENT_SWIM_DOWN: return STATE_AGENT_HOVER_DOWN; + default: return state; + } +} + +AOState LLFloaterAO::getAnimationState() { if (gAgentAvatarp) { @@ -676,9 +505,9 @@ int LLFloaterAO::getAnimationState() return mAnimationState; } -void LLFloaterAO::setAnimationState(const int state) +void LLFloaterAO::setAnimationState(const AOState& state) { - mAnimationState = state; + mAnimationState = sSwimming ? flyToSwimState(state) : state; } LLUUID LLFloaterAO::getCurrentStandId() @@ -691,81 +520,113 @@ void LLFloaterAO::setCurrentStandId(const LLUUID& id) mCurrentStandId = id; } +bool LLFloaterAO::swimCheck(const AOState& state) +{ + switch (state) + { + case STATE_AGENT_HOVER: + case STATE_AGENT_FLY: + case STATE_AGENT_HOVER_UP: + case STATE_AGENT_HOVER_DOWN: + return !sSwimming; + case STATE_AGENT_FLOAT: + case STATE_AGENT_SWIM: + case STATE_AGENT_SWIM_UP: + case STATE_AGENT_SWIM_DOWN: + return sSwimming; + default: return true; + } +} + LLUUID LLFloaterAO::GetAnimID(const LLUUID& id) { - for (std::vector::iterator iter = mAOOverrides.begin(); iter != mAOOverrides.end(); ++iter) + for (const auto& ao : mAOOverrides) { - if (iter->orig_id == id) return iter->ao_id; + if (swimCheck(ao.state) && ao.orig_id == id) + { + return ao.ao_id; + } } return LLUUID::null; } -int LLFloaterAO::GetStateFromAnimID(const LLUUID& id) +AOState LLFloaterAO::GetStateFromAnimID(const LLUUID& id) { - for (std::vector::iterator iter = mAOOverrides.begin(); iter != mAOOverrides.end(); ++iter) + for (const auto& ao : mAOOverrides) { - if (iter->orig_id == id) return iter->state; + if (swimCheck(ao.state) && ao.orig_id == id) return ao.state; } return STATE_AGENT_IDLE; } -LLUUID LLFloaterAO::GetAnimIDFromState(const int state) +LLUUID LLFloaterAO::GetAnimIDFromState(const AOState& state) { - for (std::vector::iterator iter = mAOOverrides.begin(); iter != mAOOverrides.end(); ++iter) + for (const auto& ao : mAOOverrides) { - if (iter->state == state) return iter->ao_id; + if (ao.state == state) return ao.ao_id; } return LLUUID::null; } -int LLFloaterAO::GetStateFromToken(std::string strtoken) +AOState LLFloaterAO::GetStateFromToken(std::string strtoken) { - for (std::vector::iterator iter = mAOTokens.begin(); iter != mAOTokens.end(); ++iter) - { - if (iter->token == strtoken) return iter->state; - } + if (strtoken == "[ Sitting On Ground ]") return STATE_AGENT_GROUNDSIT; + if (strtoken == "[ Sitting ]") return STATE_AGENT_SIT; + if (strtoken == "[ Crouching ]") return STATE_AGENT_CROUCH; + if (strtoken == "[ Crouch Walking ]") return STATE_AGENT_CROUCHWALK; + if (strtoken == "[ Standing Up ]") return STATE_AGENT_STANDUP; + if (strtoken == "[ Falling ]") return STATE_AGENT_FALLDOWN; + if (strtoken == "[ Flying Down ]") return STATE_AGENT_HOVER_DOWN; + if (strtoken == "[ Flying Up ]") return STATE_AGENT_HOVER_UP; + if (strtoken == "[ Flying Slow ]") return STATE_AGENT_FLYSLOW; + if (strtoken == "[ Flying ]") return STATE_AGENT_FLY; + if (strtoken == "[ Hovering ]") return STATE_AGENT_HOVER; + if (strtoken == "[ Jumping ]") return STATE_AGENT_JUMP; + if (strtoken == "[ Pre Jumping ]") return STATE_AGENT_PRE_JUMP; + if (strtoken == "[ Running ]") return STATE_AGENT_RUN; + if (strtoken == "[ Turning Right ]") return STATE_AGENT_TURNRIGHT; + if (strtoken == "[ Turning Left ]") return STATE_AGENT_TURNLEFT; + if (strtoken == "[ Walking ]") return STATE_AGENT_WALK; + if (strtoken == "[ Landing ]") return STATE_AGENT_LAND; + if (strtoken == "[ Standing ]") return STATE_AGENT_STAND; + if (strtoken == "[ Swimming Down ]") return STATE_AGENT_SWIM_DOWN; + if (strtoken == "[ Swimming Up ]") return STATE_AGENT_SWIM_UP; + if (strtoken == "[ Swimming Forward ]") return STATE_AGENT_SWIM; + if (strtoken == "[ Floating ]") return STATE_AGENT_FLOAT; + if (strtoken == "[ Typing ]") return STATE_AGENT_TYPING; return STATE_AGENT_IDLE; } -void LLFloaterAO::onClickPrevStand(void* user_data) +void LLFloaterAO::onClickCycleStand(bool next) { - if (!(mAOStands.size() > 0)) return; - stand_iterator=stand_iterator-1; + if (mAOStands.empty()) return; + stand_iterator += (next ? 1 : -1); if (stand_iterator < 0) stand_iterator = int( mAOStands.size()-stand_iterator); if (stand_iterator > int( mAOStands.size()-1)) stand_iterator = 0; cmdline_printchat(llformat("Changing stand to %s.",mAOStands[stand_iterator].anim_name.c_str())); ChangeStand(); } -void LLFloaterAO::onClickNextStand(void* user_data) -{ - if (!(mAOStands.size() > 0)) return; - stand_iterator=stand_iterator+1; - if (stand_iterator < 0) stand_iterator = int( mAOStands.size()-stand_iterator); - if (stand_iterator > int( mAOStands.size()-1)) stand_iterator = 0; - cmdline_printchat(llformat("Changing stand to %s.",mAOStands[stand_iterator].anim_name.c_str())); - ChangeStand(); -} - -BOOL LLFloaterAO::ChangeStand() +void LLFloaterAO::ChangeStand() { if (gSavedSettings.getBOOL("AOEnabled")) { if (gAgentAvatarp) { - if (gSavedSettings.getBOOL("AONoStandsInMouselook") && gAgentCamera.cameraMouselook()) return FALSE; + if (gSavedSettings.getBOOL("AONoStandsInMouselook") && gAgentCamera.cameraMouselook()) return; if (gAgentAvatarp->isSitting()) { -// stopMotion(getCurrentStandId(), FALSE, TRUE); //stop stand first then set state +// stopMotion(getCurrentStandId(), true); //stop stand first then set state // if (getAnimationState() != STATE_AGENT_GROUNDSIT) setAnimationState(STATE_AGENT_SIT); // setCurrentStandId(LLUUID::null); - return FALSE; + return; } } - if ((getAnimationState() == STATE_AGENT_IDLE) || (getAnimationState() == STATE_AGENT_STAND))// stands have lowest priority + AOState state = getAnimationState(); + if (state == STATE_AGENT_IDLE || state == STATE_AGENT_STAND)// stands have lowest priority { - if (!(mAOStands.size() > 0)) return TRUE; + if (mAOStands.empty()) return; if (gSavedSettings.getBOOL("AOStandRandomize")) { stand_iterator = ll_rand(mAOStands.size()-1); @@ -779,113 +640,107 @@ BOOL LLFloaterAO::ChangeStand() if (mAOStands[stand_iterator].ao_id.notNull()) { - stopMotion(getCurrentStandId(), FALSE, TRUE); //stop stand first then set state - startMotion(mAOStands[stand_iterator].ao_id, 0, TRUE); + stopMotion(getCurrentStandId(), true); //stop stand first then set state + startMotion(mAOStands[stand_iterator].ao_id, true); setAnimationState(STATE_AGENT_STAND); setCurrentStandId(mAOStands[stand_iterator].ao_id); - if ((sInstance)&&(mcomboBox_stands)) mcomboBox_stands->selectNthItem(stand_iterator); + if (mcomboBox_stands) mcomboBox_stands->selectNthItem(stand_iterator); // LL_INFOS() << "changing stand to " << mAOStands[stand_iterator].anim_name << LL_ENDL; - return FALSE; } } } - else - { - stopMotion(getCurrentStandId(), FALSE, TRUE); - return TRUE; //stop if ao is off - } - return TRUE; + else stopMotion(getCurrentStandId(), true); } - -BOOL LLFloaterAO::startMotion(const LLUUID& id, F32 time_offset, BOOL stand) +void LLFloaterAO::toggleSwim(bool underwater) { + static const LLCachedControl enabled(gSavedSettings, "AOEnabled", false); + + sSwimming = underwater && enable_swim(); + + // Don't send requests if we have the AO disabled. + if (enabled) + { + AOState state = getAnimationState(); + if (state != STATE_AGENT_IDLE && state != STATE_AGENT_STAND) // Don't bother if we're just standing or idle (Who pushed us?!) + { + // Stop all of the previous states + constexpr std::array swim_states = { STATE_AGENT_FLOAT, STATE_AGENT_SWIM, STATE_AGENT_SWIM_UP, STATE_AGENT_SWIM_DOWN }; + constexpr std::array fly_states = { STATE_AGENT_HOVER, STATE_AGENT_FLY, STATE_AGENT_HOVER_UP, STATE_AGENT_HOVER_DOWN }; + uuid_vec_t vec; + for (const auto& state : sSwimming ? fly_states : swim_states) + vec.push_back(GetAnimIDFromState(state)); + gAgent.sendAnimationRequests(vec, ANIM_REQUEST_STOP); + // Process new animations + gAgentAvatarp->processAnimationStateChanges(); + } + } +} + +void LLFloaterAO::doMotion(const LLUUID& id, bool start, bool stand) +{ + if (id.isNull()) return; + LLUUID anim = id; if (stand) { - if (id.notNull()) + if (start) { - BOOL sitting = FALSE; - if (gAgentAvatarp) - { - sitting = gAgentAvatarp->isSitting(); - } - if (sitting) return FALSE; - gAgent.sendAnimationRequest(id, ANIM_REQUEST_START); - return TRUE; + if (gAgentAvatarp && gAgentAvatarp->isSitting()) + return; } + else setAnimationState(STATE_AGENT_IDLE); } else { - if (GetAnimID(id).notNull() && gSavedSettings.getBOOL("AOEnabled")) + anim = GetAnimID(id); + if (anim.isNull() || !gSavedSettings.getBOOL("AOEnabled")) return; + AOState state = GetStateFromAnimID(id); + if (start) { - stopMotion(getCurrentStandId(), FALSE, TRUE); //stop stand first then set state - setAnimationState(GetStateFromAnimID(id)); - -// LL_INFOS() << " state " << getAnimationState() << " start anim " << id << " overriding with " << GetAnimID(id) << LL_ENDL; - if ((GetStateFromAnimID(id) == STATE_AGENT_SIT) && !(gSavedSettings.getBOOL("AOSitsEnabled"))) return TRUE; - gAgent.sendAnimationRequest(GetAnimID(id), ANIM_REQUEST_START); - return TRUE; + stopMotion(getCurrentStandId(), true); //stop stand first then set state + setAnimationState(state); +// LL_INFOS() << " state " << getAnimationState() << " start anim " << id << " overriding with " << anim << LL_ENDL; + if (state == STATE_AGENT_SIT && !gSavedSettings.getBOOL("AOSitsEnabled")) return; + } + else + { +// LL_INFOS() << " state " << getAnimationState() << "/" << state << "(now 0) stop anim " << id << " overriding with " << anim << LL_ENDL; + if (getAnimationState() == state) setAnimationState(STATE_AGENT_IDLE); + ChangeStand(); // startMotion(getCurrentStandId(), true); } } - return FALSE; + gAgent.sendAnimationRequest(anim, start ? ANIM_REQUEST_START : ANIM_REQUEST_STOP); } -BOOL LLFloaterAO::stopMotion(const LLUUID& id, BOOL stop_immediate, BOOL stand) -{ - if (stand) - { - setAnimationState(STATE_AGENT_IDLE); - gAgent.sendAnimationRequest(id, ANIM_REQUEST_STOP); - return TRUE; - } - else - { - if (GetAnimID(id).notNull() && gSavedSettings.getBOOL("AOEnabled")) - { -// LL_INFOS() << " state " << getAnimationState() << "/" << GetStateFromAnimID(id) << "(now 0) stop anim " << id << " overriding with " << GetAnimID(id) << LL_ENDL; - if (getAnimationState() == GetStateFromAnimID(id)) - { - setAnimationState(STATE_AGENT_IDLE); - } - ChangeStand(); // startMotion(getCurrentStandId(), 0, TRUE); - gAgent.sendAnimationRequest(GetAnimID(id), ANIM_REQUEST_STOP); - return TRUE; - } - } - return FALSE; -} - -void LLFloaterAO::onClickReloadCard(void* user_data) +void LLFloaterAO::onClickReloadCard() { - if(LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) + if (LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) { - LLFloaterAO::init(); + init(); } } -void LLFloaterAO::onClickOpenCard(void* user_data) +void LLFloaterAO::onClickOpenCard() { - if(LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) + if (LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) { LLUUID configncitem = (LLUUID)gSavedPerAccountSettings.getString("AOConfigNotecardID"); if (configncitem.notNull()) { - const LLInventoryItem* item = gInventory.getItem(configncitem); - if(item) + if (LLViewerInventoryItem* item = gInventory.getItem(configncitem)) { if (gAgent.allowOperation(PERM_COPY, item->getPermissions(),GP_OBJECT_MANIPULATE) || gAgent.isGodlike()) { if(!item->getAssetUUID().isNull()) - open_notecard((LLViewerInventoryItem*)item, std::string("Note: ") + item->getName(), LLUUID::null, FALSE); - // open_notecard((LLViewerInventoryItem*)item, std::string("Note: ") + item->getName(), LLUUID::null, FALSE, LLUUID::null, FALSE); + open_notecard(item, "Note: " + item->getName(), LLUUID::null, false); } } } } } -void LLFloaterAO::onClickNewCard(void* user_data) +void LLFloaterAO::onClickNewCard() { // load the template file from app_settings/ao_template.ini then // create a new properly-formatted notecard in the user's inventory @@ -893,7 +748,7 @@ void LLFloaterAO::onClickNewCard(void* user_data) if (!ao_template.empty()) { LLPointer cb = new AONotecardCallback(ao_template); - create_inventory_item(gAgent.getID(), gAgent.getSessionID(), + create_inventory_item(gAgentID, gAgentSessionID, LLUUID::null, LLTransactionID::tnull, "New AO Notecard", "Drop this notecard in your AO window to use", LLAssetType::AT_NOTECARD, LLInventoryType::IT_NOTECARD, NOT_WEARABLE, PERM_ALL, cb); @@ -906,10 +761,66 @@ void LLFloaterAO::onClickNewCard(void* user_data) struct AOAssetInfo { - std::string path; - std::string name; + std::string path, name; }; +AOState LLFloaterAO::getStateFromCombo(const LLComboBox* combo) +{ + if (combo == mcomboBox_stands) return STATE_AGENT_STAND; + if (combo == mcomboBox_walks) return STATE_AGENT_WALK; + if (combo == mcomboBox_runs) return STATE_AGENT_RUN; + if (combo == mcomboBox_jumps) return STATE_AGENT_JUMP; + if (combo == mcomboBox_sits) return STATE_AGENT_SIT; + if (combo == mcomboBox_gsits) return STATE_AGENT_GROUNDSIT; + if (combo == mcomboBox_crouchs) return STATE_AGENT_CROUCH; + if (combo == mcomboBox_cwalks) return STATE_AGENT_CROUCHWALK; + if (combo == mcomboBox_falls) return STATE_AGENT_FALLDOWN; + if (combo == mcomboBox_hovers) return STATE_AGENT_HOVER; + if (combo == mcomboBox_flys) return STATE_AGENT_FLY; + if (combo == mcomboBox_flyslows) return STATE_AGENT_FLYSLOW; + if (combo == mcomboBox_flyups) return STATE_AGENT_HOVER_UP; + if (combo == mcomboBox_flydowns) return STATE_AGENT_HOVER_DOWN; + if (combo == mcomboBox_lands) return STATE_AGENT_LAND; + if (combo == mcomboBox_typings) return STATE_AGENT_TYPING; + if (combo == mcomboBox_floats) return STATE_AGENT_FLOAT; + if (combo == mcomboBox_swims) return STATE_AGENT_SWIM; + if (combo == mcomboBox_swimups) return STATE_AGENT_SWIM_UP; + if (combo == mcomboBox_swimdowns) return STATE_AGENT_SWIM_DOWN; + if (combo == mcomboBox_standups) return STATE_AGENT_STANDUP; + if (combo == mcomboBox_prejumps) return STATE_AGENT_PRE_JUMP; + return STATE_AGENT_IDLE; +} + +LLComboBox* LLFloaterAO::getComboFromState(const AOState& state) +{ + switch (state) + { + case STATE_AGENT_STAND: return mcomboBox_stands; + case STATE_AGENT_WALK: return mcomboBox_walks; + case STATE_AGENT_RUN: return mcomboBox_runs; + case STATE_AGENT_JUMP: return mcomboBox_jumps; + case STATE_AGENT_SIT: return mcomboBox_sits; + case STATE_AGENT_GROUNDSIT: return mcomboBox_gsits; + case STATE_AGENT_CROUCH: return mcomboBox_crouchs; + case STATE_AGENT_CROUCHWALK: return mcomboBox_cwalks; + case STATE_AGENT_FALLDOWN: return mcomboBox_falls; + case STATE_AGENT_HOVER: return mcomboBox_hovers; + case STATE_AGENT_FLY: return mcomboBox_flys; + case STATE_AGENT_FLYSLOW: return mcomboBox_flyslows; + case STATE_AGENT_HOVER_UP: return mcomboBox_flyups; + case STATE_AGENT_HOVER_DOWN: return mcomboBox_flydowns; + case STATE_AGENT_LAND: return mcomboBox_lands; + case STATE_AGENT_TYPING: return mcomboBox_typings; + case STATE_AGENT_FLOAT: return mcomboBox_floats; + case STATE_AGENT_SWIM: return mcomboBox_swims; + case STATE_AGENT_SWIM_UP: return mcomboBox_swimups; + case STATE_AGENT_SWIM_DOWN: return mcomboBox_swimdowns; + case STATE_AGENT_STANDUP: return mcomboBox_standups; + case STATE_AGENT_PRE_JUMP: return mcomboBox_prejumps; + default: return nullptr; + } +} + void LLFloaterAO::onNotecardLoadComplete(LLVFS *vfs,const LLUUID& asset_uuid,LLAssetType::EType type,void* user_data, S32 status, LLExtStat ext_status) { if(status == LL_ERR_NOERR) @@ -946,187 +857,66 @@ void LLFloaterAO::onNotecardLoadComplete(LLVFS *vfs,const LLUUID& asset_uuid,LLA if (mcomboBox_flyups) mcomboBox_flyups->clear(); if (mcomboBox_flydowns) mcomboBox_flydowns->clear(); if (mcomboBox_lands) mcomboBox_lands->clear(); + if (mcomboBox_typings) mcomboBox_typings->clear(); + if (mcomboBox_floats) mcomboBox_floats->clear(); + if (mcomboBox_swims) mcomboBox_swims->clear(); + if (mcomboBox_swimups) mcomboBox_swimups->clear(); + if (mcomboBox_swimdowns) mcomboBox_swimdowns->clear(); if (mcomboBox_standups) mcomboBox_standups->clear(); if (mcomboBox_prejumps) mcomboBox_prejumps->clear(); - - struct_stands loader; - typedef boost::tokenizer > tokenizer; boost::char_separator sep("\n"); tokenizer tokline(card, sep); - for (tokenizer::iterator line = tokline.begin(); line != tokline.end(); ++line) + for (const auto& strline : tokline) { -// LL_INFOS() << *line << LL_ENDL; - std::string strline(*line); // LL_INFOS() << "uncommented line: " << strline << LL_ENDL; boost::regex type("^(\\s*)(\\[ )(.*)( \\])"); boost::smatch what; if (boost::regex_search(strline, what, type)) { -// LL_INFOS() << "type: " << what[0] << LL_ENDL; + std::string strtoken(what[0]); +// LL_INFOS() << "type: " << strtoken << LL_ENDL; // LL_INFOS() << "anims in type: " << boost::regex_replace(strline, type, "") << LL_ENDL; boost::char_separator sep("|,"); std::string stranimnames(boost::regex_replace(strline, type, "")); tokenizer tokanimnames(stranimnames, sep); - for (tokenizer::iterator anim = tokanimnames.begin(); anim != tokanimnames.end(); ++anim) + for (const auto& stranim : tokanimnames) { - std::string strtoken(what[0]); - std::string stranim(*anim); LLUUID animid(getAssetIDByName(stranim)); // LL_INFOS() << invfolderid.asString().c_str() << LL_ENDL; // LL_INFOS() << "anim: " << stranim.c_str() << " assetid: " << animid << LL_ENDL; - if (!(animid.notNull())) + if (animid.isNull()) { cmdline_printchat(llformat("Warning: animation '%s' could not be found (Section: %s).",stranim.c_str(),strtoken.c_str())); } else { - switch(GetStateFromToken(strtoken.c_str())) + const AOState state = GetStateFromToken(strtoken); + switch (state) { case STATE_AGENT_STAND: - loader.ao_id = animid; loader.anim_name = stranim.c_str(); mAOStands.push_back(loader); - if(mcomboBox_stands != NULL) mcomboBox_stands->add(stranim.c_str(), ADD_BOTTOM, TRUE); - break; - case STATE_AGENT_WALK: - { - if (sInstance && (mcomboBox_walks != NULL)) - { - //LL_INFOS() << "1 anim: " << stranim.c_str() << " assetid: " << animid << LL_ENDL; - if (!(mcomboBox_walks->selectByValue(stranim.c_str()))) mcomboBox_walks->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_RUN: - { - if (sInstance && (mcomboBox_runs != NULL)) - { - if (!(mcomboBox_runs->selectByValue(stranim.c_str()))) mcomboBox_runs->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_JUMP: - { - if (sInstance && (mcomboBox_jumps != NULL)) - { - if (!(mcomboBox_jumps->selectByValue(stranim.c_str()))) mcomboBox_jumps->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_SIT: - { - if (sInstance && (mcomboBox_sits != NULL)) - { - if (!(mcomboBox_sits->selectByValue(stranim.c_str()))) mcomboBox_sits->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_GROUNDSIT: - { - if (sInstance && (mcomboBox_gsits != NULL)) - { - if (!(mcomboBox_gsits->selectByValue(stranim.c_str()))) mcomboBox_gsits->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_CROUCH: - { - if (sInstance && (mcomboBox_crouchs != NULL)) - { - if (!(mcomboBox_crouchs->selectByValue(stranim.c_str()))) mcomboBox_crouchs->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_CROUCHWALK: - { - if (sInstance && (mcomboBox_cwalks != NULL)) - { - if (!(mcomboBox_cwalks->selectByValue(stranim.c_str()))) mcomboBox_cwalks->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_FALLDOWN: - { - if (sInstance && (mcomboBox_falls != NULL)) - { - if (!(mcomboBox_falls->selectByValue(stranim.c_str()))) mcomboBox_falls->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_HOVER: - { - if (sInstance && (mcomboBox_hovers != NULL)) - { - if (!(mcomboBox_hovers->selectByValue(stranim.c_str()))) mcomboBox_hovers->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_FLY: - { - if (sInstance && (mcomboBox_flys != NULL)) - { - if (!(mcomboBox_flys->selectByValue(stranim.c_str()))) mcomboBox_flys->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_FLYSLOW: - { - if (sInstance && (mcomboBox_flyslows != NULL)) - { - if (!(mcomboBox_flyslows->selectByValue(stranim.c_str()))) mcomboBox_flyslows->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_HOVER_UP: - { - if (sInstance && (mcomboBox_flyups != NULL)) - { - if (!(mcomboBox_flyups->selectByValue(stranim.c_str()))) mcomboBox_flyups->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_HOVER_DOWN: - { - if (sInstance && (mcomboBox_flydowns != NULL)) - { - if (!(mcomboBox_flydowns->selectByValue(stranim.c_str()))) mcomboBox_flydowns->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_LAND: - { - if (sInstance && (mcomboBox_lands != NULL)) - { - if (!(mcomboBox_lands->selectByValue(stranim.c_str()))) mcomboBox_lands->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_STANDUP: - { - if (sInstance && (mcomboBox_standups != NULL)) - { - if (!(mcomboBox_standups->selectByValue(stranim.c_str()))) mcomboBox_standups->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; - case STATE_AGENT_PRE_JUMP: - { - if (sInstance && (mcomboBox_prejumps != NULL)) - { - if (!(mcomboBox_prejumps->selectByValue(stranim.c_str()))) mcomboBox_prejumps->add(stranim.c_str(), ADD_BOTTOM, TRUE); //check if exist - } - } - break; + mAOStands.push_back({ animid, stranim }); + default: break; } - for (std::vector::iterator iter = mAOOverrides.begin(); iter != mAOOverrides.end(); ++iter) + + if (LLComboBox* combo = getComboFromState(state)) { - if (GetStateFromToken(strtoken.c_str()) == iter->state) + //LL_INFOS() << "1 anim: " << stranim.c_str() << " assetid: " << animid << LL_ENDL; + if (!combo->selectByValue(stranim)) //check if exist + combo->add(stranim, ADD_BOTTOM, true); + } + + for (auto& ao : mAOOverrides) + { + if (state == ao.state) { - iter->ao_id = animid; + ao.ao_id = animid; + break; } } } @@ -1135,123 +925,14 @@ void LLFloaterAO::onNotecardLoadComplete(LLVFS *vfs,const LLUUID& asset_uuid,LLA } LL_INFOS() << "ao nc read sucess" << LL_ENDL; - for (std::vector::iterator iter = mAOOverrides.begin(); iter != mAOOverrides.end(); ++iter) + for (auto& ao : mAOOverrides) { - switch(iter->state) + if (LLComboBox* combo = getComboFromState(ao.state)) { - - case STATE_AGENT_WALK: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultWalk"); - SetDefault(mcomboBox_walks,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_RUN: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultRun"); - SetDefault(mcomboBox_runs,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_JUMP: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultJump"); - SetDefault(mcomboBox_jumps,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_SIT: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultSit"); - SetDefault(mcomboBox_sits,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_CROUCH: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultCrouch"); - SetDefault(mcomboBox_crouchs,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_GROUNDSIT: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultGroundSit"); - SetDefault(mcomboBox_gsits,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_CROUCHWALK: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultCrouchWalk"); - SetDefault(mcomboBox_cwalks,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_FALLDOWN: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultFall"); - SetDefault(mcomboBox_falls,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_HOVER: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultHover"); - SetDefault(mcomboBox_hovers,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_FLY: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultFly"); - SetDefault(mcomboBox_flys,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_HOVER_UP: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultFlyUp"); - SetDefault(mcomboBox_flyups,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_FLYSLOW: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultFlySlow"); - SetDefault(mcomboBox_flyslows,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_HOVER_DOWN: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultFlyDown"); - SetDefault(mcomboBox_flydowns,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_LAND: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultLand"); - SetDefault(mcomboBox_lands,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_STANDUP: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultStandUp"); - SetDefault(mcomboBox_standups,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; - case STATE_AGENT_PRE_JUMP: - { - std::string defaultanim = gSavedPerAccountSettings.getString("AODefaultPreJump"); - SetDefault(mcomboBox_prejumps,iter->ao_id,defaultanim); - if (getAssetIDByName(defaultanim) != LLUUID::null) iter->ao_id = getAssetIDByName(defaultanim); - } - break; + std::string defaultanim = gSavedPerAccountSettings.getString(combo->getControlName()); + const LLUUID ao_id = getAssetIDByName(defaultanim); + if (ao_id != LLUUID::null) ao.ao_id = ao_id; + combo->selectByValue(defaultanim); } } run(); @@ -1268,44 +949,14 @@ void LLFloaterAO::onNotecardLoadComplete(LLVFS *vfs,const LLUUID& asset_uuid,LLA } } -BOOL LLFloaterAO::SetDefault(void* userdata, LLUUID ao_id, std::string defaultanim) -{ - if (sInstance && (userdata)) - { - LLComboBox *box = (LLComboBox *) userdata; - if (ao_id.isNull()) - { - box->clear(); - box->removeall(); - } - else - { - box->selectByValue(defaultanim); - } - } - return TRUE; -} - class ObjectNameMatches : public LLInventoryCollectFunctor { public: - ObjectNameMatches(std::string name) - { - sName = name; - } + ObjectNameMatches(std::string name) : sName(name) {} virtual ~ObjectNameMatches() {} - virtual bool operator()(LLInventoryCategory* cat, - LLInventoryItem* item) + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) { - if(item) - { - if (item->getParentUUID() == LLFloaterAO::invfolderid) - { - return (item->getName() == sName); - } - return false; - } - return false; + return item && item->getParentUUID() == LLFloaterAO::invfolderid && item->getName() == sName; } private: std::string sName; @@ -1313,16 +964,12 @@ private: const LLUUID& LLFloaterAO::getAssetIDByName(const std::string& name) { - if (name.empty() || !(LLInventoryModelBackgroundFetch::instance().isEverythingFetched())) return LLUUID::null; + if (name.empty() || !LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) return LLUUID::null; LLViewerInventoryCategory::cat_array_t cats; LLViewerInventoryItem::item_array_t items; - ObjectNameMatches objectnamematches(name); - gInventory.collectDescendentsIf(LLUUID::null,cats,items,FALSE,objectnamematches); + ObjectNameMatches matches(name); + gInventory.collectDescendentsIf(LLUUID::null,cats,items,false, matches); - if (items.size()) - { - return items[0]->getAssetUUID(); - } - return LLUUID::null; + return items.empty() ? LLUUID::null : items[0]->getAssetUUID(); }; diff --git a/indra/newview/floaterao.h b/indra/newview/floaterao.h index b8a63f0da..2aaa789c9 100644 --- a/indra/newview/floaterao.h +++ b/indra/newview/floaterao.h @@ -8,32 +8,42 @@ #include "lleventtimer.h" -const int STATE_AGENT_IDLE = 0; -const int STATE_AGENT_WALK = 1; -const int STATE_AGENT_RUN = 2; -const int STATE_AGENT_STAND = 3; +enum AOState +{ + STATE_AGENT_IDLE = 0, + STATE_AGENT_WALK = 1, + STATE_AGENT_RUN = 2, + STATE_AGENT_STAND = 3, -const int STATE_AGENT_PRE_JUMP = 4; -const int STATE_AGENT_JUMP = 5; -const int STATE_AGENT_TURNLEFT = 6; -const int STATE_AGENT_TURNRIGHT = 7; + STATE_AGENT_PRE_JUMP = 4, + STATE_AGENT_JUMP = 5, + STATE_AGENT_TURNLEFT = 6, + STATE_AGENT_TURNRIGHT = 7, -const int STATE_AGENT_SIT = 8; -const int STATE_AGENT_GROUNDSIT = 9; + STATE_AGENT_SIT = 8, + STATE_AGENT_GROUNDSIT = 9, -const int STATE_AGENT_HOVER = 10; -const int STATE_AGENT_HOVER_DOWN = 11; -const int STATE_AGENT_HOVER_UP = 12; + STATE_AGENT_HOVER = 10, + STATE_AGENT_HOVER_DOWN = 11, + STATE_AGENT_HOVER_UP = 12, -const int STATE_AGENT_CROUCH = 13; -const int STATE_AGENT_CROUCHWALK = 14; -const int STATE_AGENT_FALLDOWN = 15; -const int STATE_AGENT_STANDUP = 16; -const int STATE_AGENT_LAND = 17; + STATE_AGENT_CROUCH = 13, + STATE_AGENT_CROUCHWALK = 14, + STATE_AGENT_FALLDOWN = 15, + STATE_AGENT_STANDUP = 16, + STATE_AGENT_LAND = 17, -const int STATE_AGENT_FLY = 18; -const int STATE_AGENT_FLYSLOW = 19; + STATE_AGENT_FLY = 18, + STATE_AGENT_FLYSLOW = 19, + STATE_AGENT_TYPING = 20, + + STATE_AGENT_SWIM_DOWN = 21, + STATE_AGENT_SWIM_UP = 22, + STATE_AGENT_SWIM = 23, + STATE_AGENT_FLOAT = 24, + STATE_AGENT_END +}; @@ -43,8 +53,8 @@ class AOStandTimer : public LLEventTimer public: AOStandTimer(); ~AOStandTimer(); - virtual BOOL tick(); - virtual void reset(); + BOOL tick() override; + void reset(); }; class AOInvTimer : public LLEventTimer @@ -52,73 +62,69 @@ class AOInvTimer : public LLEventTimer public: AOInvTimer(); ~AOInvTimer(); - BOOL tick(); + BOOL tick() override; }; -class LLFloaterAO : public LLFloater +class LLFloaterAO : public LLFloater, public LLFloaterSingleton { public: - LLFloaterAO(); - virtual BOOL postBuild(); + LLFloaterAO(const LLSD&); + BOOL postBuild() override; + void onOpen() override; virtual ~LLFloaterAO(); - static void show(void*); static void init(); - static void onClickToggleAO(); - static void onClickToggleSits(); static void run(); - static void updateLayout(LLFloaterAO* floater); + void updateLayout(bool advanced); - static BOOL loadAnims(); + //static bool loadAnims(); - static int getAnimationState(); - static void setAnimationState(int state); - static void setStates(const LLUUID& id, BOOL start); + static void typing(bool start); + static AOState flyToSwimState(const AOState& state); + static AOState swimToFlyState(const AOState& state); + static AOState getAnimationState(); + static void setAnimationState(const AOState& state); static LLUUID getCurrentStandId(); static void setCurrentStandId(const LLUUID& id); static int stand_iterator; - static BOOL ChangeStand(); + static void ChangeStand(); + static void toggleSwim(bool underwater); - static BOOL startMotion(const LLUUID& id, F32 time_offset = 0.f, BOOL stand = FALSE); - static BOOL stopMotion(const LLUUID& id, BOOL stop_immediate, BOOL stand = FALSE); + static void doMotion(const LLUUID& id, bool start, bool stand = false); + static void startMotion(const LLUUID& id, bool stand = false) { doMotion(id, true, stand); } + static void stopMotion(const LLUUID& id, bool stand = false) { doMotion(id, false, stand); } + static bool swimCheck(const AOState& state); static LLUUID GetAnimID(const LLUUID& id); - static int GetStateFromAnimID(const LLUUID& id); - static LLUUID GetAnimIDFromState(const int state); - static int GetStateFromToken(std::string strtoken); + static AOState GetStateFromAnimID(const LLUUID& id); + static LLUUID GetAnimIDFromState(const AOState& state); + static AOState GetStateFromToken(std::string strtoken); - static void onClickLess(void* data) ; - static void onClickMore(void* data) ; - - static void onClickPrevStand(void* userdata); - static void onClickNextStand(void* userdata); - static void onClickReloadCard(void* userdata); - static void onClickOpenCard(void* userdata); - static void onClickNewCard(void* userdata); + static void onClickCycleStand(bool next); + static void onClickReloadCard(); + static void onClickOpenCard(); + static void onClickNewCard(); static LLUUID invfolderid; static const LLUUID& getAssetIDByName(const std::string& name); - - static bool getInstance(); private: - static LLFloaterAO* sInstance; - static int mAnimationState; + static AOState mAnimationState; static LLUUID mCurrentStandId; - static void onSpinnerCommit(LLUICtrl* ctrl); + static void onSpinnerCommit(); static void onComboBoxCommit(LLUICtrl* ctrl); - static BOOL SetDefault(void *userdata, LLUUID ao_id, std::string defaultanim); - - BOOL mDirty; protected: + static AOState getStateFromCombo(const class LLComboBox* combo); + static LLComboBox* getComboFromState(const AOState& state); + static void onNotecardLoadComplete(LLVFS *vfs,const LLUUID& asset_uuid,LLAssetType::EType type,void* user_data, S32 status, LLExtStat ext_status); }; diff --git a/indra/newview/floaterlocalassetbrowse.cpp b/indra/newview/floaterlocalassetbrowse.cpp index 3c8e4fae7..5ee0445a2 100644 --- a/indra/newview/floaterlocalassetbrowse.cpp +++ b/indra/newview/floaterlocalassetbrowse.cpp @@ -64,6 +64,8 @@ this feature is still a work in progress. #include "llviewermenufile.h" #include "llfloaterimagepreview.h" #include "llfile.h" +#include "llsdparam.h" +#include "llsdserialize.h" /* including to force rebakes when needed */ #include "llvoavatarself.h" @@ -93,21 +95,29 @@ bool LocalAssetBrowser::mSculptUpdated; containing one loaded local texture. */ -LocalBitmap::LocalBitmap(std::string fullpath) +LocalBitmap::Params::Params(const std::string& path) +: fullpath("path", path) +, keep_updating("update", gSavedSettings.getBOOL("LocalBitmapUpdate")) +, type("type", TYPE_TEXTURE) +, id("id", LLUUID::generateNewID()) { +} + +LocalBitmap::LocalBitmap(const Params& p) +{ + llassert(!p.fullpath().empty()); valid = false; - if ( gDirUtilp->fileExists(fullpath) ) + if ( gDirUtilp->fileExists(p.fullpath) ) { /* taking care of basic properties */ - id.generate(); - filename = fullpath; - keep_updating = gSavedSettings.getBOOL("LocalBitmapUpdate"); + id = p.id; + filename = p.fullpath; + keep_updating = p.keep_updating; linkstatus = keep_updating ? LINK_ON : LINK_OFF; shortname = gDirUtilp->getBaseFileName(filename, true); - bitmap_type = TYPE_TEXTURE; + bitmap_type = p.type; sculpt_dirty = false; volume_dirty = false; - valid = false; /* taking care of extension type now to avoid switch madness */ std::string temp_exten = gDirUtilp->getExtension(filename); @@ -143,6 +153,7 @@ LocalBitmap::LocalBitmap(std::string fullpath) /* filename is valid, bitmap is decoded and valid, i can haz liftoff! */ valid = true; + LocalAssetBrowser::add(*this); } } } @@ -293,64 +304,64 @@ void LocalBitmap::setType( S32 type ) bitmap_type = type; } +void LocalBitmap::setID(const LLUUID& uuid) +{ + LLViewerFetchedTexture* image = gTextureList.findImage(id); + gTextureList.deleteImage(image); + id = uuid; + image->setID(id); + gTextureList.addImage(image); +} + /* [information query functions] */ -std::string LocalBitmap::getShortName() +std::string LocalBitmap::getShortName() const { return shortname; } -std::string LocalBitmap::getFileName() +std::string LocalBitmap::getFileName() const { return filename; } -LLUUID LocalBitmap::getID() +LLUUID LocalBitmap::getID() const { return id; } -LLSD LocalBitmap::getLastModified() +LLSD LocalBitmap::getLastModified() const { return last_modified; } -std::string LocalBitmap::getLinkStatus() +std::string LocalBitmap::getLinkStatus() const { switch(linkstatus) { - case LINK_ON: - return "On"; - - case LINK_OFF: - return "Off"; - - case LINK_BROKEN: - return "Broken"; - - case LINK_UPDATING: - return "Updating"; - - default: - return "Unknown"; + case LINK_ON: return "On"; + case LINK_OFF: return "Off"; + case LINK_BROKEN: return "Broken"; + case LINK_UPDATING: return "Updating"; + default: return "Unknown"; } } -bool LocalBitmap::getUpdateBool() +bool LocalBitmap::getUpdateBool() const { return keep_updating; } -bool LocalBitmap::getIfValidBool() +bool LocalBitmap::getIfValidBool() const { return valid; } -S32 LocalBitmap::getType() +S32 LocalBitmap::getType() const { return bitmap_type; } -std::vector LocalBitmap::getFaceUsesThis(LLDrawable* drawable) +std::vector LocalBitmap::getFaceUsesThis(LLDrawable* drawable) const { std::vector matching_faces; @@ -365,7 +376,7 @@ std::vector LocalBitmap::getFaceUsesThis(LLDrawable* drawable) return matching_faces; } -std::vector LocalBitmap::getUsingObjects(bool seek_by_type, bool seek_textures, bool seek_sculptmaps) +std::vector LocalBitmap::getUsingObjects(bool seek_by_type, bool seek_textures, bool seek_sculptmaps) const { std::vector affected_vector; @@ -413,21 +424,29 @@ std::vector LocalBitmap::getUsingObjects(bool seek_by_type, boo return affected_vector; } -void LocalBitmap::getDebugInfo() +void LocalBitmap::getDebugInfo() const { /* debug function: dumps everything human readable into llinfos */ - LL_INFOS() << "===[local bitmap debug]===" << "\n" - << "path: " << filename << "\n" - << "name: " << shortname << "\n" - << "extension: " << extension << "\n" - << "uuid: " << id << "\n" - << "last modified: " << last_modified << "\n" - << "link status: " << getLinkStatus() << "\n" - << "keep updated: " << keep_updating << "\n" - << "type: " << bitmap_type << "\n" - << "is valid: " << valid << "\n" + LL_INFOS() << "===[local bitmap debug]===" << '\n' + << "path: " << filename << '\n' + << "name: " << shortname << '\n' + << "extension: " << extension << '\n' + << "uuid: " << id << '\n' + << "last modified: " << last_modified << '\n' + << "link status: " << getLinkStatus() << '\n' + << "keep updated: " << keep_updating << '\n' + << "type: " << bitmap_type << '\n' + << "is valid: " << valid << '\n' << "==========================" << LL_ENDL; - +} + +LLSD LocalBitmap::asLLSD() const +{ + return LLSD() + .with("path", filename) + .with("id", id) + .with("update", keep_updating) + .with("type", bitmap_type); } /*=======================================*/ @@ -439,15 +458,47 @@ void LocalBitmap::getDebugInfo() Sits in memory until the viewer is closed. */ +const std::string LocalAssetBrowser::getFileName() const +{ + return gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "local_assets.xml"); +} + LocalAssetBrowser::LocalAssetBrowser() { + gLocalBrowser = this; mLayerUpdated = false; mSculptUpdated = false; + + // Load bitmaps + llifstream file(getFileName()); + if (!file) return; + LLSD saved_assets; + LLSDSerialize::fromXML(saved_assets, file); + file.close(); + for (auto it = saved_assets.beginArray(), end = saved_assets.endArray(); it < end; ++it) + { + const auto&& p = LLSDParamAdapter(*it); + LocalBitmap bm(p); // Creating one adds it to the list + } + + if (!loaded_bitmaps.empty()) PingTimer(); } LocalAssetBrowser::~LocalAssetBrowser() { - + // Save bitmaps + llofstream file(getFileName()); + if (!file) + { + LL_WARNS() << "Could not open file " << getFileName() << " for saving." << LL_ENDL; + return; + } + LLSD saved_assets(LLSD::emptyArray()); + for (const auto& bitmap : loaded_bitmaps) + saved_assets.append(bitmap.asLLSD()); + LLSDSerialize::toPrettyXML(saved_assets, file); + file.close(); + gLocalBrowser = nullptr; } void LocalAssetBrowser::AddBitmap() @@ -463,20 +514,13 @@ void LocalAssetBrowser::AddBitmap_continued(AIFilePicker* filepicker) return; bool change_happened = false; - std::vector const& filenames(filepicker->getFilenames()); - for(std::vector::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) - { - LocalBitmap unit(*filename); - if (unit.getIfValidBool()) - { - loaded_bitmaps.push_back(unit); + + for(const auto& filename : filepicker->getFilenames()) + if (LocalBitmap(filename).getIfValidBool()) change_happened = true; - } - } if (change_happened) onChangeHappened(); } - void LocalAssetBrowser::DelBitmap( std::vector delete_vector, S32 column ) { bool change_happened = false; @@ -592,21 +636,20 @@ void LocalAssetBrowser::UpdateTextureCtrlList(LLScrollListCtrl* ctrl) if (ctrl) // checking again in case called externally for some silly reason. { ctrl->clearRows(); - if ( !loaded_bitmaps.empty() ) + for (const auto& bitmap : loaded_bitmaps) { - for (local_list_iter iter = loaded_bitmaps.begin(); iter != loaded_bitmaps.end(); ++iter) - { - LLSD element; - element["columns"][0]["column"] = "unit_name"; - element["columns"][0]["type"] = "text"; - element["columns"][0]["value"] = (*iter).shortname; + auto row = LLScrollListItem::Params(); + row.columns.add(LLScrollListCell::Params() + .column("unit_name") + .type("text") + .value(bitmap.shortname)); - element["columns"][1]["column"] = "unit_id_HIDDEN"; - element["columns"][1]["type"] = "text"; - element["columns"][1]["value"] = (*iter).id; + row.columns.add(LLScrollListCell::Params() + .column("unit_id_HIDDEN") + .type("text") + .value(bitmap.id)); - ctrl->addElement(element); - } + ctrl->addRow(row); } } } @@ -731,6 +774,11 @@ FloaterLocalAssetBrowser::FloaterLocalAssetBrowser() // checkbox callbacks mUpdateChkBox->setCommitCallback(boost::bind(&FloaterLocalAssetBrowser::onClickUpdateChkbox,this)); + + // Allow changing the ID! + mUUIDTxt->setCommitCallback(boost::bind(&FloaterLocalAssetBrowser::onUpdateID, this, _2)); + mUUIDTxt->setCommitOnReturn(true); + mUUIDTxt->setCommitOnFocusLost(true); } void FloaterLocalAssetBrowser::show(void*) @@ -798,6 +846,21 @@ void FloaterLocalAssetBrowser::onCommitTypeCombo() } } +void FloaterLocalAssetBrowser::onUpdateID(const LLSD& val) +{ + const auto& id = val.asUUID(); + const auto& col = mBitmapList->getFirstSelected()->getColumn(BITMAPLIST_COL_ID); + const auto& old_id = col->getValue().asString(); + if (id.isNull() && id.asString() != old_id) // Just reset if invalid or unchanged + mUUIDTxt->setValue(old_id); + else + { + gLocalBrowser->GetBitmapUnit(LLUUID(old_id))->setID(id); + mTextureView->setImageAssetID(id); + col->setValue(id); + } +} + void FloaterLocalAssetBrowser::FloaterResize(bool expand) { mMoreBtn->setVisible(!expand); @@ -855,10 +918,9 @@ void FloaterLocalAssetBrowser::UpdateRightSide() */ if (!mTextureView->getVisible()) return; - if (!mBitmapList->getAllSelected().empty()) + if (const auto& selected = mBitmapList->getFirstSelected()) { - LocalBitmap* unit = gLocalBrowser->GetBitmapUnit( LLUUID(mBitmapList->getSelectedItemLabel(BITMAPLIST_COL_ID)) ); - + LocalBitmap* unit = gLocalBrowser->GetBitmapUnit(LLUUID(selected->getColumn(BITMAPLIST_COL_ID)->getValue().asString())); if ( unit ) { mTextureView->setImageAssetID(unit->getID()); @@ -873,6 +935,7 @@ void FloaterLocalAssetBrowser::UpdateRightSide() mTextureView->setEnabled(true); mUpdateChkBox->setEnabled(true); mTypeComboBox->setEnabled(true); + mUUIDTxt->setEnabled(true); } } else @@ -884,12 +947,14 @@ void FloaterLocalAssetBrowser::UpdateRightSide() mTypeComboBox->selectFirstItem(); mTypeComboBox->setEnabled(false); + mUUIDTxt->setEnabled(false); - mPathTxt->setText(LLStringExplicit("None")); - mUUIDTxt->setText(LLStringExplicit("None")); - mNameTxt->setText(LLStringExplicit("None")); - mLinkTxt->setText(LLStringExplicit("None")); - mTimeTxt->setText(LLStringExplicit("None")); + const auto none = LLStringExplicit("None"); + mPathTxt->setText(none); + mUUIDTxt->setText(none); + mNameTxt->setText(none); + mLinkTxt->setText(none); + mTimeTxt->setText(none); } } @@ -931,7 +996,7 @@ void LocalAssetBrowserTimer::stop() mEventTimer.stop(); } -bool LocalAssetBrowserTimer::isRunning() +bool LocalAssetBrowserTimer::isRunning() const { return mEventTimer.getStarted(); } diff --git a/indra/newview/floaterlocalassetbrowse.h b/indra/newview/floaterlocalassetbrowse.h index dd7e618ea..290fbe4e3 100644 --- a/indra/newview/floaterlocalassetbrowse.h +++ b/indra/newview/floaterlocalassetbrowse.h @@ -97,7 +97,15 @@ struct affected_object class LocalBitmap { public: - LocalBitmap(std::string filename); + struct Params : public LLInitParam::Block + { + Mandatory fullpath; + Optional keep_updating; + Optional type; + Optional id; + Params(const std::string& path = LLStringUtil::null); + }; + LocalBitmap(const Params& p); virtual ~LocalBitmap(); friend class LocalAssetBrowser; @@ -127,25 +135,28 @@ class LocalBitmap }; public: /* [information query functions] */ - std::string getShortName(); - std::string getFileName(); - LLUUID getID(); - LLSD getLastModified(); - std::string getLinkStatus(); - bool getUpdateBool(); + std::string getShortName() const; + std::string getFileName() const; + LLUUID getID() const; + void setID(const LLUUID&); + LLSD getLastModified() const; + std::string getLinkStatus() const; + bool getUpdateBool() const; void setType( S32 ); - bool getIfValidBool(); - S32 getType(); - void getDebugInfo(); + bool getIfValidBool() const; + S32 getType() const; + void getDebugInfo() const; + LLSD asLLSD() const; + private: /* [maintenence functions] */ void updateSelf(); bool decodeSelf(LLImageRaw* rawimg); void setUpdateBool(); - std::vector getFaceUsesThis(LLDrawable*); + std::vector getFaceUsesThis(LLDrawable*) const; std::vector getUsingObjects(bool seek_by_type = true, - bool seek_textures = false, bool seek_sculptmaps = false); + bool seek_textures = false, bool seek_sculptmaps = false) const; protected: /* [basic properties] */ std::string shortname; @@ -173,9 +184,10 @@ class LocalBitmap class AIFilePicker; -class LocalAssetBrowser +class LocalAssetBrowser : public LLSingleton { public: + const std::string getFileName() const; LocalAssetBrowser(); virtual ~LocalAssetBrowser(); friend class FloaterLocalAssetBrowser; @@ -183,6 +195,7 @@ class LocalAssetBrowser static void UpdateTextureCtrlList(LLScrollListCtrl*); static void setLayerUpdated(bool toggle) { mLayerUpdated = toggle; } static void setSculptUpdated(bool toggle) { mSculptUpdated = toggle; } + static void add(const LocalBitmap& unit) { loaded_bitmaps.push_back(unit); } static void AddBitmap(); static void AddBitmap_continued(AIFilePicker* filepicker); static void DelBitmap( std::vector, S32 column = BITMAPLIST_COL_ID ); @@ -241,6 +254,8 @@ private: // Combobox type select void onCommitTypeCombo(); + void onUpdateID(const LLSD& val); + // Widgets LLButton* mAddBtn; LLButton* mDelBtn; @@ -289,10 +304,10 @@ class LocalAssetBrowserTimer : public LLEventTimer public: LocalAssetBrowserTimer(); ~LocalAssetBrowserTimer(); - virtual BOOL tick(); + BOOL tick() override; void start(); void stop(); - bool isRunning(); + bool isRunning() const; }; #endif diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index 5295aac95..315b7cf8b 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -48,6 +48,7 @@ HippoGridInfo::HippoGridInfo(const std::string& gridName) : mWebSite(LLStringUtil::null), mSupportUrl(LLStringUtil::null), mRegisterUrl(LLStringUtil::null), + mPartnerUrl(LLStringUtil::null), mPasswordUrl(LLStringUtil::null), mSearchUrl(LLStringUtil::null), mGridMessage(""), @@ -81,7 +82,7 @@ const std::string& HippoGridInfo::getGridOwner() const } else { - return this->getGridName(); + return getGridName(); } } @@ -102,12 +103,11 @@ void HippoGridInfo::setPlatform(Platform platform) void HippoGridInfo::setPlatform(const std::string& platform) { std::string tmp = platform; - for (unsigned i=0; imXmlState = XML_SUPPORT; else if ((strcasecmp(name, "register") == 0) || (strcasecmp(name, "account") == 0)) self->mXmlState = XML_REGISTER; + else if (strcasecmp(name, "partner") == 0) + self->mXmlState = XML_PARTNER; else if (strcasecmp(name, "password") == 0) self->mXmlState = XML_PASSWORD; else if (strcasecmp(name, "search") == 0) @@ -337,7 +344,7 @@ void HippoGridInfo::onXmlCharacterData(void* userData, const XML_Char* s, int le case XML_GRIDNAME: { - if (self->mGridName == "") + if (self->mGridName.empty()) { self->mGridName.assign(s, len); } @@ -348,6 +355,7 @@ void HippoGridInfo::onXmlCharacterData(void* userData, const XML_Char* s, int le case XML_WEBSITE: self->mWebSite.assign(s, len); break; case XML_SUPPORT: self->mSupportUrl.assign(s, len); break; case XML_REGISTER: self->mRegisterUrl.assign(s, len); break; + case XML_PARTNER: self->mPartnerUrl.assign(s, len); break; case XML_PASSWORD: self->mPasswordUrl.assign(s, len); break; case XML_MESSAGE: self->mGridMessage.assign(s, len); break; @@ -366,7 +374,7 @@ void HippoGridInfo::getGridInfo() // Make sure the uri ends on a '/'. std::string uri = mLoginUri; - if (uri.compare(uri.length() - 1, 1, "/") != 0) + if (uri.back() != '/') { uri += '/'; } @@ -482,9 +490,9 @@ std::string HippoGridInfo::getGridNick() const // static const char* HippoGridInfo::getPlatformString(Platform platform) { - static const char* platformStrings[PLATFORM_LAST] = + constexpr const char* platformStrings[PLATFORM_LAST] = { - "Other", "Aurora", "OpenSim", "SecondLife" + "Other", "WhiteCore", "OpenSim", "SecondLife" }; if ((platform < PLATFORM_OTHER) || (platform >= PLATFORM_LAST)) @@ -894,6 +902,7 @@ void HippoGridManager::parseData(LLSD &gridInfo, bool mergeIfNewer) if (gridMap.has("website")) grid->setWebSite(gridMap["website"]); if (gridMap.has("support")) grid->setSupportUrl(gridMap["support"]); if (gridMap.has("register")) grid->setRegisterUrl(gridMap["register"]); + if (gridMap.has("partner")) grid->setPartnerUrl(gridMap["partner"]); if (gridMap.has("password")) grid->setPasswordUrl(gridMap["password"]); if (gridMap.has("search")) grid->setSearchUrl(gridMap["search"]); if (gridMap.has("render_compat")) grid->setRenderCompat(gridMap["render_compat"]); @@ -929,6 +938,7 @@ void HippoGridManager::saveFile() gridInfo[i]["website"] = grid->getWebSite(); gridInfo[i]["support"] = grid->getSupportUrl(); gridInfo[i]["register"] = grid->getRegisterUrl(); + gridInfo[i]["partner"] = grid->getPartnerUrl(); gridInfo[i]["password"] = grid->getPasswordUrl(); gridInfo[i]["search"] = grid->getSearchUrl(); diff --git a/indra/newview/hippogridmanager.h b/indra/newview/hippogridmanager.h index 450cc35f7..8d2ae9650 100644 --- a/indra/newview/hippogridmanager.h +++ b/indra/newview/hippogridmanager.h @@ -26,7 +26,7 @@ class HippoGridInfo public: enum Platform { PLATFORM_OTHER = 0, - PLATFORM_AURORA, + PLATFORM_WHITECORE, PLATFORM_OPENSIM, PLATFORM_SECONDLIFE, PLATFORM_LAST @@ -35,8 +35,8 @@ public: explicit HippoGridInfo(const std::string& gridName); Platform getPlatform() { return mPlatform; } - bool isOpenSimulator() const { return (mPlatform == PLATFORM_OPENSIM || mPlatform == PLATFORM_AURORA); } - bool isAurora() const { return (mPlatform == PLATFORM_AURORA); } + bool isOpenSimulator() const { return (mPlatform == PLATFORM_OPENSIM || mPlatform == PLATFORM_WHITECORE); } + bool isWhiteCore() const { return (mPlatform == PLATFORM_WHITECORE); } bool isSecondLife() const { return (mPlatform == PLATFORM_SECONDLIFE); } bool isAvination() const { return mIsInAvination; } bool isInProductionGrid() const { llassert(mPlatform == PLATFORM_SECONDLIFE); return mIsInProductionGrid; } // Should only be called if isSecondLife() returns true. @@ -48,6 +48,7 @@ public: const std::string& getWebSite() const { return mWebSite; } const std::string& getSupportUrl() const { return mSupportUrl; } const std::string& getRegisterUrl() const { return mRegisterUrl; } + const std::string& getPartnerUrl() const { return mPartnerUrl; } const std::string& getPasswordUrl() const { return mPasswordUrl; } // Returns the url base used for the Web Search tab const std::string& getSearchUrl() const { return mSearchUrl; } @@ -75,6 +76,7 @@ public: void setWebSite (const std::string& website); void setSupportUrl(const std::string& url); void setRegisterUrl(const std::string& url); + void setPartnerUrl(const std::string& url); void setPasswordUrl(const std::string& url); // sets the url base used for the Web Search tab void setSearchUrl(const std::string& url); @@ -111,6 +113,7 @@ private: std::string mWebSite; std::string mSupportUrl; std::string mRegisterUrl; + std::string mPartnerUrl; std::string mPasswordUrl; std::string mSearchUrl; std::string mVoiceConnector; @@ -134,7 +137,7 @@ private: { XML_VOID, XML_PLATFORM, XML_GRIDNAME, XML_GRIDNICK, XML_LOGINURI, XML_LOGINPAGE, XML_HELPERURI, - XML_WEBSITE, XML_SUPPORT, XML_REGISTER, XML_PASSWORD, XML_SEARCH, XML_MESSAGE + XML_WEBSITE, XML_SUPPORT, XML_REGISTER, XML_PARTNER, XML_PASSWORD, XML_SEARCH, XML_MESSAGE }; XmlState mXmlState; diff --git a/indra/newview/hippolimits.cpp b/indra/newview/hippolimits.cpp index 8e7462511..439af0157 100644 --- a/indra/newview/hippolimits.cpp +++ b/indra/newview/hippolimits.cpp @@ -21,8 +21,8 @@ void HippoLimits::setLimits() { if (gHippoGridManager->getConnectedGrid()->getPlatform() == HippoGridInfo::PLATFORM_SECONDLIFE) { setSecondLifeLimits(); - } else if (gHippoGridManager->getConnectedGrid()->getPlatform() == HippoGridInfo::PLATFORM_AURORA) { - setAuroraLimits(); + } else if (gHippoGridManager->getConnectedGrid()->getPlatform() == HippoGridInfo::PLATFORM_WHITECORE) { + setWhiteCoreLimits(); } else { setOpenSimLimits(); } @@ -53,20 +53,19 @@ void HippoLimits::setOpenSimLimits() setMaxAgentGroups(); mMaxPrimScale = 8192.0f; mMaxHeight = 10000.0f; + mMinPrimScale = 0.001f; if (gHippoGridManager->getConnectedGrid()->isRenderCompat()) { LL_INFOS() << "Using rendering compatible OpenSim limits." << LL_ENDL; - mMinPrimScale = 0.01f; mMinHoleSize = 0.05f; mMaxHollow = 0.95f; } else { LL_INFOS() << "Using Hippo OpenSim limits." << LL_ENDL; - mMinPrimScale = 0.001f; mMinHoleSize = 0.01f; mMaxHollow = 0.99f; } } -void HippoLimits::setAuroraLimits() +void HippoLimits::setWhiteCoreLimits() { setMaxAgentGroups(); mMaxPrimScale = 8192.0f; diff --git a/indra/newview/hippolimits.h b/indra/newview/hippolimits.h index 0c119cd34..6c48fc298 100644 --- a/indra/newview/hippolimits.h +++ b/indra/newview/hippolimits.h @@ -27,7 +27,7 @@ private: void setMaxAgentGroups(); void setOpenSimLimits(); - void setAuroraLimits(); + void setWhiteCoreLimits(); void setSecondLifeLimits(); }; diff --git a/indra/newview/hippopanelgrids.cpp b/indra/newview/hippopanelgrids.cpp index d5570e84a..53446f17c 100644 --- a/indra/newview/hippopanelgrids.cpp +++ b/indra/newview/hippopanelgrids.cpp @@ -130,6 +130,7 @@ BOOL HippoPanelGridsImpl::postBuild() requires("website"); requires("support"); requires("register"); + requires("partner"); requires("password"); requires("search"); requires("btn_delete"); @@ -172,7 +173,7 @@ void HippoPanelGridsImpl::refresh() LLComboBox *grids = getChild("grid_selector"); S32 selectIndex = -1, i = 0; grids->removeall(); - if (defaultGrid != "") { + if (!defaultGrid.empty()) { grids->add(defaultGrid); selectIndex = i++; } @@ -192,10 +193,10 @@ void HippoPanelGridsImpl::refresh() if (selectIndex >= 0) { grids->setCurrentByIndex(selectIndex); } else { - grids->setLabel(LLStringExplicit("")); // LLComboBox::removeall() does not clear the label + grids->setLabel(LLStringUtil::null); // LLComboBox::removeall() does not clear the label } - childSetTextArg("default_grid", "[DEFAULT]", (defaultGrid != "")? defaultGrid: " "); + childSetTextArg("default_grid", "[DEFAULT]", !defaultGrid.empty() ? defaultGrid : " "); childSetEnabled("btn_delete", (selectIndex >= 0) && mIsEditable ); childSetEnabled("btn_copy", (mState == NORMAL) && (selectIndex >= 0)); @@ -244,11 +245,12 @@ void HippoPanelGridsImpl::loadCurGrid() childSetText("support", gridInfo->getSupportUrl()); childSetText("search", gridInfo->getSearchUrl()); childSetText("register", gridInfo->getRegisterUrl()); + childSetText("partner", gridInfo->getPartnerUrl()); childSetText("password", gridInfo->getPasswordUrl()); childSetValue("render_compat", gridInfo->isRenderCompat()); enableEditing(!gridInfo->getLocked()); } else { - std::string empty = ""; + const std::string& empty = LLStringUtil::null; LLComboBox *platform = getChild("platform"); if (platform) platform->setCurrentByIndex(HippoGridInfo::PLATFORM_OTHER); childSetText("gridname", empty); @@ -259,6 +261,7 @@ void HippoPanelGridsImpl::loadCurGrid() childSetText("support", empty); childSetText("search", empty); childSetText("register", empty); + childSetText("partner", empty); childSetText("password", empty); childSetValue("render_compat", true); enableEditing(true); @@ -288,9 +291,9 @@ bool HippoPanelGridsImpl::saveCurGrid() refresh(); std::string gridname = childGetValue("gridname"); - if (gridname == "") gridname = ""; + if (gridname == "") gridname.clear(); std::string loginuri = childGetValue("loginuri"); - if (loginuri == "") loginuri = ""; + if (loginuri == "") loginuri.clear(); if (gridname.empty() && !loginuri.empty()) this->retrieveGridInfo(); @@ -299,8 +302,8 @@ bool HippoPanelGridsImpl::saveCurGrid() // check nickname std::string gridname = childGetValue("gridname"); - childSetValue("gridname", (gridname != "")? gridname: ""); - if (gridname == "") { + childSetValue("gridname", !gridname.empty() ? gridname : ""); + if (gridname.empty()) { LLNotificationsUtil::add("GridsNoNick"); return false; } @@ -312,7 +315,7 @@ bool HippoPanelGridsImpl::saveCurGrid() } // check login URI - if (loginuri == "") { + if (loginuri.empty()) { LLSD args; args["NAME"] = gridname; LLNotificationsUtil::add("GridsNoLoginUri", args); @@ -361,6 +364,7 @@ bool HippoPanelGridsImpl::saveCurGrid() gridInfo->setWebSite(childGetValue("website")); gridInfo->setSupportUrl(childGetValue("support")); gridInfo->setRegisterUrl(childGetValue("register")); + gridInfo->setPartnerUrl(childGetValue("partner")); gridInfo->setPasswordUrl(childGetValue("password")); gridInfo->setSearchUrl(childGetValue("search")); gridInfo->setRenderCompat(childGetValue("render_compat")); @@ -384,7 +388,7 @@ void HippoPanelGridsImpl::reset() void HippoPanelGridsImpl::retrieveGridInfo() { std::string loginuri = childGetValue("loginuri"); - if ((loginuri == "") || (loginuri == "")) { + if ((loginuri.empty()) || (loginuri == "")) { LLNotificationsUtil::add("GridInfoNoLoginUri"); return; } @@ -394,7 +398,7 @@ void HippoPanelGridsImpl::retrieveGridInfo() if (mState == NORMAL) { grid = gHippoGridManager->getGrid(mCurGrid); } else if ((mState == ADD_NEW) || (mState == ADD_COPY)) { - grid = new HippoGridInfo(""); + grid = new HippoGridInfo(LLStringUtil::null); cleanupGrid = true; } else { LL_ERRS() << "Illegal state " << mState << '.' << LL_ENDL; @@ -412,16 +416,17 @@ void HippoPanelGridsImpl::retrieveGridInfo() if (grid->getPlatform() != HippoGridInfo::PLATFORM_OTHER) getChild("platform")->setCurrentByIndex(grid->getPlatform()); - if (grid->getGridName() != "") childSetText("gridname", grid->getGridName()); - if (grid->getLoginUri() != "") childSetText("loginuri", grid->getLoginUri()); - if (grid->getLoginPage() != "") childSetText("loginpage", grid->getLoginPage()); - if (grid->getHelperUri() != "") childSetText("helperuri", grid->getHelperUri()); - if (grid->getWebSite() != "") childSetText("website", grid->getWebSite()); - if (grid->getSupportUrl() != "") childSetText("support", grid->getSupportUrl()); - if (grid->getRegisterUrl() != "") childSetText("register", grid->getRegisterUrl()); - if (grid->getPasswordUrl() != "") childSetText("password", grid->getPasswordUrl()); - if (grid->getSearchUrl() != "") childSetText("search", grid->getSearchUrl()); - if (grid->getGridMessage() != "") childSetText("gridmessage", grid->getGridMessage()); + if (!grid->getGridName().empty()) childSetText("gridname", grid->getGridName()); + if (!grid->getLoginUri().empty()) childSetText("loginuri", grid->getLoginUri()); + if (!grid->getLoginPage().empty()) childSetText("loginpage", grid->getLoginPage()); + if (!grid->getHelperUri().empty()) childSetText("helperuri", grid->getHelperUri()); + if (!grid->getWebSite().empty()) childSetText("website", grid->getWebSite()); + if (!grid->getSupportUrl().empty()) childSetText("support", grid->getSupportUrl()); + if (!grid->getRegisterUrl().empty()) childSetText("register", grid->getRegisterUrl()); + if (!grid->getPartnerUrl().empty()) childSetText("partner", grid->getPartnerUrl()); + if (!grid->getPasswordUrl().empty()) childSetText("password", grid->getPasswordUrl()); + if (!grid->getSearchUrl().empty()) childSetText("search", grid->getSearchUrl()); + if (!grid->getGridMessage().empty()) childSetText("gridmessage", grid->getGridMessage()); } catch(AIAlert::ErrorCode const& error) { @@ -532,6 +537,8 @@ void HippoPanelGridsImpl::onClickAdvanced(void *data) self->childSetVisible("support", false); self->childSetVisible("register_label", false); self->childSetVisible("register", false); + self->childSetVisible("partner_label", false); + self->childSetVisible("partner", false); self->childSetVisible("password_label", false); self->childSetVisible("password", false); self->childSetVisible("search_label", false); @@ -551,6 +558,8 @@ void HippoPanelGridsImpl::onClickAdvanced(void *data) self->childSetVisible("support", true); self->childSetVisible("register_label", true); self->childSetVisible("register", true); + self->childSetVisible("partner_label", true); + self->childSetVisible("partner", true); self->childSetVisible("password_label", true); self->childSetVisible("password", true); self->childSetVisible("search_label", true); @@ -578,6 +587,7 @@ void HippoPanelGridsImpl::enableEditing(bool b) "website", "support", "register", + "partner", "password", "search", "btn_delete", diff --git a/indra/newview/icons/alpha/viewer.png b/indra/newview/icons/alpha/viewer.png index 3e4c44fe5..8d7619f95 100644 Binary files a/indra/newview/icons/alpha/viewer.png and b/indra/newview/icons/alpha/viewer.png differ diff --git a/indra/newview/installers/darwin/firstlook-dmg/background.jpg b/indra/newview/installers/darwin/firstlook-dmg/background.jpg index 55294dcc9..3962e2fb3 100644 Binary files a/indra/newview/installers/darwin/firstlook-dmg/background.jpg and b/indra/newview/installers/darwin/firstlook-dmg/background.jpg differ diff --git a/indra/newview/installers/darwin/publicnightly-dmg/background.jpg b/indra/newview/installers/darwin/publicnightly-dmg/background.jpg index 55294dcc9..3962e2fb3 100644 Binary files a/indra/newview/installers/darwin/publicnightly-dmg/background.jpg and b/indra/newview/installers/darwin/publicnightly-dmg/background.jpg differ diff --git a/indra/newview/installers/darwin/release-dmg/background.jpg b/indra/newview/installers/darwin/release-dmg/background.jpg index 55294dcc9..3962e2fb3 100644 Binary files a/indra/newview/installers/darwin/release-dmg/background.jpg and b/indra/newview/installers/darwin/release-dmg/background.jpg differ diff --git a/indra/newview/installers/darwin/releasecandidate-dmg/background.jpg b/indra/newview/installers/darwin/releasecandidate-dmg/background.jpg index 55294dcc9..3962e2fb3 100644 Binary files a/indra/newview/installers/darwin/releasecandidate-dmg/background.jpg and b/indra/newview/installers/darwin/releasecandidate-dmg/background.jpg differ diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index ed160f8a1..8ff723f76 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -24,6 +24,10 @@ ;; ;; Author: James Cook, Don Kjer, Callum Prentice, Drake Arconis ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;-------------------------------- +;Unicode + Unicode true + ;-------------------------------- ;Include Modern UI @@ -72,7 +76,7 @@ InstallDirRegKey HKLM "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" "" !endif - ;Request application privileges for Windows Vista + ;Request application privileges for Windows UAC RequestExecutionLevel admin ;Compression @@ -139,7 +143,11 @@ !define MUI_STARTMENUPAGE_REGISTRY_KEY "SOFTWARE\${VENDORSTR}\${APPNAMEONEWORD}" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !define MUI_PAGE_CUSTOMFUNCTION_PRE check_skip +!ifdef WIN64_BIN_BUILD !define MUI_STARTMENUPAGE_DEFAULTFOLDER "${APPNAME} (64 bit) Viewer" +!else + !define MUI_STARTMENUPAGE_DEFAULTFOLDER "${APPNAME} Viewer" +!endif !insertmacro MUI_PAGE_STARTMENU Application $STARTMENUFOLDER ;Install Progress Page @@ -150,6 +158,10 @@ !define MUI_PAGE_CUSTOMFUNCTION_PRE check_skip_finish !define MUI_FINISHPAGE_RUN !define MUI_FINISHPAGE_RUN_FUNCTION launch_viewer + !define MUI_FINISHPAGE_SHOWREADME + !define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut" + !define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED + !define MUI_FINISHPAGE_SHOWREADME_FUNCTION create_desktop_shortcut !define MUI_FINISHPAGE_NOREBOOTSUPPORT !insertmacro MUI_PAGE_FINISH @@ -185,12 +197,12 @@ ;because this will make your installer start faster. !insertmacro MUI_RESERVEFILE_LANGDLL - ReserveFile "${NSISDIR}\Plugins\NSISdl.dll" - ReserveFile "${NSISDIR}\Plugins\nsDialogs.dll" - ReserveFile "${NSISDIR}\Plugins\StartMenu.dll" - ReserveFile "${NSISDIR}\Plugins\StdUtils.dll" - ReserveFile "${NSISDIR}\Plugins\System.dll" - ReserveFile "${NSISDIR}\Plugins\UserInfo.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" + ReserveFile "${NSISDIR}\Plugins\x86-unicode\System.dll" + ReserveFile "${NSISDIR}\Plugins\x86-unicode\UserInfo.dll" ;-------------------------------- ; Local Functions @@ -212,6 +224,14 @@ Function launch_viewer ${StdUtils.ExecShellAsUser} $0 "$INSTDIR\$INSTEXE" "open" "$SHORTCUT_LANG_PARAM" FunctionEnd +Function create_desktop_shortcut +!ifdef WIN64_BIN_BUILD + CreateShortCut "$DESKTOP\$INSTSHORTCUT x64.lnk" "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$INSTEXE" +!else + CreateShortCut "$DESKTOP\$INSTSHORTCUT.lnk" "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "$INSTDIR\$INSTEXE" +!endif +FunctionEnd + ;Check version compatibility Function CheckWindowsVersion !ifdef WIN64_BIN_BUILD @@ -221,7 +241,7 @@ Function CheckWindowsVersion ${EndIf} !endif - ${If} ${AtMostWinXP} + ${If} ${AtMostWinVista} MessageBox MB_OK $(CheckWindowsVersionMB) Quit ${EndIf} @@ -229,20 +249,6 @@ FunctionEnd ;Check service pack compatibility and suggest upgrade Function CheckWindowsServPack - ${If} ${IsWinVista} - ${AndIfNot} ${IsServicePack} 2 - MessageBox MB_OK $(CheckWindowsServPackMB) - DetailPrint $(UseLatestServPackDP) - Return - ${EndIf} - - ${If} ${IsWin2008} - ${AndIfNot} ${IsServicePack} 2 - MessageBox MB_OK $(CheckWindowsServPackMB) - DetailPrint $(UseLatestServPackDP) - Return - ${EndIf} - ${If} ${IsWin7} ${AndIfNot} ${IsServicePack} 1 MessageBox MB_OK $(CheckWindowsServPackMB) @@ -321,6 +327,8 @@ Function CloseSecondLife StrCmp $SKIP_DIALOGS "true" CLOSE MessageBox MB_YESNOCANCEL $(CloseSecondLifeInstMB) IDYES CLOSE IDNO DONE + +; CANCEL_INSTALL: Quit CLOSE: @@ -346,7 +354,9 @@ Function un.CloseSecondLife Push $0 FindWindow $0 "Second Life" "" IntCmp $0 0 DONE - MessageBox MB_YESNOCANCEL $(CloseSecondLifeUnInstMB) IDYES CLOSE IDNO DONE + MessageBox MB_OKCANCEL $(CloseSecondLifeUnInstMB) IDOK CLOSE IDCANCEL CANCEL_UNINSTALL + + CANCEL_UNINSTALL: Quit CLOSE: @@ -491,18 +501,46 @@ Section "Viewer" ;Write URL registry info - WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "(Default)" "URL:Second Life" + DeleteRegKey HKEY_CLASSES_ROOT "${URLNAME}" + WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "" "URL:Second Life" WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "URL Protocol" "" WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\DefaultIcon" "" "$INSTDIR\$INSTEXE" ;; URL param must be last item passed to viewer, it ignores subsequent params ;; to avoid parameter injection attacks. + WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\shell" "" "open" +!ifdef WIN64_BIN_BUILD + WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open" "FriendlyAppName" "$INSTSHORTCUT (64 bit) Viewer" +!else + WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open" "FriendlyAppName" "$INSTSHORTCUT Viewer" +!endif WriteRegExpandStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open\command" "" "$\"$INSTDIR\$INSTEXE$\" -url $\"%1$\"" - WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "(Default)" "URL:Second Life" + DeleteRegKey HKEY_CLASSES_ROOT "x-grid-info" + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info" "" "URL:Hypergrid" + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info" "URL Protocol" "" + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info\DefaultIcon" "" "$INSTDIR\$INSTEXE" + ;; URL param must be last item passed to viewer, it ignores subsequent params + ;; to avoid parameter injection attacks. + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info\shell" "" "open" +!ifdef WIN64_BIN_BUILD + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info\shell\open" "FriendlyAppName" "$INSTSHORTCUT (64 bit) Viewer" +!else + WriteRegStr HKEY_CLASSES_ROOT "x-grid-info\shell\open" "FriendlyAppName" "$INSTSHORTCUT Viewer" +!endif + WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-info\shell\open\command" "" "$\"$INSTDIR\$INSTEXE$\" -url $\"%1$\"" + + DeleteRegKey HKEY_CLASSES_ROOT "x-grid-location-info" + WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "" "URL:Hypergrid legacy" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" "" WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" "$\"$INSTDIR\$INSTEXE$\"" ;; URL param must be last item passed to viewer, it ignores subsequent params ;; to avoid parameter injection attacks. + WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\shell" "" "open" +!ifdef WIN64_BIN_BUILD + WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open" "FriendlyAppName" "$INSTSHORTCUT (64 bit) Viewer" +!else + WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open" "FriendlyAppName" "$INSTSHORTCUT Viewer" +!endif WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open\command" "" "$\"$INSTDIR\$INSTEXE$\" -url $\"%1$\"" ;Create uninstaller diff --git a/indra/newview/lffloaterinvpanel.cpp b/indra/newview/lffloaterinvpanel.cpp index 5fde1fa10..2578c90a2 100644 --- a/indra/newview/lffloaterinvpanel.cpp +++ b/indra/newview/lffloaterinvpanel.cpp @@ -32,7 +32,7 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLUUID& cat_id, LLInventoryModel* mod mCommitCallbackRegistrar.add("InvPanel.Search", boost::bind(&LLInventoryPanel::setFilterSubString, boost::ref(mPanel), _2)); LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inv_panel.xml"); LLPanel* panel = getChild("placeholder_panel"); - mPanel = new LLInventoryPanel("inv_panel", LLInventoryPanel::DEFAULT_SORT_ORDER, cat_id.asString(), panel->getRect(), model, true); + mPanel = new LLInventoryPanel("inv_panel", LLInventoryPanel::DEFAULT_SORT_ORDER, LLSD().with("id", cat_id), panel->getRect(), model, true); mPanel->postBuild(); mPanel->setFollows(FOLLOWS_ALL); mPanel->setEnabled(true); diff --git a/indra/newview/lfsimfeaturehandler.cpp b/indra/newview/lfsimfeaturehandler.cpp index 65d1e6e55..dbdf2e5de 100644 --- a/indra/newview/lfsimfeaturehandler.cpp +++ b/indra/newview/lfsimfeaturehandler.cpp @@ -92,6 +92,7 @@ void LFSimFeatureHandler::setSupportedFeatures() mGridName = gHippoGridManager->getConnectedGrid()->getGridName() != grid_name ? grid_name : LLStringUtil::null; } } + has_feature_or_default(mEventsURL, extras, "EventsURL"); has_feature_or_default(mSayRange, extras, "say-range"); has_feature_or_default(mShoutRange, extras, "shout-range"); has_feature_or_default(mWhisperRange, extras, "whisper-range"); @@ -105,6 +106,7 @@ void LFSimFeatureHandler::setSupportedFeatures() mMapServerURL = LLStringUtil::null; mSearchURL.reset(); mGridName.reset(); + mEventsURL.reset(); } mSayRange.reset(); mShoutRange.reset(); diff --git a/indra/newview/lfsimfeaturehandler.h b/indra/newview/lfsimfeaturehandler.h index 893edb5a0..9b7554e32 100644 --- a/indra/newview/lfsimfeaturehandler.h +++ b/indra/newview/lfsimfeaturehandler.h @@ -65,6 +65,7 @@ public: boost::signals2::connection setSupportsExportCallback(const SignaledType::slot_t& slot) { return mSupportsExport.connect(slot); } boost::signals2::connection setDestinationGuideURLCallback(const SignaledType::slot_t& slot) { return mDestinationGuideURL.connect(slot); } boost::signals2::connection setSearchURLCallback(const SignaledType::slot_t& slot) { return mSearchURL.connect(slot); } + boost::signals2::connection setEventsURLCallback(const SignaledType::slot_t& slot) { return mEventsURL.connect(slot); } boost::signals2::connection setSayRangeCallback(const SignaledType::slot_t& slot) { return mSayRange.connect(slot); } boost::signals2::connection setShoutRangeCallback(const SignaledType::slot_t& slot) { return mShoutRange.connect(slot); } boost::signals2::connection setWhisperRangeCallback(const SignaledType::slot_t& slot) { return mWhisperRange.connect(slot); } @@ -74,6 +75,7 @@ public: std::string destinationGuideURL() const { return mDestinationGuideURL; } std::string mapServerURL() const { return mMapServerURL; } std::string searchURL() const { return mSearchURL; } + const std::string& getEventsURL() const { return mEventsURL.ref(); } const std::string& gridName() const { return mGridName.ref(); } U32 sayRange() const { return mSayRange; } U32 shoutRange() const { return mShoutRange; } @@ -87,6 +89,7 @@ private: std::string mMapServerURL; SignaledType mSearchURL; SignaledType mGridName; + SignaledType mEventsURL; SignaledType mSayRange; SignaledType mShoutRange; SignaledType mWhisperRange; diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index c447a4925..457de28d5 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -103,6 +103,7 @@ #include "lluictrlfactory.h" //For LLUICtrlFactory::getLayeredXMLNode +#include "floaterao.h" // for Typing override #include "hippolimits.h" // for getMaxAgentGroups // [RLVa:KB] - Checked: 2011-11-04 (RLVa-1.4.4a) #include "rlvactions.h" @@ -2014,10 +2015,7 @@ void LLAgent::startTyping() } } - if (gSavedSettings.getBOOL("PlayTypingAnim")) - { - sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); - } + LLFloaterAO::typing(true); // Singu Note: Typing anims handled by AO/settings. gChatBar-> sendChatFromViewer("", CHAT_TYPE_START, FALSE); } @@ -2030,7 +2028,7 @@ void LLAgent::stopTyping() if (mRenderState & AGENT_STATE_TYPING) { clearRenderState(AGENT_STATE_TYPING); - sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); + LLFloaterAO::typing(false); // Singu Note: Typing anims handled by AO/settings. gChatBar-> sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } @@ -4123,6 +4121,12 @@ void LLAgent::handleTeleportFinished() LLNotificationsUtil::add("PreferredMaturityChanged", args); mIsMaturityRatingChangingDuringTeleport = false; } + + // Init SLM Marketplace connection so we know which UI should be used for the user as a merchant + // Note: Eventually, all merchant will be migrated to the new SLM system and there will be no reason to show the old UI at all. + // Note: Some regions will not support the SLM cap for a while so we need to do that check for each teleport. + // *TODO : Suppress that line from here once the whole grid migrated to SLM and move it to idle_startup() (llstartup.cpp) + check_merchant_status(); } void LLAgent::handleTeleportFailed() diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index e76bd28c3..bb3317005 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -2270,7 +2270,7 @@ void LLAgentCamera::changeCameraToMouselook(BOOL animate) mMouselookTimer.reset(); gFocusMgr.setKeyboardFocus(NULL); - if (gSavedSettings.getBOOL("AONoStandsInMouselook")) LLFloaterAO::stopMotion(LLFloaterAO::getCurrentStandId(), FALSE,TRUE); + if (gSavedSettings.getBOOL("AONoStandsInMouselook")) LLFloaterAO::stopMotion(LLFloaterAO::getCurrentStandId(), true); updateLastCamera(); mCameraMode = CAMERA_MODE_MOUSELOOK; diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 446626b5e..bf5d47d0a 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -32,6 +32,7 @@ #include "llagentcamera.h" #include "llagentwearablesfetch.h" #include "llappearancemgr.h" +#include "llattachmentsmgr.h" #include "llcallbacklist.h" #include "llfloatercustomize.h" #include "llfolderview.h" diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 7f55b00c9..baa6809ea 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1568,9 +1568,11 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, break; case LLAssetType::AT_OBJECT: - { - rez_attachment(item_to_wear, NULL, replace); - } + { + LL_DEBUGS("Avatar") << "ATT wearing object. calling rez_attachment, item " << item_to_wear->getName() + << " id " << item_to_wear->getLinkedUUID() << LL_ENDL; + rez_attachment(item_to_wear, NULL, replace); + } break; default: continue; @@ -3759,7 +3761,7 @@ void RequestAgentUpdateAppearanceResponder::onRequestRequested() } // Actually send the request. - LL_DEBUGS("Avatar") << "Will send request for cof_version " << cof_version << LL_ENDL; + LL_DEBUGS("Avatar") << "ATT sending bake request for cof_version " << cof_version << LL_ENDL; mRetryPolicy->reset(); sendRequest(); } diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 2aee3a578..a0c7fc3d8 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -321,6 +321,7 @@ private: nullary_func_t mPostUpdateFunc; }; +// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-08-31 (Catznip-2.1) class LLUpdateAppearanceAndEditWearableOnDestroy: public LLInventoryCallback { public: diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 1950f5194..a10b6a6e3 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -165,6 +165,7 @@ // in save_settings_to_globals() #include "llbutton.h" #include "llcombobox.h" +#include "floaterlocalassetbrowse.h" #include "llstatusbar.h" #include "llsurface.h" #include "llvosky.h" @@ -773,7 +774,6 @@ bool LLAppViewer::init() writeSystemInfo(); - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -818,7 +818,14 @@ bool LLAppViewer::init() LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null)); LLUrlAction::setOpenURLExternalCallback(boost::bind(&LLWeb::loadURLExternal, _1, true, LLStringUtil::null)); LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor); - + + LL_INFOS("InitInfo") << "UI initialization is done." << LL_ENDL ; + + // Load translations for tooltips + LLFloater::initClass(); + + ///////////////////////////////////////////////// + LLToolMgr::getInstance(); // Initialize tool manager if not already instantiated ///////////////////////////////////////////////// @@ -884,7 +891,7 @@ bool LLAppViewer::init() std::ostringstream msg; msg << LLTrans::getString("MBUnableToAccessFile"); OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); - return 1; + return true; } LL_INFOS("InitInfo") << "Cache initialization is done." << LL_ENDL ; @@ -913,7 +920,7 @@ bool LLAppViewer::init() load_default_bindings(gSavedSettings.getBOOL("LiruUseZQSDKeys")); // If we don't have the right GL requirements, exit. - if (!gGLManager.mHasRequirements && !gNoRender) + if (!gGLManager.mHasRequirements) { // can't use an alert here since we're exiting and // all hell breaks lose. @@ -923,10 +930,9 @@ bool LLAppViewer::init() msg, LLStringUtil::null, OSMB_OK); - return 0; + return false; } -#if (_M_IX86_FP > 1 || defined(__SSE2__)) // Without SSE2 support we will crash almost immediately, warn here. if (!gSysCPU.hasSSE2()) { @@ -938,23 +944,8 @@ bool LLAppViewer::init() msg, LLStringUtil::null, OSMB_OK); - return 0; + return false; } -#elif (_M_IX86_FP == 1 || defined(__SSE__)) - // Without SSE support we will crash almost immediately, warn here. - if (!gSysCPU.hasSSE()) - { - // can't use an alert here since we're exiting and - // all hell breaks lose. - std::string msg = LNotificationTemplates::instance().getGlobalString("UnsupportedCPUSSE2"); - LLStringUtil::format(msg,LLTrans::getDefaultArgs()); - OSMessageBox( - msg, - LLStringUtil::null, - OSMB_OK); - return 0; - } -#endif // alert the user if they are using unsupported hardware if(!gSavedSettings.getBOOL("AlertedUnsupportedHardware")) @@ -1172,18 +1163,18 @@ bool LLAppViewer::mainLoop() BOOL restore_rendering_masks = FALSE; // Handle messages - while (!LLApp::isExiting()) + try { - LLFastTimer::nextFrame(); // Should be outside of any timer instances - - //clear call stack records - LL_CLEAR_CALLSTACKS(); - - //check memory availability information - checkMemory() ; - - try + while (!LLApp::isExiting()) { + LLFastTimer::nextFrame(); // Should be outside of any timer instances + + //clear call stack records + LL_CLEAR_CALLSTACKS(); + + //check memory availability information + checkMemory() ; + // Check if we need to restore rendering masks. if (restore_rendering_masks) { @@ -1236,12 +1227,10 @@ bool LLAppViewer::mainLoop() #endif //memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterMemLeak::getInstance(); - if(mem_leak_instance) + if (auto mem_leak_instance = LLFloaterMemLeak::getInstance()) { - mem_leak_instance->idle() ; - } + mem_leak_instance->idle(); + } // canonical per-frame event mainloop.post(newFrame); @@ -1424,54 +1413,29 @@ bool LLAppViewer::mainLoop() // LLAppViewer::getTextureFetch()->pause(); // Don't pause the fetch (IO) thread } //LLVFSThread::sLocal->pause(); // Prevent the VFS thread from running while rendering. - //LLLFSThread::sLocal->pause(); // Prevent the LFS thread from running while rendering. resumeMainloopTimeout(); pingMainloopTimeout("Main:End"); } } - catch(std::bad_alloc) - { - LLMemory::logMemoryInfo(TRUE) ; - //stop memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterMemLeak::getInstance(); - if(mem_leak_instance) - { - mem_leak_instance->stop() ; - LL_WARNS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ; - } - else - { - //output possible call stacks to log file. - LLError::LLCallStacks::print() ; - - LL_ERRS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ; - } - } - } - - // Save snapshot for next time, if we made it through initialization - if (STATE_STARTED == LLStartUp::getStartupState()) - { - try - { + // Save snapshot for next time, if we made it through initialization + if (STATE_STARTED == LLStartUp::getStartupState()) saveFinalSnapshot(); - } - catch(std::bad_alloc) - { - LL_WARNS() << "Bad memory allocation when saveFinalSnapshot() is called!" << LL_ENDL ; + } + catch(std::bad_alloc) + { + LLMemory::logMemoryInfo(TRUE); - //stop memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterMemLeak::getInstance(); - if(mem_leak_instance) - { - mem_leak_instance->stop() ; - } - } + //stop memory leaking simulation + if (auto mem_leak_instance = LLFloaterMemLeak::getInstance()) + mem_leak_instance->stop(); + + //output possible call stacks to log file. + LLError::LLCallStacks::print(); + + LL_ERRS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL; } delete gServicePump; @@ -1733,6 +1697,8 @@ bool LLAppViewer::cleanup() LLFloaterTeleportHistory::saveFile("teleport_history.xml"); + LocalAssetBrowser::deleteSingleton(); // + // save mute list. gMuteList used to also be deleted here too. LLMuteList::getInstance()->cache(gAgent.getID()); @@ -2474,12 +2440,11 @@ bool LLAppViewer::initConfiguration() // // Set the name of the window // - gWindowTitle = LLTrans::getString("APP_NAME"); + gWindowTitle = LLTrans::getString("APP_NAME") + llformat(" (%d) ", LLVersionInfo::getBuild()) #if LL_DEBUG - gWindowTitle += std::string(" [DEBUG] ") + gArgs; -#else - gWindowTitle += std::string(" ") + gArgs; + + "[DEBUG] " #endif + + gArgs; LLStringUtil::truncate(gWindowTitle, 255); //RN: if we received a URL, hand it off to the existing instance. @@ -2679,6 +2644,8 @@ void LLAppViewer::cleanupSavedSettings() { gSavedSettings.setF32("RenderFarClip", gAgentCamera.mDrawDistance); } + + LLSpeakerVolumeStorage::deleteSingleton(); } void LLAppViewer::removeCacheFiles(const std::string& file_mask) diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp index 21ca02e38..e556ec61b 100644 --- a/indra/newview/llattachmentsmgr.cpp +++ b/indra/newview/llattachmentsmgr.cpp @@ -39,6 +39,7 @@ #include "rlvhandler.h" #include "rlvlocks.h" // [/RLVa:KB] +#include const F32 COF_LINK_BATCH_TIME = 5.0F; const F32 MAX_ATTACHMENT_REQUEST_LIFETIME = 30.0F; @@ -478,9 +479,17 @@ void LLAttachmentsMgr::onAttachmentArrived(const LLUUID& inv_item_id) { LLTimer timer; 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) { - LLInventoryItem *item = gInventory.getItem(inv_item_id); LL_WARNS() << "ATT Attachment was unexpected or arrived after " << MAX_ATTACHMENT_REQUEST_LIFETIME << " seconds: " << (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL; } diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 9609d60ff..269c24956 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -546,20 +546,12 @@ void LLAvatarActions::csr(const LLUUID& id) if (!gCacheName->getFullName(id, name)) return; std::string url = "http://csr.lindenlab.com/agent/"; - - // slow and stupid, but it's late - S32 len = name.length(); - for (S32 i = 0; i < len; i++) + if (char* output = curl_easy_escape(nullptr, name.c_str(), name.length())) { - if (name[i] == ' ') - { - url += "%20"; - } - else - { - url += name[i]; - } + name = output; + curl_free(output); } + url += name; LLWeb::loadURL(url); } diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp index f11e228ba..75ab16c18 100644 --- a/indra/newview/llcontrolavatar.cpp +++ b/indra/newview/llcontrolavatar.cpp @@ -113,7 +113,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()) { diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index 7609358b5..3320e609b 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -387,6 +387,7 @@ void LLCurrencyUIManager::Impl::updateUI() } mPanel.childSetTextArg("currency_est", "[USD]", llformat("%#.2f", mSiteCurrencyEstimatedCost / 100.0)); + mPanel.childSetTextArg("currency_est", "[REALCURRENCY]", gHippoGridManager->getConnectedGrid()->getRealCurrencySymbol()); mPanel.childSetVisible("currency_est", mSiteCurrencyEstimated && mUserCurrencyBuy > 0); if (mPanel.childIsEnabled("buy_btn") diff --git a/indra/newview/lldebugmessagebox.cpp b/indra/newview/lldebugmessagebox.cpp index d485c7a9d..7073d9b05 100644 --- a/indra/newview/lldebugmessagebox.cpp +++ b/indra/newview/lldebugmessagebox.cpp @@ -56,25 +56,25 @@ LLDebugVarMessageBox::LLDebugVarMessageBox(const std::string& title, EDebugVarTy switch(var_type) { case VAR_TYPE_F32: - mSlider1 = new LLSliderCtrl(std::string("slider 1"), LLRect(20,130,190,110), title, NULL, 70, 130, TRUE, TRUE, FALSE, NULL, *((F32*)var), -100.f, 100.f, 0.1f, LLStringUtil::null); + mSlider1 = new LLSliderCtrl(std::string("slider 1"), LLRect(20,130,190,110), title, NULL, 70, 130, TRUE, TRUE, NULL, *((F32*)var), -100.f, 100.f, 0.1f, LLStringUtil::null); mSlider1->setPrecision(3); addChild(mSlider1); mSlider2 = NULL; mSlider3 = NULL; break; case VAR_TYPE_S32: - mSlider1 = new LLSliderCtrl(std::string("slider 1"), LLRect(20,100,190,80), title, NULL, 70, 130, TRUE, TRUE, FALSE, NULL, (F32)*((S32*)var), -255.f, 255.f, 1.f, LLStringUtil::null); + mSlider1 = new LLSliderCtrl(std::string("slider 1"), LLRect(20,100,190,80), title, NULL, 70, 130, TRUE, TRUE, NULL, (F32)*((S32*)var), -255.f, 255.f, 1.f, LLStringUtil::null); mSlider1->setPrecision(0); addChild(mSlider1); mSlider2 = NULL; mSlider3 = NULL; break; case VAR_TYPE_VEC3: - mSlider1 = new LLSliderCtrl(std::string("slider 1"), LLRect(20,130,190,110), std::string("x: "), NULL, 70, 130, TRUE, TRUE, FALSE, NULL, ((LLVector3*)var)->mV[VX], -100.f, 100.f, 0.1f, LLStringUtil::null); + mSlider1 = new LLSliderCtrl(std::string("slider 1"), LLRect(20,130,190,110), std::string("x: "), NULL, 70, 130, TRUE, TRUE, NULL, ((LLVector3*)var)->mV[VX], -100.f, 100.f, 0.1f, LLStringUtil::null); mSlider1->setPrecision(3); - mSlider2 = new LLSliderCtrl(std::string("slider 2"), LLRect(20,100,190,80), std::string("y: "), NULL, 70, 130, TRUE, TRUE, FALSE, NULL, ((LLVector3*)var)->mV[VY], -100.f, 100.f, 0.1f, LLStringUtil::null); + mSlider2 = new LLSliderCtrl(std::string("slider 2"), LLRect(20,100,190,80), std::string("y: "), NULL, 70, 130, TRUE, TRUE, NULL, ((LLVector3*)var)->mV[VY], -100.f, 100.f, 0.1f, LLStringUtil::null); mSlider2->setPrecision(3); - mSlider3 = new LLSliderCtrl(std::string("slider 3"), LLRect(20,70,190,50), std::string("z: "), NULL, 70, 130, TRUE, TRUE, FALSE, NULL, ((LLVector3*)var)->mV[VZ], -100.f, 100.f, 0.1f, LLStringUtil::null); + mSlider3 = new LLSliderCtrl(std::string("slider 3"), LLRect(20,70,190,50), std::string("z: "), NULL, 70, 130, TRUE, TRUE, NULL, ((LLVector3*)var)->mV[VZ], -100.f, 100.f, 0.1f, LLStringUtil::null); mSlider3->setPrecision(3); addChild(mSlider1); addChild(mSlider2); diff --git a/indra/newview/llenvmanager.cpp b/indra/newview/llenvmanager.cpp index 4abd7fc8e..d2fb204cc 100644 --- a/indra/newview/llenvmanager.cpp +++ b/indra/newview/llenvmanager.cpp @@ -505,7 +505,7 @@ void LLEnvManagerNew::onRegionSettingsResponse(const LLSD& content) mRegionSettingsChangeSignal(); // reset - if (!gHippoGridManager->getConnectedGrid()->isAurora()) // On Aurora, the region says when to refresh + if (!gHippoGridManager->getConnectedGrid()->isWhiteCore()) // On WhiteCore, the region says when to refresh mInterpNextChangeMessage = false; } @@ -704,7 +704,7 @@ void LLEnvManagerNew::onRegionChange() } } -// Aurora-sim windlight refresh +// WhiteCore windlight refresh class WindLightRefresh : public LLHTTPNode { /*virtual*/ void post(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp index 22b715590..b804b81be 100644 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -190,6 +190,20 @@ namespace mErrorCount = 0; makeRequest(); } + else if (mStatus == HTTP_NOT_FOUND) + { // Event polling for this server has been canceled. In + // some cases the server gets ahead of the viewer and will + // return a 404 error (Not Found) before the cancel event + // comes back in the queue + LL_WARNS("LLEventPollImpl") << "Canceling coroutine" << LL_ENDL; + stop(); + } + else if (mCode != CURLE_OK) + { + /// Some LLCore or LIBCurl error was returned. This is unlikely to be recoverable + LL_WARNS("LLEventPollImpl") << "Critical error from poll request returned from libraries. Canceling coroutine." << LL_ENDL; + stop(); + } else if (mErrorCount < MAX_EVENT_POLL_HTTP_ERRORS) { ++mErrorCount; @@ -213,6 +227,7 @@ namespace // They are essentially disconnected from the region even though some things may still work. // Since things won't get better until they relog we force a disconnect now. + /* Singu Note: There's no reason to disconnect, just because this failed a few too many times // *NOTE:Mani - The following condition check to see if this failing event poll // is attached to the Agent's main region. If so we disconnect the viewer. // Else... its a child region and we just leave the dead event poll stopped and @@ -222,6 +237,7 @@ namespace LL_WARNS() << "Forcing disconnect due to stalled main region event poll." << LL_ENDL; LLAppViewer::instance()->forceDisconnect(LLTrans::getString("AgentLostConnection")); } + */ } } diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp new file mode 100644 index 000000000..d578b6062 --- /dev/null +++ b/indra/newview/llfavoritesbar.cpp @@ -0,0 +1,1802 @@ +// 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 llfavoritesbar.cpp + * @brief LLFavoritesBarCtrl class implementation + * + * $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 "llviewerprecompiledheaders.h" +#include "llfavoritesbar.h" + +//#include "llfloaterreg.h" +#include "llfocusmgr.h" +#include "llinventory.h" +#include "lllandmarkactions.h" +//#include "lltoolbarview.h" +#include "lltrans.h" +#include "llmenugl.h" +//#include "lltooltip.h" +#include "lluictrlfactory.h" + +#include "llagent.h" +#include "llavatarnamecache.h" +#include "llclipboard.h" +#include "llinventorybridge.h" +#include "llinventoryclipboard.h" +#include "llinventoryfunctions.h" +//#include "llfloatersidepanelcontainer.h" +#include "llfloaterworldmap.h" +#include "lllandmarkactions.h" +//#include "lllogininstance.h" +#include "llnotificationsutil.h" +//#include "lltoggleablemenu.h" +#include "llviewerinventory.h" +#include "llviewermenu.h" +#include "hippogridmanager.h" //"llviewernetwork.h" +#include "lltooldraganddrop.h" +#include "llsdserialize.h" + +void open_landmark(LLViewerInventoryItem* inv_item, const std::string& title, BOOL show_keep_discard, const LLUUID& source_id, BOOL take_focus); + +static LLRegisterWidget r("favorites_bar"); + +const S32 DROP_DOWN_MENU_WIDTH = 250; +const S32 DROP_DOWN_MENU_TOP_PAD = 13; + +/** + * Helper for LLFavoriteLandmarkButton and LLFavoriteLandmarkMenuItem. + * Performing requests for SLURL for given Landmark ID + */ +class LLLandmarkInfoGetter +{ +public: + LLLandmarkInfoGetter() + : mLandmarkID(LLUUID::null), + mName("(Loading...)"), + mPosX(0), + mPosY(0), + mPosZ(0), + mLoaded(false) + { + mHandle.bind(this); + } + + void setLandmarkID(const LLUUID& id) { mLandmarkID = id; } + const LLUUID& getLandmarkId() const { return mLandmarkID; } + + const std::string& getName() + { + if(!mLoaded) + requestNameAndPos(); + + return mName; + } + + S32 getPosX() + { + if (!mLoaded) + requestNameAndPos(); + return mPosX; + } + + S32 getPosY() + { + if (!mLoaded) + requestNameAndPos(); + return mPosY; + } + + S32 getPosZ() + { + if (!mLoaded) + requestNameAndPos(); + return mPosZ; + } + +private: + /** + * Requests landmark data from server. + */ + void requestNameAndPos() + { + if (mLandmarkID.isNull()) + return; + + LLVector3d g_pos; + if(LLLandmarkActions::getLandmarkGlobalPos(mLandmarkID, g_pos)) + { + LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(g_pos, + boost::bind(&LLLandmarkInfoGetter::landmarkNameCallback, static_cast >(mHandle), _1, _2, _3, _4)); + } + } + + static void landmarkNameCallback(LLHandle handle, const std::string& name, S32 x, S32 y, S32 z) + { + LLLandmarkInfoGetter* getter = handle.get(); + if (getter) + { + getter->mPosX = x; + getter->mPosY = y; + getter->mPosZ = z; + getter->mName = name; + getter->mLoaded = true; + } + } + + LLUUID mLandmarkID; + std::string mName; + S32 mPosX; + S32 mPosY; + S32 mPosZ; + bool mLoaded; + LLRootHandle mHandle; +}; + +/** + * This class is needed to override LLButton default handleToolTip function and + * show SLURL as button tooltip. + * *NOTE: dzaporozhan: This is a workaround. We could set tooltips for buttons + * in createButtons function but landmark data is not available when Favorites Bar is + * created. Thats why we are requesting landmark data after + */ +class LLFavoriteLandmarkButton : public LLButton +{ +public: + + BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect) override + { + std::string region_name = mLandmarkInfoGetter.getName(); + + if (!region_name.empty()) + { + std::string extra_message = llformat("%s (%d, %d, %d)", region_name.c_str(), + mLandmarkInfoGetter.getPosX(), mLandmarkInfoGetter.getPosY(), mLandmarkInfoGetter.getPosZ()); + + msg = llformat("%s\n%s", getLabelSelected().c_str(), extra_message.c_str()); + *sticky_rect = calcScreenRect(); + } + return TRUE; + } + + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) override + { + LLFavoritesBarCtrl* fb = dynamic_cast(getParent()); + + if (fb) + { + fb->handleHover(x, y, mask); + } + + return LLButton::handleHover(x, y, mask); + } + + void setLandmarkID(const LLUUID& id){ mLandmarkInfoGetter.setLandmarkID(id); } + const LLUUID& getLandmarkId() const { return mLandmarkInfoGetter.getLandmarkId(); } + + void onMouseEnter(S32 x, S32 y, MASK mask) override + { + if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) + { + LLUICtrl::onMouseEnter(x, y, mask); + } + else + { + LLButton::onMouseEnter(x, y, mask); + } + } + +//protected: + LLFavoriteLandmarkButton(const LLButton::Params& p) : LLButton(p) {} + friend class LLUICtrlFactory; + +private: + LLLandmarkInfoGetter mLandmarkInfoGetter; +}; + +/** + * This class is needed to override LLMenuItemCallGL default handleToolTip function and + * show SLURL as button tooltip. + * *NOTE: dzaporozhan: This is a workaround. We could set tooltips for buttons + * in showDropDownMenu function but landmark data is not available when Favorites Bar is + * created. Thats why we are requesting landmark data after + */ +class LLFavoriteLandmarkMenuItem : public LLMenuItemCallGL +{ +public: + BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect) override + { + std::string region_name = mLandmarkInfoGetter.getName(); + if (!region_name.empty()) + { + msg = llformat("%s\n%s (%d, %d)", getLabel().c_str(), region_name.c_str(), mLandmarkInfoGetter.getPosX(), mLandmarkInfoGetter.getPosY()); + *sticky_rect = calcScreenRect(); + } + return TRUE; + } + + void setLandmarkID(const LLUUID& id){ mLandmarkInfoGetter.setLandmarkID(id); } + + BOOL handleMouseDown(S32 x, S32 y, MASK mask) override + { + if (mMouseDownSignal) + (*mMouseDownSignal)(this, x, y, mask); + return LLMenuItemCallGL::handleMouseDown(x, y, mask); + } + + BOOL handleMouseUp(S32 x, S32 y, MASK mask) override + { + if (mMouseUpSignal) + (*mMouseUpSignal)(this, x, y, mask); + return LLMenuItemCallGL::handleMouseUp(x, y, mask); + } + + BOOL handleHover(S32 x, S32 y, MASK mask) override + { + if (fb) + { + fb->handleHover(x, y, mask); + } + + return TRUE; + } + + void initFavoritesBarPointer(LLFavoritesBarCtrl* fb) { this->fb = fb; } + +//protected: + + LLFavoriteLandmarkMenuItem(const LLMenuItemCallGL::Params& p) : LLMenuItemCallGL(p.name(), p.label(), nullptr), fb(nullptr) {} + friend class LLUICtrlFactory; + +private: + LLLandmarkInfoGetter mLandmarkInfoGetter; + LLFavoritesBarCtrl* fb; +}; + +/** + * This class was introduced just for fixing the following issue: + * EXT-836 Nav bar: Favorites overflow menu passes left-mouse click through. + * We must explicitly handle drag and drop event by returning TRUE + * because otherwise LLToolDragAndDrop will initiate drag and drop operation + * with the world. + */ +class LLFavoriteLandmarkToggleableMenu : public LLMenuGL +{ +public: + BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) override + { + *accept = ACCEPT_NO; + return TRUE; + } + +//protected: + LLFavoriteLandmarkToggleableMenu(): + LLMenuGL("favorites menu") + { + } + + friend class LLUICtrlFactory; +}; + +/** + * This class is needed to update an item being copied to the favorites folder + * with a sort field value (required to save favorites bar's tabs order). + * See method handleNewFavoriteDragAndDrop for more details on how this class is used. + */ +class LLItemCopiedCallback : public LLInventoryCallback +{ +public: + LLItemCopiedCallback(S32 sortField): mSortField(sortField) {} + + void fire(const LLUUID& inv_item) override + { + LLViewerInventoryItem* item = gInventory.getItem(inv_item); + + if (item) + { + LLFavoritesOrderStorage::instance().setSortIndex(item, mSortField); + + item->setComplete(TRUE); + item->updateServer(FALSE); + + gInventory.updateItem(item); + gInventory.notifyObservers(); + LLFavoritesOrderStorage::instance().saveOrder(); + } + + LLView::getWindow()->setCursor(UI_CURSOR_ARROW); + } + +private: + S32 mSortField; +}; + +// updateButtons's helper +struct LLFavoritesSort +{ + // Sorting by creation date and name + // TODO - made it customizible using gSavedSettings + bool operator()(const LLViewerInventoryItem* const& a, const LLViewerInventoryItem* const& b) const + { + S32 sortField1 = LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID()); + S32 sortField2 = LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID()); + + if (!(sortField1 < 0 && sortField2 < 0)) + { + return sortField2 > sortField1; + } + + time_t first_create = a->getCreationDate(); + time_t second_create = b->getCreationDate(); + if (first_create == second_create) + { + return (LLStringUtil::compareDict(a->getName(), b->getName()) < 0); + } + else + { + return (first_create > second_create); + } + } +}; + +LLFavoritesBarCtrl::Params::Params() +: image_drag_indication("image_drag_indication"), + more_button("more_button"), + label("label") +{ +} + +LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) +: LLUICtrl(p), + mOverflowMenuHandle(), + mContextMenuHandle(), + mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), + mUpdateDropDownItems(true), + mRestoreOverflowMenu(false), + mImageDragIndication(p.image_drag_indication), + mShowDragMarker(FALSE), + mLandingTab(NULL) + , mLastTab(NULL) +, mTabsHighlightEnabled(TRUE) +{ + // Register callback for menus with current registrar (will be parent panel's registrar) + LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Favorites.DoToSelected", + boost::bind(&LLFavoritesBarCtrl::doToSelected, this, _2)); + + // Add this if we need to selectively enable items + LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("Favorites.EnableSelected", + boost::bind(&LLFavoritesBarCtrl::enableSelected, this, _2)); + + gInventory.addObserver(this); + + //make chevron button + mMoreTextBox = new LLTextBox(">>", "\u0032", 50); + mMoreTextBox->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + mMoreTextBox->setToolTip(LLStringExplicit("Show more of My Favorites")); + mMoreTextBox->setTabStop(false); + mMoreTextBox->setClickedCallback(boost::bind(&LLFavoritesBarCtrl::showDropDownMenu, this)); + addChild(mMoreTextBox); + + mDropDownItemsCount = 0; + + mBarLabel = new LLTextBox(p.label.name, p.label.label); + addChild(mBarLabel); +} + +LLFavoritesBarCtrl::~LLFavoritesBarCtrl() +{ + gInventory.removeObserver(this); + + if (mOverflowMenuHandle.get()) mOverflowMenuHandle.get()->die(); + if (mContextMenuHandle.get()) mContextMenuHandle.get()->die(); +} + +BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + *accept = ACCEPT_NO; + + LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); + if (LLToolDragAndDrop::SOURCE_AGENT != source && LLToolDragAndDrop::SOURCE_LIBRARY != source) return FALSE; + + switch (cargo_type) + { + + case DAD_LANDMARK: + { + /* + * add a callback to the end drag event. + * the callback will disconnet itself immediately after execution + * this is done because LLToolDragAndDrop is a common tool so it shouldn't + * be overloaded with redundant callbacks. + */ + if (!mEndDragConnection.connected()) + { + mEndDragConnection = LLToolDragAndDrop::getInstance()->setEndDragCallback(boost::bind(&LLFavoritesBarCtrl::onEndDrag, this)); + } + + // Copy the item into the favorites folder (if it's not already there). + LLInventoryItem *item = (LLInventoryItem *)cargo_data; + + if (LLFavoriteLandmarkButton* dest = dynamic_cast(findChildByLocalCoords(x, y))) + { + setLandingTab(dest); + } + else if (mLastTab && (x >= mLastTab->getRect().mRight)) + { + /* + * the condition dest == NULL can be satisfied not only in the case + * of dragging to the right from the last tab of the favbar. there is a + * small gap between each tab. if the user drags something exactly there + * then mLandingTab will be set to NULL and the dragged item will be pushed + * to the end of the favorites bar. this is incorrect behavior. that's why + * we need an additional check which excludes the case described previously + * making sure that the mouse pointer is beyond the last tab. + */ + setLandingTab(NULL); + } + + // check if we are dragging an existing item from the favorites bar + if (item && mDragItemId == item->getUUID()) + { + *accept = ACCEPT_YES_SINGLE; + + showDragMarker(TRUE); + + if (drop) + { + handleExistingFavoriteDragAndDrop(x, y); + } + } + else + { + const LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + if (item && item->getParentUUID() == favorites_id) + { + LL_WARNS("FavoritesBar") << "Attemt to copy a favorite item into the same folder." << LL_ENDL; + break; + } + + *accept = ACCEPT_YES_COPY_MULTI; + + showDragMarker(TRUE); + + if (drop) + { + if (mItems.empty()) + { + setLandingTab(NULL); + } + handleNewFavoriteDragAndDrop(item, favorites_id, x, y); + } + } + } + break; + default: + break; + } + + return TRUE; +} + +void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) +{ + // Identify the button hovered and the side to drop + LLFavoriteLandmarkButton* dest = dynamic_cast(mLandingTab); + bool insert_before = true; + if (!dest) + { + insert_before = false; + dest = dynamic_cast(mLastTab); + } + + // There is no need to handle if an item was dragged onto itself + if (dest && dest->getLandmarkId() == mDragItemId) + { + return; + } + + // Insert the dragged item in the right place + if (dest) + { + LLInventoryModel::updateItemsOrder(mItems, mDragItemId, dest->getLandmarkId(), insert_before); + } + else + { + // This can happen when the item list is empty + mItems.push_back(gInventory.getItem(mDragItemId)); + } + + LLFavoritesOrderStorage::instance().saveItemsOrder(mItems); + + LLMenuGL* menu = (LLMenuGL*) mOverflowMenuHandle.get(); + + if (menu && menu->getVisible()) + { + menu->setVisible(FALSE); + showDropDownMenu(); + } +} + +void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, const LLUUID& favorites_id, S32 x, S32 y) +{ + if (!item) return; + // Identify the button hovered and the side to drop + LLFavoriteLandmarkButton* dest = NULL; + bool insert_before = true; + if (!mItems.empty()) + { + // [MAINT-2386] When multiple landmarks are selected and dragged onto an empty favorites bar, + // the viewer would crash when casting mLastTab below, as mLastTab is still null when the + // second landmark is being added. + // To ensure mLastTab is valid, we need to call updateButtons() at the end of this function + dest = dynamic_cast(mLandingTab); + if (!dest) + { + insert_before = false; + dest = dynamic_cast(mLastTab); + } + } + + // There is no need to handle if an item was dragged onto itself + if (dest && dest->getLandmarkId() == mDragItemId) + { + return; + } + + LLPointer viewer_item = new LLViewerInventoryItem(item); + + // Insert the dragged item in the right place + if (dest) + { + insertItem(mItems, dest->getLandmarkId(), viewer_item, insert_before); + } + else + { + // This can happen when the item list is empty + mItems.push_back(viewer_item); + } + + int sortField = 0; + LLPointer cb; + + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (LLInventoryModel::item_array_t::iterator i = mItems.begin(); i != mItems.end(); ++i) + { + LLViewerInventoryItem* currItem = *i; + + if (currItem->getUUID() == item->getUUID()) + { + cb = new LLItemCopiedCallback(++sortField); + } + else + { + LLFavoritesOrderStorage::instance().setSortIndex(currItem, ++sortField); + + currItem->setComplete(TRUE); + currItem->updateServer(FALSE); + + gInventory.updateItem(currItem); + } + } + + LLToolDragAndDrop* tool_dad = LLToolDragAndDrop::getInstance(); + if (tool_dad->getSource() == LLToolDragAndDrop::SOURCE_NOTECARD) + { + viewer_item->setType(LLAssetType::AT_LANDMARK); + copy_inventory_from_notecard(favorites_id, + tool_dad->getObjectID(), + tool_dad->getSourceID(), + viewer_item.get(), + gInventoryCallbacks.registerCB(cb)); + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + favorites_id, + std::string(), + cb); + } + + // [MAINT-2386] Ensure the favorite button has been created and is valid. + // This also ensures that mLastTab will be valid when dropping multiple + // landmarks to an empty favorites bar. + updateButtons(); + + LL_INFOS("FavoritesBar") << "Copied inventory item #" << item->getUUID() << " to favorites." << LL_ENDL; +} + +//virtual +void LLFavoritesBarCtrl::changed(U32 mask) +{ + if (mFavoriteFolderId.isNull()) + { + mFavoriteFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + + if (mFavoriteFolderId.notNull()) + { + gInventory.fetchDescendentsOf(mFavoriteFolderId); + } + } + else + { + LLInventoryModel::item_array_t items; + LLInventoryModel::cat_array_t cats; + LLIsType is_type(LLAssetType::AT_LANDMARK); + gInventory.collectDescendentsIf(mFavoriteFolderId, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) + { + LLFavoritesOrderStorage::instance().getSLURL((*i)->getAssetUUID()); + } + + updateButtons(); + } +} + +//virtual +void LLFavoritesBarCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLUICtrl::reshape(width, height, called_from_parent); + updateButtons(); +} + +void LLFavoritesBarCtrl::draw() +{ + LLUICtrl::draw(); + + if (mShowDragMarker) + { + S32 w = mImageDragIndication->getWidth(); + S32 h = mImageDragIndication->getHeight(); + + if (mLandingTab) + { + // mouse pointer hovers over an existing tab + LLRect rect = mLandingTab->getRect(); + mImageDragIndication->draw(rect.mLeft, rect.getHeight(), w, h); + } + else if (mLastTab) + { + // mouse pointer hovers over the favbar empty space (right to the last tab) + LLRect rect = mLastTab->getRect(); + mImageDragIndication->draw(rect.mRight, rect.getHeight(), w, h); + } + // Once drawn, mark this false so we won't draw it again (unless we hit the favorite bar again) + mShowDragMarker = FALSE; + } +} + +const LLButton::Params& LLFavoritesBarCtrl::getButtonParams() +{ + static LLButton::Params button_params; + static bool params_initialized = false; + + if (!params_initialized) + { + button_params.image_overlay(LLUI::getUIImage("Favorite_Link_Over")) + .hover_glow_amount(0.15f) + .use_ellipses(true).tab_stop(false) + .name("favorites_bar_btn") + .follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM); + params_initialized = true; + } + + return button_params; +} + +void LLFavoritesBarCtrl::updateButtons() +{ + mItems.clear(); + + if (!collectFavoriteItems(mItems)) + { + return; + } + + const LLButton::Params& button_params = getButtonParams(); + + if(mItems.empty()) + { + mBarLabel->setVisible(TRUE); + } + else + { + mBarLabel->setVisible(FALSE); + } + const child_list_t* childs = getChildList(); + child_list_const_iter_t child_it = childs->begin(); + int first_changed_item_index = 0; + int rightest_point = getRect().mRight - mMoreTextBox->getRect().getWidth(); + //lets find first changed button + while (child_it != childs->end() && first_changed_item_index < mItems.size()) + { + LLFavoriteLandmarkButton* button = dynamic_cast (*child_it); + if (button) + { + const LLViewerInventoryItem *item = mItems[first_changed_item_index].get(); + if (item) + { + // an child's order and mItems should be same + if (button->getLandmarkId() != item->getUUID() // sort order has been changed + || button->getLabelSelected() != item->getName() // favorite's name has been changed + || button->getRect().mRight < rightest_point) // favbar's width has been changed + { + break; + } + } + first_changed_item_index++; + } + child_it++; + } + // now first_changed_item_index should contains a number of button that need to change + + if (first_changed_item_index <= mItems.size()) + { + // Rebuild the buttons only + // child_list_t is a linked list, so safe to erase from the middle if we pre-increment the iterator + + while (child_it != childs->end()) + { + //lets remove other landmarks button and rebuild it + child_list_const_iter_t cur_it = child_it++; + LLFavoriteLandmarkButton* button = + dynamic_cast (*cur_it); + if (button) + { + removeChild(button); + delete button; + } + } + // we have to remove ChevronButton to make sure that the last item will be LandmarkButton to get the right aligning + // keep in mind that we are cutting all buttons in space between the last visible child of favbar and ChevronButton + if (mMoreTextBox->getParent() == this) + { + removeChild(mMoreTextBox); + } + int last_right_edge = 0; + //calculate new buttons offset + if (getChildList()->size() > 0) + { + //find last visible child to get the rightest button offset + child_list_const_reverse_iter_t last_visible_it = std::find_if(childs->rbegin(), childs->rend(), + std::mem_fn(&LLView::getVisible)); + if(last_visible_it != childs->rend()) + { + last_right_edge = (*last_visible_it)->getRect().mRight; + } + } + //last_right_edge is saving coordinates + LLButton* last_new_button = NULL; + int j = first_changed_item_index; + for (; j < mItems.size(); j++) + { + last_new_button = createButton(mItems[j], button_params, last_right_edge); + if (!last_new_button) + { + break; + } + sendChildToBack(last_new_button); + last_right_edge = last_new_button->getRect().mRight; + + mLastTab = last_new_button; + } + mFirstDropDownItem = j; + // Chevron button + if (mFirstDropDownItem < mItems.size()) + { + // if updateButton had been called it means: + //or there are some new favorites, or width had been changed + // so if we need to display chevron button, we must update dropdown items too. + mUpdateDropDownItems = true; + S32 buttonHGap = button_params.rect.left; // default value + LLRect rect; + // Chevron button should stay right aligned + rect.setOriginAndSize(getRect().mRight - mMoreTextBox->getRect().getWidth() - buttonHGap, 0, + mMoreTextBox->getRect().getWidth(), + mMoreTextBox->getRect().getHeight()); + + addChild(mMoreTextBox); + mMoreTextBox->setRect(rect); + mMoreTextBox->setVisible(TRUE); + } + // Update overflow menu + LLMenuGL* overflow_menu = static_cast (mOverflowMenuHandle.get()); + if (overflow_menu && overflow_menu->getVisible() && (overflow_menu->getItemCount() != mDropDownItemsCount)) + { + overflow_menu->setVisible(FALSE); + if (mUpdateDropDownItems) + { + showDropDownMenu(); + } + } + } + else + { + mUpdateDropDownItems = false; + } + +} + +LLButton* LLFavoritesBarCtrl::createButton(const LLPointer item, const LLButton::Params& button_params, S32 x_offset) +{ + S32 def_button_width = button_params.rect.width; + S32 button_x_delta = button_params.rect.left; // default value + S32 curr_x = x_offset; + + /** + * WORKAROUND: + * There are some problem with displaying of fonts in buttons. + * Empty space or ellipsis might be displayed instead of last symbols, even though the width of the button is enough. + * The problem disappears if we pad the button with 20 pixels. + */ + int required_width = mFont->getWidth(item->getName()) + 20; + int width = required_width > def_button_width? def_button_width : required_width; + LLFavoriteLandmarkButton* fav_btn = NULL; + + // do we have a place for next button + double buttonHGap + mMoreTextBox ? + if(curr_x + width + 2*button_x_delta + mMoreTextBox->getRect().getWidth() > getRect().mRight ) + { + return NULL; + } + LLButton::Params fav_btn_params(button_params); + fav_btn = new LLFavoriteLandmarkButton(fav_btn_params); + if (NULL == fav_btn) + { + LL_WARNS("FavoritesBar") << "Unable to create LLFavoriteLandmarkButton widget: " << item->getName() << LL_ENDL; + return NULL; + } + + addChild(fav_btn); + + LLRect butt_rect (fav_btn->getRect()); + fav_btn->setLandmarkID(item->getUUID()); + butt_rect.setOriginAndSize(curr_x + button_x_delta, fav_btn->getRect().mBottom, width, fav_btn->getRect().getHeight()); + + fav_btn->setRect(butt_rect); + // change only left and save bottom + fav_btn->setFont(mFont); + fav_btn->setLabel(item->getName()); + fav_btn->setToolTip(item->getName()); + fav_btn->setCommitCallback(boost::bind(&LLFavoritesBarCtrl::onButtonClick, this, item->getUUID())); + fav_btn->setRightMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this, item->getUUID(), _1, _2, _3,_4 )); + + fav_btn->LLUICtrl::setMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseDown, this, item->getUUID(), _1, _2, _3, _4)); + fav_btn->LLUICtrl::setMouseUpCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseUp, this, item->getUUID(), _1, _2, _3, _4)); + + return fav_btn; +} + + +BOOL LLFavoritesBarCtrl::postBuild() +{ + // make the popup menu available + LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_favorites.xml", gMenuHolder); + if (!menu) + { + return FALSE; //menu = LLUICtrlFactory::getDefaultWidget("inventory_menu"); + } + menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor")); + mContextMenuHandle = menu->getHandle(); + + return TRUE; +} + +BOOL collectFavoriteItems(LLInventoryModel::item_array_t& items) +{ + + auto mFavoriteFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + if (mFavoriteFolderId.isNull()) + return FALSE; + + + LLInventoryModel::cat_array_t cats; + + LLIsType is_type(LLAssetType::AT_LANDMARK); + gInventory.collectDescendentsIf(mFavoriteFolderId, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + std::sort(items.begin(), items.end(), LLFavoritesSort()); + + BOOL needToSaveItemsOrder(const LLInventoryModel::item_array_t& items); + //if (needToSaveItemsOrder(items)) + { + S32 sortField = 0; + for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) + { + LLFavoritesOrderStorage::instance().setSortIndex((*i), ++sortField); + } + } + + return TRUE; +} +BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t& items) { return ::collectFavoriteItems(items); } + +void LLFavoritesBarCtrl::showDropDownMenu() +{ + if (mOverflowMenuHandle.isDead()) + { + createOverflowMenu(); + } + + LLMenuGL* menu = (LLMenuGL*)mOverflowMenuHandle.get(); + if (menu) + { + + if (mUpdateDropDownItems) + { + updateMenuItems(menu); + } + + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + //menu->setButtonRect(mMoreTextBox->getRect(), this); + positionAndShowMenu(menu); + mDropDownItemsCount = menu->getItemCount(); + } +} + +void LLFavoritesBarCtrl::createOverflowMenu() +{ + LLMenuGL* menu = new LLFavoriteLandmarkToggleableMenu(); + menu->setCanTearOff(false); + menu->setVisible(false); + menu->setScrollable(true); + //menu_p.max_scrollable_items = 10; + //menu_p.preferred_width = DROP_DOWN_MENU_WIDTH; + mOverflowMenuHandle = menu->getHandle(); +} + +void LLFavoritesBarCtrl::updateMenuItems(LLMenuGL* menu) +{ + menu->empty(); + + U32 widest_item = 0; + + for (S32 i = mFirstDropDownItem; i < mItems.size(); i++) + { + LLViewerInventoryItem* item = mItems.at(i); + const std::string& item_name = item->getName(); + + LLFavoriteLandmarkMenuItem::Params item_params; + item_params.name(item_name); + item_params.label(item_name); + + LLFavoriteLandmarkMenuItem *menu_item = new LLFavoriteLandmarkMenuItem(item_params); + menu_item->setMouseUpCallback(std::bind(&LLFavoritesBarCtrl::onButtonClick, this, item->getUUID())); + menu_item->initFavoritesBarPointer(this); + menu_item->setRightMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this, item->getUUID(), _1, _2, _3, _4)); + menu_item->LLUICtrl::setMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseDown, this, item->getUUID(), _1, _2, _3, _4)); + menu_item->LLUICtrl::setMouseUpCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseUp, this, item->getUUID(), _1, _2, _3, _4)); + menu_item->setLandmarkID(item->getUUID()); + + fitLabelWidth(menu_item); + + widest_item = llmax(widest_item, menu_item->getNominalWidth()); + + menu->addChild(menu_item); + } + + addOpenLandmarksMenuItem(menu); + mUpdateDropDownItems = false; +} + +void LLFavoritesBarCtrl::fitLabelWidth(LLMenuItemCallGL* menu_item) +{ + U32 max_width = llmin(DROP_DOWN_MENU_WIDTH, getRect().getWidth()); + std::string item_name = menu_item->getName(); + + // Check whether item name wider than menu + if (menu_item->getNominalWidth() > max_width) + { + S32 chars_total = item_name.length(); + S32 chars_fitted = 1; + menu_item->setLabel(LLStringExplicit("")); + S32 label_space = max_width - menu_item->getFont()->getWidth("...") - + menu_item->getNominalWidth();// This returns width of menu item with empty label (pad pixels) + + while (chars_fitted < chars_total + && menu_item->getFont()->getWidth(item_name, 0, chars_fitted) < label_space) + { + chars_fitted++; + } + chars_fitted--; // Rolling back one char, that doesn't fit + + menu_item->setLabel(item_name.substr(0, chars_fitted) + "..."); + } +} + +void LLFavoritesBarCtrl::addOpenLandmarksMenuItem(LLMenuGL* menu) +{ + std::string label_untrans = "Open landmarks"; + std::string label_transl; + bool translated = LLTrans::findString(label_transl, label_untrans); + + LLMenuItemCallGL* menu_item = new LLMenuItemCallGL(std::string("open_my_landmarks"), std::string(translated ? label_transl : label_untrans), nullptr); + void show_floater(const std::string& floater_name); + menu_item->setMouseUpCallback(std::bind(show_floater, "floater_inventory_favs.xml")); + + fitLabelWidth(menu_item); + + LLMenuItemSeparatorGL* separator = new LLMenuItemSeparatorGL; + + menu->addChild(separator); + menu->addChild(menu_item); +} + +void LLFavoritesBarCtrl::positionAndShowMenu(LLMenuGL* menu) +{ + U32 max_width = llmin(DROP_DOWN_MENU_WIDTH, getRect().getWidth()); + + S32 menu_x = getRect().getWidth() - max_width; + S32 menu_y = getParent()->getRect().mBottom - DROP_DOWN_MENU_TOP_PAD; + + /* Singu TODO: Side Toolbar? + // the menu should be offset of the right edge of the window + // so it's no covered by buttons in the right-side toolbar. + LLToolBar* right_toolbar = gToolBarView->getChild("toolbar_right"); + if (right_toolbar && right_toolbar->hasButtons()) + { + S32 toolbar_top = 0; + + if (LLView* top_border_panel = right_toolbar->getChild("button_panel")) + { + toolbar_top = top_border_panel->calcScreenRect().mTop; + } + + // Calculating the bottom (in screen coord) of the drop down menu + S32 menu_top = getParent()->getRect().mBottom - DROP_DOWN_MENU_TOP_PAD; + S32 menu_bottom = menu_top - menu->getRect().getHeight(); + S32 menu_bottom_screen = 0; + + localPointToScreen(0, menu_bottom, &menu_top, &menu_bottom_screen); + + if (menu_bottom_screen < toolbar_top) + { + menu_x -= right_toolbar->getRect().getWidth(); + } + } + */ + + LLMenuGL::showPopup(this, menu, menu_x, menu_y); +} + +void LLFavoritesBarCtrl::onButtonClick(LLUUID item_id) +{ + // We only have one Inventory, gInventory. Some day this should be better abstracted. + LLInvFVBridgeAction::doAction(item_id,&gInventory); +} + +void LLFavoritesBarCtrl::onButtonRightClick( LLUUID item_id,LLView* fav_button,S32 x,S32 y,MASK mask) +{ + mSelectedItemID = item_id; + + LLMenuGL* menu = (LLMenuGL*)mContextMenuHandle.get(); + if (!menu) + { + return; + } + + // Remember that the context menu was shown simultaneously with the overflow menu, + // so that we can restore the overflow menu when user clicks a context menu item + // (which hides the overflow menu). + { + LLView* overflow_menu = mOverflowMenuHandle.get(); + mRestoreOverflowMenu = overflow_menu && overflow_menu->getVisible(); + } + + // Release mouse capture so hover events go to the popup menu + // because this is happening during a mouse down. + gFocusMgr.setMouseCapture(NULL); + + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(fav_button, menu, x, y); +} + +BOOL LLFavoritesBarCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = childrenHandleRightMouseDown( x, y, mask) != NULL; + /*if(!handled && !gMenuHolder->hasVisibleMenu()) + { + show_navbar_context_menu(this,x,y); + handled = true; + }*/ + + return handled; +} +void copy_slurl_to_clipboard_cb(std::string& slurl) +{ + LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl)); + + LLSD args; + args["SLURL"] = slurl; + LLNotificationsUtil::add("CopySLURL", args); +} + + +bool LLFavoritesBarCtrl::enableSelected(const LLSD& userdata) +{ + std::string param = userdata.asString(); + + if (param == std::string("can_paste")) + { + return isClipboardPasteable(); + } + + return false; +} + +void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) +{ + std::string action = userdata.asString(); + LL_INFOS("FavoritesBar") << "Action = " << action << " Item = " << mSelectedItemID.asString() << LL_ENDL; + + LLViewerInventoryItem* item = gInventory.getItem(mSelectedItemID); + if (!item) + return; + + if (action == "open") + { + onButtonClick(item->getUUID()); + } + else if (action == "about") + { + LLSD key; + key["type"] = "landmark"; + key["id"] = mSelectedItemID; + + open_landmark(item, "Favorite", false, mSelectedItemID, true); + } + else if (action == "copy_slurl") + { + LLVector3d posGlobal; + LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); + + if (!posGlobal.isExactlyZero()) + { + LLLandmarkActions::getSLURLfromPosGlobal(posGlobal, copy_slurl_to_clipboard_cb); + } + } + else if (action == "show_on_map") + { + LLFloaterWorldMap* worldmap_instance = gFloaterWorldMap; + + LLVector3d posGlobal; + LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); + + if (!posGlobal.isExactlyZero() && worldmap_instance) + { + worldmap_instance->trackLocation(posGlobal); + LLFloaterWorldMap::show(true); + } + } + else if (action == "cut") + { + } + else if (action == "copy") + { + LLInventoryClipboard::instance().add(mSelectedItemID); + } + else if (action == "paste") + { + pasteFromClipboard(); + } + else if (action == "delete") + { + gInventory.removeItem(mSelectedItemID); + } + + // Pop-up the overflow menu again (it gets hidden whenever the user clicks a context menu item). + // See EXT-4217 and STORM-207. + LLMenuGL* menu = (LLMenuGL*) mOverflowMenuHandle.get(); + if (mRestoreOverflowMenu && menu && !menu->getVisible()) + { + menu->resetScrollPositionOnShow(false); + showDropDownMenu(); + menu->resetScrollPositionOnShow(true); + } +} + +BOOL LLFavoritesBarCtrl::isClipboardPasteable() const +{ + if (!LLInventoryClipboard::instance().hasContents()) + { + return FALSE; + } + + std::vector objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.size(); + for(S32 i = 0; i < count; i++) + { + const LLUUID &item_id = objects.at(i); + + // Can't paste folders + const LLInventoryCategory *cat = gInventory.getCategory(item_id); + if (cat) + { + return FALSE; + } + + const LLInventoryItem *item = gInventory.getItem(item_id); + if (item && LLAssetType::AT_LANDMARK != item->getType()) + { + return FALSE; + } + } + return TRUE; +} + +void LLFavoritesBarCtrl::pasteFromClipboard() const +{ + LLInventoryModel* model = &gInventory; + if(model && isClipboardPasteable()) + { + LLInventoryItem* item = NULL; + std::vector objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.size(); + LLUUID parent_id(mFavoriteFolderId); + for(S32 i = 0; i < count; i++) + { + item = model->getItem(objects.at(i)); + if (item) + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + parent_id, + std::string(), + LLPointer(NULL)); + } + } + } +} + +void LLFavoritesBarCtrl::onButtonMouseDown(LLUUID id, LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + // EXT-6997 (Fav bar: Pop-up menu for LM in overflow dropdown is kept after LM was dragged away) + // mContextMenuHandle.get() - is a pop-up menu (of items) in already opened dropdown menu. + // We have to check and set visibility of pop-up menu in such a way instead of using + // LLMenuHolderGL::hideMenus() because it will close both menus(dropdown and pop-up), but + // we need to close only pop-up menu while dropdown one should be still opened. + LLMenuGL* menu = (LLMenuGL*)mContextMenuHandle.get(); + if(menu && menu->getVisible()) + { + menu->setVisible(FALSE); + } + + mDragItemId = id; + mStartDrag = TRUE; + + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + + LLToolDragAndDrop::getInstance()->setDragStart(screenX, screenY); +} + +void LLFavoritesBarCtrl::onButtonMouseUp(LLUUID id, LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + mStartDrag = FALSE; + mDragItemId = LLUUID::null; +} + +void LLFavoritesBarCtrl::onEndDrag() +{ + mEndDragConnection.disconnect(); + + showDragMarker(FALSE); + mDragItemId = LLUUID::null; + LLView::getWindow()->setCursor(UI_CURSOR_ARROW); +} + +BOOL LLFavoritesBarCtrl::handleHover(S32 x, S32 y, MASK mask) +{ + if (mDragItemId != LLUUID::null && mStartDrag) + { + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + + if(LLToolDragAndDrop::getInstance()->isOverThreshold(screenX, screenY)) + { + LLToolDragAndDrop::getInstance()->beginDrag( + DAD_LANDMARK, mDragItemId, + LLToolDragAndDrop::SOURCE_LIBRARY); + + mStartDrag = FALSE; + + return LLToolDragAndDrop::getInstance()->handleHover(x, y, mask); + } + } + + return TRUE; +} + +LLUICtrl* LLFavoritesBarCtrl::findChildByLocalCoords(S32 x, S32 y) +{ + LLUICtrl* ctrl = NULL; + const child_list_t* list = getChildList(); + + for (child_list_const_iter_t i = list->begin(); i != list->end(); ++i) + { + // Look only for children that are favorite buttons + if ((*i)->getName() == "favorites_bar_btn") + { + LLRect rect = (*i)->getRect(); + // We consider a button hit if the cursor is left of the right side + // This makes the hit a bit less finicky than hitting directly on the button itself + if (x <= rect.mRight) + { + ctrl = dynamic_cast(*i); + break; + } + } + } + return ctrl; +} + +BOOL needToSaveItemsOrder(const LLInventoryModel::item_array_t& items) +{ + BOOL result = FALSE; + + // if there is an item without sort order field set, we need to save items order + for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) + { + if (LLFavoritesOrderStorage::instance().getSortIndex((*i)->getUUID()) < 0) + { + result = TRUE; + break; + } + } + + return result; +} +BOOL LLFavoritesBarCtrl::needToSaveItemsOrder(const LLInventoryModel::item_array_t& items) { return ::needToSaveItemsOrder(items); } + +void LLFavoritesBarCtrl::insertItem(LLInventoryModel::item_array_t& items, const LLUUID& dest_item_id, LLViewerInventoryItem* insertedItem, bool insert_before) +{ + // Get the iterator to the destination item + LLInventoryModel::item_array_t::iterator it_dest = LLInventoryModel::findItemIterByUUID(items, dest_item_id); + if (it_dest == items.end()) + return; + + // Go to the next element if one wishes to insert after the dest element + if (!insert_before) + { + ++it_dest; + } + + // Insert the source item in the right place + if (it_dest != items.end()) + { + items.insert(it_dest, insertedItem); + } + else + { + // Append to the list if it_dest reached the end + items.push_back(insertedItem); + } +} + +const std::string LLFavoritesOrderStorage::SORTING_DATA_FILE_NAME = "landmarks_sorting.xml"; +const S32 LLFavoritesOrderStorage::NO_INDEX = -1; + +void LLFavoritesOrderStorage::setSortIndex(const LLViewerInventoryItem* inv_item, S32 sort_index) +{ + mSortIndexes[inv_item->getUUID()] = sort_index; + mIsDirty = true; + getSLURL(inv_item->getAssetUUID()); +} + +S32 LLFavoritesOrderStorage::getSortIndex(const LLUUID& inv_item_id) +{ + sort_index_map_t::const_iterator it = mSortIndexes.find(inv_item_id); + if (it != mSortIndexes.end()) + { + return it->second; + } + return NO_INDEX; +} + +void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& inv_item_id) +{ + mSortIndexes.erase(inv_item_id); + mIsDirty = true; +} + +void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) +{ + slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id); + if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached + + LLLandmark* lm = gLandmarkList.getAsset(asset_id, + boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1)); + if (lm) + { + LL_DEBUGS("FavoritesBar") << "landmark for " << asset_id << " already loaded" << LL_ENDL; + onLandmarkLoaded(asset_id, lm); + } +} + +// static +std::string LLFavoritesOrderStorage::getStoredFavoritesFilename() +{ + std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); + + return (user_dir.empty() ? "" + : gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "stored_favorites_" + + gHippoGridManager->getCurrentGridName() + + ".xml") + ); +} + +// static +void LLFavoritesOrderStorage::destroyClass() +{ + LLFavoritesOrderStorage::instance().cleanup(); + + + std::string old_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + llifstream file; + file.open(old_filename.c_str()); + if (file.is_open()) + { + file.close(); + std::string new_filename = getStoredFavoritesFilename(); + LL_INFOS("FavoritesBar") << "moving favorites from old name '" << old_filename + << "' to new name '" << new_filename << "'" + << LL_ENDL; + LLFile::rename(old_filename,new_filename); + } + + if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) + { + LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); + } + else + { + LLFavoritesOrderStorage::instance().removeFavoritesRecordOfUser(); + } +} + +std::string LLFavoritesOrderStorage::getSavedOrderFileName() +{ + // If we quit from the login screen we will not have an SL account + // name. Don't try to save, otherwise we'll dump a file in + // C:\Program Files\SecondLife\ or similar. JC + std::string user_dir = gDirUtilp->getLindenUserDir(); + return (user_dir.empty() ? "" : gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME)); +} + +void LLFavoritesOrderStorage::load() +{ + // load per-resident sorting information + std::string filename = getSavedOrderFileName(); + LLSD settings_llsd; + llifstream file; + file.open(filename.c_str()); + if (file.is_open()) + { + LLSDSerialize::fromXML(settings_llsd, file); + LL_INFOS("FavoritesBar") << "loaded favorites order from '" << filename << "' " + << (settings_llsd.isMap() ? "" : "un") << "successfully" + << LL_ENDL; + file.close(); + } + else + { + LL_WARNS("FavoritesBar") << "unable to open favorites order file at '" << filename << "'" << LL_ENDL; + } + + for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); + iter != settings_llsd.endMap(); ++iter) + { + mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); + } +} + +void LLFavoritesOrderStorage::saveFavoritesSLURLs() +{ + // Do not change the file if we are not logged in yet. + if (gAgentID.isNull()) //LLLoginInstance::getInstance()->authSuccess()) + { + LL_WARNS("FavoritesBar") << "Cannot save favorites: not logged in" << LL_ENDL; + return; + } + + std::string filename = getStoredFavoritesFilename(); + if (!filename.empty()) + { + llifstream in_file; + in_file.open(filename.c_str()); + LLSD fav_llsd; + if (in_file.is_open()) + { + LLSDSerialize::fromXML(fav_llsd, in_file); + LL_INFOS("FavoritesBar") << "loaded favorites from '" << filename << "' " + << (fav_llsd.isMap() ? "" : "un") << "successfully" + << LL_ENDL; + in_file.close(); + } + else + { + LL_WARNS("FavoritesBar") << "unable to open favorites from '" << filename << "'" << LL_ENDL; + } + + const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + + LLSD user_llsd; + for (LLInventoryModel::item_array_t::const_iterator it = items.cbegin(); it != items.cend(); ++it) + { + if (it->get()->getActualType() != LLAssetType::AT_LANDMARK) continue; + LLSD value; + value["name"] = (*it)->getName(); + value["asset_id"] = (*it)->getAssetUUID(); + + slurls_map_t::const_iterator slurl_iter = mSLURLs.find(value["asset_id"]); + if (slurl_iter != mSLURLs.cend()) + { + LL_DEBUGS("FavoritesBar") << "Saving favorite: idx=" << LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID()) + << ", SLURL=" << slurl_iter->second + << ", value=" << value << LL_ENDL; + value["slurl"] = slurl_iter->second; + user_llsd[LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID())] = value; + } + else + { + LL_WARNS("FavoritesBar") << "Not saving favorite " << value["name"] << ": no matching SLURL" << LL_ENDL; + } + } + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + // Note : use the "John Doe" and not the "john.doe" version of the name + // as we'll compare it with the stored credentials in the login panel. + fav_llsd[av_name.getUserName()] = user_llsd; + + llofstream file; + file.open(filename.c_str()); + if ( file.is_open() ) + { + LLSDSerialize::toPrettyXML(fav_llsd, file); + LL_INFOS("FavoritesBar") << "saved favorites for '" << av_name.getUserName() + << "' to '" << filename << "' " + << LL_ENDL; + file.close(); + } + else + { + LL_WARNS("FavoritesBar") << "unable to open favorites storage for '" << av_name.getUserName() + << "' at '" << filename << "' " + << LL_ENDL; + } + } +} + +void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() +{ + std::string filename = getStoredFavoritesFilename(); + if (!filename.empty()) + { + LLSD fav_llsd; + llifstream file; + file.open(filename.c_str()); + if (file.is_open()) + { + LLSDSerialize::fromXML(fav_llsd, file); + file.close(); + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + // Note : use the "John Doe" and not the "john.doe" version of the name. + // See saveFavoritesSLURLs() here above for the reason why. + if (fav_llsd.has(av_name.getUserName())) + { + LL_INFOS("FavoritesBar") << "Removed favorites for " << av_name.getUserName() << LL_ENDL; + fav_llsd.erase(av_name.getUserName()); + } + + llofstream out_file; + out_file.open(filename.c_str()); + if ( out_file.is_open() ) + { + LLSDSerialize::toPrettyXML(fav_llsd, out_file); + LL_INFOS("FavoritesBar") << "saved favorites to '" << filename << "' " + << LL_ENDL; + out_file.close(); + } + } + } +} + +void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) +{ + if (landmark) + { + LL_DEBUGS("FavoritesBar") << "landmark for " << asset_id << " loaded" << LL_ENDL; + LLVector3d pos_global; + if (!landmark->getGlobalPos(pos_global)) + { + // If global position was unknown on first getGlobalPos() call + // it should be set for the subsequent calls. + landmark->getGlobalPos(pos_global); + } + + if (!pos_global.isExactlyZero()) + { + LL_DEBUGS("FavoritesBar") << "requesting slurl for landmark " << asset_id << LL_ENDL; + LLLandmarkActions::getSLURLfromPosGlobal(pos_global, + boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); + } + } +} + +void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) +{ + LL_DEBUGS("FavoritesBar") << "Saving landmark SLURL '" << slurl << "' for " << asset_id << LL_ENDL; + mSLURLs[asset_id] = slurl; +} + +void LLFavoritesOrderStorage::save() +{ + if (mIsDirty) + { + // something changed, so save it + std::string filename = LLFavoritesOrderStorage::getInstance()->getSavedOrderFileName(); + if (!filename.empty()) + { + LLSD settings_llsd; + + for(sort_index_map_t::const_iterator iter = mSortIndexes.begin(); iter != mSortIndexes.end(); ++iter) + { + settings_llsd[iter->first.asString()] = iter->second; + } + + llofstream file; + file.open(filename.c_str()); + if ( file.is_open() ) + { + LLSDSerialize::toPrettyXML(settings_llsd, file); + LL_INFOS("FavoritesBar") << "saved favorites order to '" << filename << "' " << LL_ENDL; + } + else + { + LL_WARNS("FavoritesBar") << "failed to open favorites order file '" << filename << "' " << LL_ENDL; + } + } + else + { + LL_DEBUGS("FavoritesBar") << "no user directory available to store favorites order file" << LL_ENDL; + } + } +} + + +void LLFavoritesOrderStorage::cleanup() +{ + // nothing to clean + if (!mIsDirty) return; + + const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + + IsNotInFavorites is_not_in_fav(items); + + sort_index_map_t aTempMap; + //copy unremoved values from mSortIndexes to aTempMap + std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), + inserter(aTempMap, aTempMap.begin()), + is_not_in_fav); + + //Swap the contents of mSortIndexes and aTempMap + mSortIndexes.swap(aTempMap); +} + +// See also LLInventorySort where landmarks in the Favorites folder are sorted. +class LLViewerInventoryItemSort +{ +public: + bool operator()(const LLPointer& a, const LLPointer& b) const + { + return LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID()) + < LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID()); + } +}; + +void LLFavoritesOrderStorage::saveOrder() +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLIsType is_type(LLAssetType::AT_LANDMARK); + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + std::sort(items.begin(), items.end(), LLViewerInventoryItemSort()); + saveItemsOrder(items); +} + +void LLFavoritesOrderStorage::saveItemsOrder( const LLInventoryModel::item_array_t& items ) +{ + + int sortField = 0; + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) + { + LLViewerInventoryItem* item = *i; + + setSortIndex(item, ++sortField); + + item->setComplete(TRUE); + item->updateServer(FALSE); + + gInventory.updateItem(item); + + // Tell the parent folder to refresh its sort order. + gInventory.addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); + } + + gInventory.notifyObservers(); +} + + +// * @param source_item_id - LLUUID of the source item to be moved into new position +// * @param target_item_id - LLUUID of the target item before which source item should be placed. +void LLFavoritesOrderStorage::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + //LLIsType is_type(LLAssetType::AT_LANDMARK); // Singu Note: We can have anything here~ + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + gInventory.collectDescendents(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH/*, is_type*/); + + // ensure items are sorted properly before changing order. EXT-3498 + std::sort(items.begin(), items.end(), LLViewerInventoryItemSort()); + + // update order + gInventory.updateItemsOrder(items, source_item_id, target_item_id); + + saveItemsOrder(items); +} + +void AddFavoriteLandmarkCallback::fire(const LLUUID& inv_item_id) +{ + if (mTargetLandmarkId.isNull()) return; + + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(inv_item_id, mTargetLandmarkId); +} +// EOF diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h new file mode 100644 index 000000000..ef6f55c5e --- /dev/null +++ b/indra/newview/llfavoritesbar.h @@ -0,0 +1,274 @@ +/** + * @file llfavoritesbar.h + * @brief LLFavoritesBarCtrl base class + * + * $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_LLFAVORITESBARCTRL_H +#define LL_LLFAVORITESBARCTRL_H + +#include "llbutton.h" +#include "lluictrl.h" +#include "lltextbox.h" + +#include "llinventoryobserver.h" +#include "llinventorymodel.h" +#include "llviewerinventory.h" +#include "llui.h" //"llinitdestroyclass.h" + +class LLMenuItemCallGL; +class LLMenuGL; + +class LLFavoritesBarCtrl : public LLUICtrl, public LLInventoryObserver +{ +public: + struct Params : public LLInitParam::Block + { + Optional image_drag_indication; + Optional more_button; + Optional label; + Params(); + }; + +protected: + LLFavoritesBarCtrl(const Params&); + friend class LLUICtrlFactory; +public: + virtual ~LLFavoritesBarCtrl(); + + /*virtual*/ BOOL postBuild() override; + + /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) override; + + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) override; + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override; + // LLInventoryObserver observer trigger + void changed(U32 mask) override; + void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) override; + void draw() override; + + void showDragMarker(BOOL show) { mShowDragMarker = show; } + void setLandingTab(LLUICtrl* tab) { mLandingTab = tab; } + +protected: + void updateButtons(); + LLButton* createButton(const LLPointer item, const LLButton::Params& button_params, S32 x_offset ); + const LLButton::Params& getButtonParams(); + BOOL collectFavoriteItems(LLInventoryModel::item_array_t &items); + + void onButtonClick(LLUUID id); + void onButtonRightClick(LLUUID id,LLView* button,S32 x,S32 y,MASK mask); + + void onButtonMouseDown(LLUUID id, LLUICtrl* button, S32 x, S32 y, MASK mask); + void onOverflowMenuItemMouseDown(LLUUID id, LLUICtrl* item, S32 x, S32 y, MASK mask); + void onButtonMouseUp(LLUUID id, LLUICtrl* button, S32 x, S32 y, MASK mask); + + void onEndDrag(); + + bool enableSelected(const LLSD& userdata); + void doToSelected(const LLSD& userdata); + BOOL isClipboardPasteable() const; + void pasteFromClipboard() const; + + void showDropDownMenu(); + + LLHandle mOverflowMenuHandle; + LLHandle mContextMenuHandle; + + LLUUID mFavoriteFolderId; + const LLFontGL *mFont; + S32 mFirstDropDownItem; + S32 mDropDownItemsCount; + bool mUpdateDropDownItems; + bool mRestoreOverflowMenu; + + LLUUID mSelectedItemID; + + LLUIImage* mImageDragIndication; + +private: + /* + * Helper function to make code more readable. It handles all drag and drop + * operations of the existing favorites items on the favorites bar. + */ + void handleExistingFavoriteDragAndDrop(S32 x, S32 y); + + /* + * Helper function to make code more readable. It handles all drag and drop + * operations of the new landmark to the favorites bar. + */ + void handleNewFavoriteDragAndDrop(LLInventoryItem *item, const LLUUID& favorites_id, S32 x, S32 y); + + // finds a control under the specified LOCAL point + LLUICtrl* findChildByLocalCoords(S32 x, S32 y); + + // checks if the current order of the favorites items must be saved + BOOL needToSaveItemsOrder(const LLInventoryModel::item_array_t& items); + + /** + * inserts an item identified by insertedItemId BEFORE an item identified by beforeItemId. + * this function assumes that an item identified by insertedItemId doesn't exist in items array. + */ + void insertItem(LLInventoryModel::item_array_t& items, const LLUUID& dest_item_id, LLViewerInventoryItem* insertedItem, bool insert_before); + + // finds an item by it's UUID in the items array + LLInventoryModel::item_array_t::iterator findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id); + + void createOverflowMenu(); + + void updateMenuItems(LLMenuGL* menu); + + // Fits menu item label width with favorites menu width + void fitLabelWidth(LLMenuItemCallGL* menu_item); + + void addOpenLandmarksMenuItem(LLMenuGL* menu); + + void positionAndShowMenu(LLMenuGL* menu); + + BOOL mShowDragMarker; + LLUICtrl* mLandingTab; + LLUICtrl* mLastTab; + LLTextBox* mMoreTextBox; + LLTextBox* mBarLabel; + + LLUUID mDragItemId; + BOOL mStartDrag; + LLInventoryModel::item_array_t mItems; + + BOOL mTabsHighlightEnabled; + + boost::signals2::connection mEndDragConnection; +}; + +/** + * Class to store sorting order of favorites landmarks in a local file. EXT-3985. + * It replaced previously implemented solution to store sort index in landmark's name as a "@" prefix. + * Data are stored in user home directory. + */ +class LLFavoritesOrderStorage : public LLSingleton + , public LLDestroyClass +{ + friend class LLSingleton; + LLFavoritesOrderStorage(); //LLSINGLETON(LLFavoritesOrderStorage); + ~LLFavoritesOrderStorage() { save(); } + LOG_CLASS(LLFavoritesOrderStorage); +public: + /** + * Sets sort index for specified with LLUUID favorite landmark + */ + void setSortIndex(const LLViewerInventoryItem* inv_item, S32 sort_index); + + /** + * Gets sort index for specified with LLUUID favorite landmark + */ + S32 getSortIndex(const LLUUID& inv_item_id); + void removeSortIndex(const LLUUID& inv_item_id); + + void getSLURL(const LLUUID& asset_id); + + // Saves current order of the passed items using inventory item sort field. + // Resets 'items' sort fields and saves them on server. + // Is used to save order for Favorites folder. + void saveItemsOrder(const LLInventoryModel::item_array_t& items); + + void saveOrder(); + + void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); + + /** + * Implementation of LLDestroyClass. Calls cleanup() instance method. + * + * It is important this callback is called before gInventory is cleaned. + * For now it is called from LLAppViewer::cleanup() -> LLAppViewer::disconnectViewer(), + * Inventory is cleaned later from LLAppViewer::cleanup() after LLAppViewer::disconnectViewer() is called. + * @see cleanup() + */ + static void destroyClass(); + + const static S32 NO_INDEX; +private: + /** + * Removes sort indexes for items which are not in Favorites bar for now. + */ + void cleanup(); + + const static std::string SORTING_DATA_FILE_NAME; + std::string getSavedOrderFileName(); + static std::string getStoredFavoritesFilename(); + + void load(); + void save(); + + void saveFavoritesSLURLs(); + + // Remove record of current user's favorites from file on disk. + void removeFavoritesRecordOfUser(); + + void onLandmarkLoaded(const LLUUID& asset_id, class LLLandmark* landmark); + void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); + + typedef std::map sort_index_map_t; + sort_index_map_t mSortIndexes; + + typedef std::map slurls_map_t; + slurls_map_t mSLURLs; + std::set mMissingSLURLs; + bool mIsDirty; + + struct IsNotInFavorites + { + IsNotInFavorites(const LLInventoryModel::item_array_t& items) + : mFavoriteItems(items) + { + + } + + /** + * Returns true if specified item is not found among inventory items + */ + bool operator()(const sort_index_map_t::value_type& id_index_pair) const + { + LLPointer item = gInventory.getItem(id_index_pair.first); + if (item.isNull()) return true; + + LLInventoryModel::item_array_t::const_iterator found_it = + std::find(mFavoriteItems.begin(), mFavoriteItems.end(), item); + + return found_it == mFavoriteItems.end(); + } + private: + LLInventoryModel::item_array_t mFavoriteItems; + }; + +}; + +inline +LLFavoritesOrderStorage::LLFavoritesOrderStorage() : + mIsDirty(false) +{ load(); } + +#endif // LL_LLFAVORITESBARCTRL_H diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 42f46fb2e..096df5cde 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -421,6 +421,9 @@ void LLFeatureManager::parseGPUTable(std::string filename) else { LL_WARNS("RenderInit") << "GPU '" << rawRenderer << "' not recognized" << LL_ENDL; + mGPUString = rawRenderer; + mGPUClass = EGPUClass::GPU_CLASS_3; + mGPUSupported = true; } } diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index 48bf7a836..7c991e4d4 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -144,7 +144,7 @@ LLFloaterAbout::LLFloaterAbout() __DATE__, __TIME__, LLVersionInfo::getChannel().c_str())); support_widget->appendColoredText(version, FALSE, FALSE, gColors.getColor("TextFgReadOnlyColor")); - support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, viewer_link_style); + support_widget->appendText(LLTrans::getString("ReleaseNotes"), false, false, viewer_link_style); std::string support; support.append("\n\n"); @@ -209,7 +209,7 @@ LLFloaterAbout::LLFloaterAbout() { LLStyleSP server_link_style(new LLStyle(*viewer_link_style)); server_link_style->setLinkHREF(url); - support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, server_link_style); + support_widget->appendText(LLTrans::getString("ReleaseNotes"), false, false, server_link_style); } support = "\n\n"; diff --git a/indra/newview/llfloateravatarlist.cpp b/indra/newview/llfloateravatarlist.cpp index 7c67b9a3d..cbd8d9e92 100644 --- a/indra/newview/llfloateravatarlist.cpp +++ b/indra/newview/llfloateravatarlist.cpp @@ -70,7 +70,7 @@ const S32& radar_namesystem() namespace { - void chat_avatar_status(const std::string& name, const LLUUID& key, ERadarStatType type, bool entering, const F32& dist) + void chat_avatar_status(const std::string& name, const LLUUID& key, ERadarStatType type, bool entering, const F32& dist, bool flood = false) { static LLCachedControl radar_chat_alerts(gSavedSettings, "RadarChatAlerts"); if (!radar_chat_alerts) return; @@ -82,6 +82,8 @@ namespace } // if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMETAGS)) return; // RLVa:LF Don't announce people are around when blind, that cheats the system. + static LLCachedControl radar_alert_flood(gSavedSettings, "RadarAlertFlood"); + if (flood && !radar_alert_flood) return; static LLCachedControl radar_alert_sim(gSavedSettings, "RadarAlertSim"); static LLCachedControl radar_alert_draw(gSavedSettings, "RadarAlertDraw"); static LLCachedControl radar_alert_shout_range(gSavedSettings, "RadarAlertShoutRange"); @@ -96,11 +98,11 @@ namespace case STAT_TYPE_DRAW: if (radar_alert_draw) args["[RANGE]"] = LLTrans::getString("draw_distance"); break; case STAT_TYPE_SHOUTRANGE: if (radar_alert_shout_range) args["[RANGE]"] = LLTrans::getString("shout_range"); break; case STAT_TYPE_CHATRANGE: if (radar_alert_chat_range) args["[RANGE]"] = LLTrans::getString("chat_range"); break; - case STAT_TYPE_AGE: if (radar_alert_age) chat.mText = name + " " + LLTrans::getString("has_triggered_your_avatar_age_alert") + "."; break; + case STAT_TYPE_AGE: if (radar_alert_age) chat.mText = name + ' ' + LLTrans::getString("has_triggered_your_avatar_age_alert") + '.'; break; default: llassert(type); break; } args["[NAME]"] = name; - args["[ACTION]"] = LLTrans::getString(entering ? "has_entered" : "has_left"); + args["[ACTION]"] = LLTrans::getString((entering ? "has_entered" : "has_left") + (flood ? "_flood" : LLStringUtil::null)); if (args.find("[RANGE]") != args.end()) chat.mText = LLTrans::getString("radar_alert_template", args); else if (chat.mText.empty()) return; @@ -110,6 +112,7 @@ namespace if (radar_show_dist) chat.mText += llformat(" (%.2fm)", dist); } chat.mFromName = name; + chat.mFromID = key; if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) // RLVa:LF - No way! chat.mURL = llformat("secondlife:///app/agent/%s/about", key.asString().c_str()); chat.mSourceType = CHAT_SOURCE_SYSTEM; @@ -140,62 +143,75 @@ LLAvatarListEntry::LLAvatarListEntry(const LLUUID& id, const std::string& name, LLAvatarPropertiesProcessor& inst(LLAvatarPropertiesProcessor::instance()); inst.addObserver(mID, this); inst.sendAvatarPropertiesRequest(mID); + inst.sendAvatarNotesRequest(mID); } LLAvatarListEntry::~LLAvatarListEntry() { - setPosition(mPosition, F32_MIN, false); // Dead and gone + static LLCachedControl radar_alert_flood_leaving(gSavedSettings, "RadarAlertFloodLeaving"); + bool cleanup = LLFloaterAvatarList::isCleanup(); + if (radar_alert_flood_leaving || !cleanup) + { + setPosition(mPosition, F32_MIN, false, cleanup); // Dead and gone + } LLAvatarPropertiesProcessor::instance().removeObserver(mID, this); } // virtual void LLAvatarListEntry::processProperties(void* data, EAvatarProcessorType type) { - if (type == APT_PROPERTIES) + // If one wanted more information that gets displayed on profiles to be displayed, here would be the place to do it. + switch(type) { - LLAvatarPropertiesProcessor& inst(LLAvatarPropertiesProcessor::instance()); - inst.removeObserver(mID, this); - const LLAvatarData* pAvatarData = static_cast(data); - if (pAvatarData && (pAvatarData->avatar_id != LLUUID::null)) - { - using namespace boost::gregorian; - int year, month, day; - sscanf(pAvatarData->born_on.c_str(),"%d/%d/%d",&month,&day,&year); - try + case APT_PROPERTIES: + if (mAge == -1) { - mAge = (day_clock::local_day() - date(year, month, day)).days(); - } - catch(const std::exception&) - { - LL_WARNS() << "Failed to extract age from APT_PROPERTIES for " << mID << ", received \"" << pAvatarData->born_on << "\". Requesting properties again." << LL_ENDL; - inst.addObserver(mID, this); - inst.sendAvatarPropertiesRequest(mID); - return; - } - if (!mStats[STAT_TYPE_AGE] && mAge >= 0) //Only announce age once per entry. - { - static const LLCachedControl sAvatarAgeAlertDays(gSavedSettings, "AvatarAgeAlertDays"); - if ((U32)mAge < sAvatarAgeAlertDays) + LLAvatarPropertiesProcessor& inst(LLAvatarPropertiesProcessor::instance()); + const LLAvatarData* pAvatarData = static_cast(data); + if (pAvatarData && (pAvatarData->avatar_id.notNull())) { - chat_avatar_status(mName, mID, STAT_TYPE_AGE, mStats[STAT_TYPE_AGE] = true, (mPosition - gAgent.getPositionGlobal()).magVec()); + using namespace boost::gregorian; + int year, month, day; + if (sscanf(pAvatarData->born_on.c_str(),"%d/%d/%d",&month,&day,&year) == 3) + try + { + mAge = (day_clock::local_day() - date(year, month, day)).days(); + } + catch(const std::out_of_range&) {} // date throws this, so we didn't assign + + if (mAge >= 0) + { + static const LLCachedControl sAvatarAgeAlertDays(gSavedSettings, "AvatarAgeAlertDays"); + if (!mStats[STAT_TYPE_AGE] && (U32)mAge < sAvatarAgeAlertDays) //Only announce age once per entry. + chat_avatar_status(mName, mID, STAT_TYPE_AGE, mStats[STAT_TYPE_AGE] = true, (mPosition - gAgent.getPositionGlobal()).magVec()); + } + else // Something failed, resend request + { + LL_WARNS() << "Failed to extract age from APT_PROPERTIES for " << mID << ", received \"" << pAvatarData->born_on << "\". Requesting properties again." << LL_ENDL; + inst.sendAvatarPropertiesRequest(mID); + } } } - // If one wanted more information that gets displayed on profiles to be displayed, here would be the place to do it. - } + break; + case APT_NOTES: + if (const LLAvatarNotes* pAvatarNotes = static_cast(data)) + mNotes = !pAvatarNotes->notes.empty(); + break; + default: break; } } -void LLAvatarListEntry::setPosition(const LLVector3d& position, const F32& dist, bool drawn) +void LLAvatarListEntry::setPosition(const LLVector3d& position, const F32& dist, bool drawn, bool flood) { mPosition = position; bool here(dist != F32_MIN); // F32_MIN only if dead bool this_sim(here && (gAgent.getRegion()->pointInRegionGlobal(position) || !(LLWorld::getInstance()->positionRegionValidGlobal(position)))); - if (this_sim != mStats[STAT_TYPE_SIM]) chat_avatar_status(mName, mID, STAT_TYPE_SIM, mStats[STAT_TYPE_SIM] = this_sim, dist); - if (drawn != mStats[STAT_TYPE_DRAW]) chat_avatar_status(mName, mID, STAT_TYPE_DRAW, mStats[STAT_TYPE_DRAW] = drawn, dist); + if (this_sim != mStats[STAT_TYPE_SIM]) chat_avatar_status(mName, mID, STAT_TYPE_SIM, mStats[STAT_TYPE_SIM] = this_sim, dist, flood); + if (drawn != mStats[STAT_TYPE_DRAW]) chat_avatar_status(mName, mID, STAT_TYPE_DRAW, mStats[STAT_TYPE_DRAW] = drawn, dist, flood); bool shoutrange(here && dist < LFSimFeatureHandler::getInstance()->shoutRange()); - if (shoutrange != mStats[STAT_TYPE_SHOUTRANGE]) chat_avatar_status(mName, mID, STAT_TYPE_SHOUTRANGE, mStats[STAT_TYPE_SHOUTRANGE] = shoutrange, dist); + if (shoutrange != mStats[STAT_TYPE_SHOUTRANGE]) chat_avatar_status(mName, mID, STAT_TYPE_SHOUTRANGE, mStats[STAT_TYPE_SHOUTRANGE] = shoutrange, dist, flood); bool chatrange(here && dist < LFSimFeatureHandler::getInstance()->sayRange()); - if (chatrange != mStats[STAT_TYPE_CHATRANGE]) chat_avatar_status(mName, mID, STAT_TYPE_CHATRANGE, mStats[STAT_TYPE_CHATRANGE] = chatrange, dist); + if (chatrange != mStats[STAT_TYPE_CHATRANGE]) chat_avatar_status(mName, mID, STAT_TYPE_CHATRANGE, mStats[STAT_TYPE_CHATRANGE] = chatrange, dist, flood); } void LLAvatarListEntry::resetName(const bool& hide_tags, const bool& anon_names, const std::string& hidden) @@ -240,6 +256,8 @@ LLFloaterAvatarList::LLFloaterAvatarList() : LLFloater(std::string("radar")), LLFloaterAvatarList::~LLFloaterAvatarList() { + mCleanup = true; + mAvatars.clear(); } //static @@ -315,7 +333,7 @@ namespace { bool handleEvent(LLPointer event, const LLSD& userdata) { - LLFloaterAvatarList::instance().setFocusAvatar(get_focused_list_id_selected()); + LLFloaterAvatarList::setFocusAvatar(get_focused_list_id_selected()); return true; } }; @@ -401,6 +419,7 @@ BOOL LLFloaterAvatarList::postBuild() gSavedSettings.getControl("RadarColumnAltitudeHidden")->getSignal()->connect(boost::bind(&LLFloaterAvatarList::assessColumns, this)); gSavedSettings.getControl("RadarColumnActivityHidden")->getSignal()->connect(boost::bind(&LLFloaterAvatarList::assessColumns, this)); gSavedSettings.getControl("RadarColumnVoiceHidden")->getSignal()->connect(boost::bind(&LLFloaterAvatarList::assessColumns, this)); + gSavedSettings.getControl("RadarColumnNotesHidden")->getSignal()->connect(boost::bind(&LLFloaterAvatarList::assessColumns, this)); gSavedSettings.getControl("RadarColumnAgeHidden")->getSignal()->connect(boost::bind(&LLFloaterAvatarList::assessColumns, this)); gSavedSettings.getControl("RadarColumnTimeHidden")->getSignal()->connect(boost::bind(&LLFloaterAvatarList::assessColumns, this)); @@ -413,7 +432,7 @@ BOOL LLFloaterAvatarList::postBuild() mAvatarList->setSortChangedCallback(boost::bind(&LLFloaterAvatarList::onAvatarSortingChanged,this)); for (LLViewerRegion* region : LLWorld::instance().getRegionList()) { - updateAvatarList(region); + updateAvatarList(region, true); } assessColumns(); @@ -445,6 +464,21 @@ void col_helper(const bool hide, LLCachedControl &setting, LLScrollListColu } } +enum AVATARS_COLUMN_ORDER +{ + LIST_MARK, + LIST_AVATAR_NAME, + LIST_DISTANCE, + LIST_POSITION, + LIST_ALTITUDE, + LIST_ACTIVITY, + LIST_VOICE, + LIST_NOTES, + LIST_AGE, + LIST_TIME, + LIST_CLIENT, +}; + //Macro to reduce redundant lines. Preprocessor concatenation and stringizing avoids bloat that //wrapping in a class would create. #define BIND_COLUMN_TO_SETTINGS(col, name)\ @@ -459,6 +493,7 @@ void LLFloaterAvatarList::assessColumns() BIND_COLUMN_TO_SETTINGS(LIST_ALTITUDE,Altitude); BIND_COLUMN_TO_SETTINGS(LIST_ACTIVITY,Activity); BIND_COLUMN_TO_SETTINGS(LIST_VOICE,Voice); + BIND_COLUMN_TO_SETTINGS(LIST_NOTES,Notes); BIND_COLUMN_TO_SETTINGS(LIST_AGE,Age); BIND_COLUMN_TO_SETTINGS(LIST_TIME,Time); @@ -515,7 +550,7 @@ const F32& radar_range_radius() return radius; } -void LLFloaterAvatarList::updateAvatarList(const LLViewerRegion* region) +void LLFloaterAvatarList::updateAvatarList(const LLViewerRegion* region, bool first) { // Check whether updates are enabled if (!mUpdate) @@ -561,7 +596,7 @@ void LLFloaterAvatarList::updateAvatarList(const LLViewerRegion* region) } // Announce position - entry->setPosition(position, (position - mypos).magVec(), avatarp); + entry->setPosition(position, (position - mypos).magVec(), avatarp, first); // Mark as typing if they are typing if (avatarp && avatarp->isTyping()) entry->setActivity(LLAvatarListEntry::ACTIVITY_TYPING); @@ -582,13 +617,13 @@ void LLFloaterAvatarList::updateAvatarList(const LLViewerRegion* region) U32 num_ids = 0; while(!announce_keys.empty()) { - ids << "," << announce_keys.front().asString(); + ids << ',' << announce_keys.front().asString(); ++num_ids; if (ids.tellp() > 200) { send_keys_message(transact_num, num_ids, ids.str()); ids.seekp(num_ids = 0); - ids.str(""); + ids.str(LLStringUtil::null); } announce_keys.pop(); } @@ -903,6 +938,12 @@ void LLFloaterAvatarList::refreshAvatarList() element.columns.add(voice); } + static const LLCachedControl hide_notes("RadarColumnNotesHidden"); + if (!hide_notes) + { + element.columns.add(LLScrollListCell::Params().column("notes").type("checkbox").enabled(false).value(entry->mNotes)); + } + static const LLCachedControl hide_age("RadarColumnAgeHidden"); if (!hide_age) { @@ -1092,7 +1133,6 @@ void LLFloaterAvatarList::trackAvatar(const LLAvatarListEntry* entry) const LLAvatarListEntry* LLFloaterAvatarList::getAvatarEntry(const LLUUID& avatar) const { - if (avatar.isNull()) return NULL; av_list_t::const_iterator iter = std::find_if(mAvatars.begin(),mAvatars.end(),LLAvatarListEntry::uuidMatch(avatar)); return (iter != mAvatars.end()) ? iter->get() : NULL; } @@ -1149,9 +1189,16 @@ void LLFloaterAvatarList::removeFocusFromAll() } } +// static void LLFloaterAvatarList::setFocusAvatar(const LLUUID& id) { if (!gAgentCamera.lookAtObject(id, false) && !lookAtAvatar(id)) return; + if (auto inst = getIfExists()) + inst->setFocusAvatarInternal(id); +} + +void LLFloaterAvatarList::setFocusAvatarInternal(const LLUUID& id) +{ av_list_t::iterator iter = std::find_if(mAvatars.begin(),mAvatars.end(),LLAvatarListEntry::uuidMatch(id)); if (iter == mAvatars.end()) return; removeFocusFromAll(); @@ -1255,13 +1302,13 @@ void LLFloaterAvatarList::sendKeys() const for (U32 i = 0; i < regionp->mMapAvatarIDs.size(); ++i) { - ids << "," << regionp->mMapAvatarIDs.at(i); + ids << ',' << regionp->mMapAvatarIDs.at(i); ++num_ids; if (ids.tellp() > 200) { send_keys_message(transact_num, num_ids, ids.str()); ids.seekp(num_ids = 0); - ids.str(""); + ids.str(LLStringUtil::null); } } if (num_ids > 0) send_keys_message(transact_num, num_ids, ids.str()); @@ -1400,7 +1447,7 @@ std::string LLFloaterAvatarList::getSelectedNames(const std::string& separator) std::string LLFloaterAvatarList::getSelectedName() const { LLAvatarListEntry* entry = getAvatarEntry(getSelectedID()); - return entry ? entry->getName() : ""; + return entry ? entry->getName() : LLStringUtil::null; } LLUUID LLFloaterAvatarList::getSelectedID() const diff --git a/indra/newview/llfloateravatarlist.h b/indra/newview/llfloateravatarlist.h index 93230f9a5..233eb4618 100644 --- a/indra/newview/llfloateravatarlist.h +++ b/indra/newview/llfloateravatarlist.h @@ -79,7 +79,7 @@ enum ACTIVITY_TYPE * Update world position. * Affects age. */ - void setPosition(const LLVector3d& position, const F32& dist, bool drawn); + void setPosition(const LLVector3d& position, const F32& dist, bool drawn, bool flood = false); const LLVector3d& getPosition() const { return mPosition; } @@ -143,6 +143,7 @@ private: bool mMarked; bool mFocused; bool mIsInList; + bool mNotes = false; int mAge; /** @@ -202,7 +203,7 @@ public: /** * @brief Updates the internal avatar list with the currently present avatars. */ - void updateAvatarList(const class LLViewerRegion* region); + void updateAvatarList(const class LLViewerRegion* region, bool first = false); /** * @brief Refresh avatar list (display) @@ -239,20 +240,6 @@ public: // when a line editor loses keyboard focus, it is committed. // commit callbacks are named onCommitWidgetName by convention. //void onCommitBaz(LLUICtrl* ctrl, void *userdata); - - enum AVATARS_COLUMN_ORDER - { - LIST_MARK, - LIST_AVATAR_NAME, - LIST_DISTANCE, - LIST_POSITION, - LIST_ALTITUDE, - LIST_ACTIVITY, - LIST_VOICE, - LIST_AGE, - LIST_TIME, - LIST_CLIENT, - }; typedef boost::function avlist_command_t; @@ -264,7 +251,7 @@ public: /** * @brief Focus camera on specified avatar */ - void setFocusAvatar(const LLUUID& id); + static void setFocusAvatar(const LLUUID& id); /** * @brief Focus camera on previous avatar @@ -318,8 +305,6 @@ public: static bool onConfirmRadarChatKeys(const LLSD& notification, const LLSD& response ); - static void callbackIdle(void *userdata); - void doCommand(avlist_command_t cmd, bool single = false) const; /** @@ -330,14 +315,22 @@ public: */ void expireAvatarList(const std::list& ids); void updateAvatarSorting(); + static bool isCleanup() + { + const auto& inst = getIfExists(); + return inst && inst->mCleanup; + } private: + void setFocusAvatarInternal(const LLUUID& id); + /** * @brief Pointer to the avatar scroll list */ LLScrollListCtrl* mAvatarList; av_list_t mAvatars; bool mDirtyAvatarSorting; + bool mCleanup = false; /** * @brief true when Updating diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index 95c683582..b0607210f 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -45,6 +45,7 @@ #include "llcombobox.h" #include "lltextparser.h" #include "lltrans.h" +#include "llurlregistry.h" #include "llwindow.h" // project include @@ -94,7 +95,6 @@ LLFloaterChat::LLFloaterChat(const LLSD& seed) LLTextEditor* history_editor_with_mute = getChild("Chat History Editor with mute"); getChild("show mutes")->setCommitCallback(boost::bind(&LLFloaterChat::onClickToggleShowMute, this, _2, getChild("Chat History Editor"), history_editor_with_mute)); - history_editor_with_mute->setVisible(false); getChild("chat_history_open")->setCommitCallback(boost::bind(show_log_browser, "chat", "chat")); } @@ -201,7 +201,7 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& if (chat.mSourceType == CHAT_SOURCE_OBJECT) { LLStringUtil::trim(chat.mFromName); - if (!chat.mFromName.length()) + if (chat.mFromName.empty()) { chat.mFromName = LLTrans::getString("Unnamed"); line = chat.mFromName + line; @@ -212,19 +212,19 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& bool is_irc = italicize && chat.mChatStyle == CHAT_STYLE_IRC; // If the chat line has an associated url, link it up to the name. if (!chat.mURL.empty() - && (line.length() > chat.mFromName.length() && line.find(chat.mFromName,0) == 0)) + && boost::algorithm::starts_with(line, chat.mFromName)) { - std::string start_line = line.substr(0, chat.mFromName.length() + 1); - line = line.substr(chat.mFromName.length() + 1); + line = line.substr(chat.mFromName.length()); LLStyleSP sourceStyle = LLStyleMap::instance().lookup(chat.mFromID, chat.mURL); sourceStyle->mItalic = is_irc; - edit->appendStyledText(start_line, false, prepend_newline, sourceStyle); + edit->appendText(chat.mFromName, false, prepend_newline, sourceStyle, false); prepend_newline = false; } LLStyleSP style(new LLStyle); style->setColor(color); style->mItalic = is_irc; - edit->appendStyledText(line, false, prepend_newline, style); + style->mBold = chat.mChatType == CHAT_TYPE_SHOUT; + edit->appendText(line, false, prepend_newline, style, false); } void log_chat_text(const LLChat& chat) @@ -339,8 +339,6 @@ void LLFloaterChat::setHistoryCursorAndScrollToEnd() //static void LLFloaterChat::onClickToggleShowMute(bool show_mute, LLTextEditor* history_editor, LLTextEditor* history_editor_with_mute) { - history_editor->setVisible(!show_mute); - history_editor_with_mute->setVisible(show_mute); (show_mute ? history_editor_with_mute : history_editor)->setCursorAndScrollToEnd(); } @@ -387,7 +385,19 @@ void LLFloaterChat::addChat(const LLChat& chat, // We display anything if it's not an IM. If it's an IM, check pref... if ( !from_instant_message || gSavedSettings.getBOOL("IMInChatConsole") ) { - gConsole->addConsoleLine(chat.mText, text_color); + // Replace registered urls in the console so it looks right. + std::string chit(chat.mText), // Read through this + chat; // Add parts to this + LLUrlMatch match; + while (!chit.empty() && LLUrlRegistry::instance().findUrl(chit, match)) + { + const auto start(match.getStart()), length(match.getEnd()+1-start); + if (start > 0) chat += chit.substr(0, start); // Add up to the start of the match + chat += match.getLabel() + match.getQuery(); // Add the label and the query + chit.erase(0, start+length); // remove the url match and all before it + } + if (!chit.empty()) chat += chit; // Add any leftovers + gConsole->addConsoleLine(chat, text_color); } } diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index 657920701..63aa904dd 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -55,8 +55,7 @@ #include "statemachine/aifilepicker.h" #include "hippogridmanager.h" -//#include "aixmllindengenepool.h" -#include "aifile.h" +#include "aixmllindengenepool.h" using namespace LLAvatarAppearanceDefines; @@ -119,9 +118,9 @@ LLFloaterCustomize::LLFloaterCustomize() gInventory.addObserver(mInventoryObserver); LLOutfitObserver& outfit_observer = LLOutfitObserver::instance(); - outfit_observer.addBOFReplacedCallback(boost::bind(&LLFloaterCustomize::refreshCurrentOutfitName, this, "")); - outfit_observer.addBOFChangedCallback(boost::bind(&LLFloaterCustomize::refreshCurrentOutfitName, this, "")); - outfit_observer.addCOFChangedCallback(boost::bind(&LLFloaterCustomize::refreshCurrentOutfitName, this, "")); + outfit_observer.addBOFReplacedCallback(boost::bind(&LLFloaterCustomize::refreshCurrentOutfitName, this, LLStringUtil::null)); + outfit_observer.addBOFChangedCallback(boost::bind(&LLFloaterCustomize::refreshCurrentOutfitName, this, LLStringUtil::null)); + outfit_observer.addCOFChangedCallback(boost::bind(&LLFloaterCustomize::refreshCurrentOutfitName, this, LLStringUtil::null)); LLCallbackMap::map_t factory_map; const std::string &invalid_name = LLWearableType::getTypeName(LLWearableType::WT_INVALID); @@ -238,7 +237,11 @@ void LLFloaterCustomize::editWearable(LLViewerWearable* wearable, bool disable_c { if(!wearable) return; - LLFloaterCustomize::getInstance()->setCurrentWearableType(wearable->getType(), disable_camera_switch); + auto& inst = LLFloaterCustomize::instance(); + inst.setCurrentWearableType(wearable->getType(), disable_camera_switch); + U32 index(0); + gAgentWearables.getWearableIndex(wearable, index); + static_cast(inst.mTabContainer->getCurrentPanel())->setWearableIndex(index); } //static @@ -314,7 +317,6 @@ void LLFloaterCustomize::onBtnImport() void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) { -#if 0 if (!filepicker->hasFilename()) { // User canceled import. @@ -457,7 +459,6 @@ void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) { AIAlert::add("AIXMLImportWearableTypeMismatch", args("[TYPE]", label)("[ARCHETYPENAME]", wearable_types)); } -#endif } // reX: new function @@ -502,7 +503,6 @@ void LLFloaterCustomize::onBtnExport() //static void LLFloaterCustomize::onBtnExport_continued(LLViewerWearable* edit_wearable, AIFilePicker* filepicker) { -#if 0 if (!filepicker->hasFilename()) { // User canceled export. @@ -514,9 +514,7 @@ void LLFloaterCustomize::onBtnExport_continued(LLViewerWearable* edit_wearable, bool success = false; try { - AIFile outfile(filename, "wb"); - - AIXMLLindenGenepool linden_genepool(outfile); + AIXMLLindenGenepool linden_genepool(filename); linden_genepool.child(edit_wearable->getArchetype()); success = true; @@ -530,7 +528,6 @@ void LLFloaterCustomize::onBtnExport_continued(LLViewerWearable* edit_wearable, { AIAlert::add_modal("AIXMLExportSuccess", AIArgs("[FILE]", filename)); } -#endif } void LLFloaterCustomize::onBtnOk() @@ -677,7 +674,8 @@ const S32 HEADER_HEIGHT = 3 * (LINE_HEIGHT + LLFLOATER_VPAD) + (2 * LLPANEL_BORD void LLFloaterCustomize::wearablesChanged(LLWearableType::EType type) { llassert( type < LLWearableType::WT_COUNT ); - gSavedSettings.setU32("AvatarSex", (gAgentAvatarp->getSex() == SEX_MALE) ); + if (type == LLWearableType::WT_SHAPE) + gSavedSettings.setU32("AvatarSex", (gAgentAvatarp->getSex() == SEX_MALE)); LLPanelEditWearable* panel = mWearablePanelList[ type ]; if( panel ) diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp index b024b0eee..7e5861276 100644 --- a/indra/newview/llfloatergodtools.cpp +++ b/indra/newview/llfloatergodtools.cpp @@ -556,7 +556,7 @@ void LLPanelRegionTools::onSaveState(void* userdata) gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gMessageSystem->nextBlockFast(_PREHASH_DataBlock); - gMessageSystem->addStringFast(_PREHASH_Filename, NULL); + gMessageSystem->addStringFast(_PREHASH_Filename, nullptr); gAgent.sendReliableMessage(); } } @@ -1354,7 +1354,7 @@ void LLPanelRequestTools::onClickRequest() if(dest == SELECTION) { std::string req =getChild("request")->getValue(); - req = req.substr(0, req.find_first_of(" ")); + req = req.substr(0, req.find_first_of(' ')); std::string param = getChild("parameter")->getValue(); LLSelectMgr::getInstance()->sendGodlikeRequest(req, param); } @@ -1401,11 +1401,11 @@ void LLPanelRequestTools::sendRequest(const LLHost& host) host, FALSE, terrain_download_done, - NULL); + nullptr); } else { - req = req.substr(0, req.find_first_of(" ")); + req = req.substr(0, req.find_first_of(' ')); sendRequest(req, getChild("parameter")->getValue().asString(), host); } } diff --git a/indra/newview/llfloaterlandmark.cpp b/indra/newview/llfloaterlandmark.cpp index 00d7cd86c..d94546a08 100644 --- a/indra/newview/llfloaterlandmark.cpp +++ b/indra/newview/llfloaterlandmark.cpp @@ -104,7 +104,7 @@ BOOL LLFloaterLandmark::postBuild() mInventoryPanel->setAllowMultiSelect(FALSE); // store this filter as the default one - mInventoryPanel->getRootFolder()->getFilter()->markDefault(); + mInventoryPanel->getRootFolder()->getFilter().markDefault(); // Commented out to stop opening all folders with textures mInventoryPanel->openDefaultFolderForType(LLAssetType::AT_LANDMARK); diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp new file mode 100644 index 000000000..e51cbd6c7 --- /dev/null +++ b/indra/newview/llfloatermarketplacelistings.cpp @@ -0,0 +1,967 @@ +/** + * @file llfloatermarketplacelistings.cpp + * @brief Implementation of the marketplace listings floater and panels + * @author merov@lindenlab.com + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatermarketplacelistings.h" + +//#include "llfloaterreg.h" +#include "llfiltereditor.h" +#include "llfolderview.h" +#include "llinventorybridge.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llinventoryobserver.h" +#include "llinventoryfunctions.h" +#include "llmarketplacefunctions.h" +//#include "llnotificationhandler.h" +//#include "llnotificationmanager.h" +#include "llnotificationsutil.h" +//#include "llsidepaneliteminfo.h" +#include "lltextbox.h" +#include "lltrans.h" + +///---------------------------------------------------------------------------- +/// LLPanelMarketplaceListings +///---------------------------------------------------------------------------- + +static LLPanelInjector t_panel_status("llpanelmarketplacelistings"); + +LLPanelMarketplaceListings::LLPanelMarketplaceListings() +: mRootFolder(NULL) +, mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME) +, mFilterListingFoldersOnly(false) +{ +} + +LLPanelMarketplaceListings::~LLPanelMarketplaceListings() { delete mMenu; } + +void showMenu(LLView* button, LLMenuGL* menu) +{ + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + const auto& rect = button->getRect(); + LLMenuGL::showPopup(button->getParent(), menu, rect.mLeft, rect.mBottom); +} + +BOOL LLPanelMarketplaceListings::postBuild() +{ + childSetAction("add_btn", boost::bind(&LLPanelMarketplaceListings::onAddButtonClicked, this)); + childSetAction("audit_btn", boost::bind(&LLPanelMarketplaceListings::onAuditButtonClicked, this)); + mMenu = LLUICtrlFactory::instance().buildMenu("menu_marketplace_view.xml", LLMenuGL::sMenuContainer); + auto sort = getChild("sort_btn"); + sort->setCommitCallback(boost::bind(showMenu, _1, mMenu)); + + mFilterEditor = getChild("filter_editor"); + mFilterEditor->setCommitCallback(boost::bind(&LLPanelMarketplaceListings::onFilterEdit, this, _2)); + + mAuditBtn = getChild("audit_btn"); + mAuditBtn->setEnabled(FALSE); + + return LLPanel::postBuild(); +} + +BOOL LLPanelMarketplaceListings::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + LLView * handled_view = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + BOOL handled = (handled_view != NULL); + // Special case the drop zone + if (handled && (handled_view->getName() == "marketplace_drop_zone")) + { + LLFolderView* root_folder = getRootFolder(); + handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + } + return handled; +} + +void LLPanelMarketplaceListings::buildAllPanels() +{ + // Build the All panel first + LLInventoryPanel* panel_all_items; + panel_all_items = buildInventoryPanel("All Items", "panel_marketplace_listings_inventory.xml"); + panel_all_items->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel_all_items->getFilter().markDefault(); + + // Build the other panels + LLInventoryPanel* panel; + panel = buildInventoryPanel("Active Items", "panel_marketplace_listings_listed.xml"); + panel->getFilter().setFilterMarketplaceActiveFolders(); + panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel->getFilter().markDefault(); + panel = buildInventoryPanel("Inactive Items", "panel_marketplace_listings_unlisted.xml"); + panel->getFilter().setFilterMarketplaceInactiveFolders(); + panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel->getFilter().markDefault(); + panel = buildInventoryPanel("Unassociated Items", "panel_marketplace_listings_unassociated.xml"); + panel->getFilter().setFilterMarketplaceUnassociatedFolders(); + panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel->getFilter().markDefault(); + + // Set the tab panel + LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + tabs_panel->setCommitCallback(boost::bind(&LLPanelMarketplaceListings::onTabChange, this)); + tabs_panel->selectTabPanel(panel_all_items); // All panel selected by default + mRootFolder = panel_all_items->getRootFolder(); // Keep the root of the all panel + + // Set the default sort order + setSortOrder(gSavedSettings.getU32("MarketplaceListingsSortOrder")); +} + +LLInventoryPanel* LLPanelMarketplaceListings::buildInventoryPanel(const std::string& childname, const std::string& filename) +{ + LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + // Singu Note: These should already be built!! + LLInventoryPanel* panel = tabs_panel->getChild(childname, false, false); + llassert(panel != NULL); + + // Set sort order and callbacks + panel->getRootFolder()->setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME); + panel->setSelectCallback(boost::bind(&LLPanelMarketplaceListings::onSelectionChange, this, panel, _1, _2)); + + return panel; +} + +void LLPanelMarketplaceListings::setSortOrder(U32 sort_order) +{ + mSortOrder = sort_order; + gSavedSettings.setU32("MarketplaceListingsSortOrder", sort_order); + + // Set each panel with that sort order + LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + LLInventoryPanel* panel = (LLInventoryPanel*)tabs_panel->getPanelByName("All Items"); + panel->setSortOrder(mSortOrder); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Active Items"); + panel->setSortOrder(mSortOrder); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Inactive Items"); + panel->setSortOrder(mSortOrder); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Unassociated Items"); + panel->setSortOrder(mSortOrder); +} + +void LLPanelMarketplaceListings::onFilterEdit(const std::string& search_string) +{ + // Find active panel + LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); + if (panel) + { + // Save filter string (needed when switching tabs) + mFilterSubString = search_string; + // Set filter string on active panel + panel->setFilterSubString(mFilterSubString); + } +} + +void LLPanelMarketplaceListings::draw() +{ + if (LLMarketplaceData::instance().checkDirtyCount()) + { + update_all_marketplace_count(); + } + + // Get the audit button enabled only after the whole inventory is fetched + if (!mAuditBtn->getEnabled()) + { + mAuditBtn->setEnabled(LLInventoryModelBackgroundFetch::instance().isEverythingFetched()); + } + + LLPanel::draw(); +} + +void LLPanelMarketplaceListings::onSelectionChange(LLInventoryPanel *panel, const std::deque& items, BOOL user_action) +{ + panel->onSelectionChange(items, user_action); +} + +bool LLPanelMarketplaceListings::allowDropOnRoot() +{ + LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); + return (panel ? panel->getAllowDropOnRoot() : false); +} + +void LLPanelMarketplaceListings::onTabChange() +{ + // Find active panel + LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); + if (panel) + { + // If the panel doesn't allow drop on root, it doesn't allow the creation of new folder on root either + LLButton* add_btn = getChild("add_btn"); + add_btn->setEnabled(panel->getAllowDropOnRoot()); + + // Set filter string on active panel + panel->setFilterSubString(mFilterSubString); + + // Show/hide the drop zone and resize the inventory tabs panel accordingly + LLPanel* drop_zone = (LLPanel*)getChild("marketplace_drop_zone"); + bool drop_zone_visible = drop_zone->getVisible(); + bool allow_drop_on_root = panel->getAllowDropOnRoot() && gSavedSettings.getBOOL("LiruEnableWIPUI"); + if (drop_zone_visible != allow_drop_on_root) + { + LLPanel* tabs = (LLPanel*)getChild("tab_container_panel"); + S32 delta_height = drop_zone->getRect().getHeight(); + delta_height = (drop_zone_visible ? delta_height : -delta_height); + tabs->reshape(tabs->getRect().getWidth(),tabs->getRect().getHeight() + delta_height); + tabs->translate(0,-delta_height); + } + drop_zone->setVisible(allow_drop_on_root); + } +} + +void LLPanelMarketplaceListings::onAddButtonClicked() +{ + // Find active panel + LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); + if (panel) + { + LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + llassert(marketplacelistings_id.notNull()); + LLFolderType::EType preferred_type = LLFolderType::lookup("category"); + LLUUID category = gInventory.createNewCategory(marketplacelistings_id, preferred_type, LLStringUtil::null); + gInventory.notifyObservers(); + panel->setSelectionByID(category, TRUE); + panel->getRootFolder()->setNeedsAutoRename(TRUE); + } +} + +void LLPanelMarketplaceListings::onAuditButtonClicked() +{ + LLSD data(LLSD::emptyMap()); + new LLFloaterMarketplaceValidation(data); // It tracks itself *sigh* +} + +void LLPanelMarketplaceListings::onViewSortMenuItemClicked(const LLSD& userdata) +{ + std::string chosen_item = userdata.asString(); + + // Sort options + if ((chosen_item == "sort_by_stock_amount") || (chosen_item == "sort_by_name") || (chosen_item == "sort_by_recent")) + { + // We're making sort options exclusive, default is SO_FOLDERS_BY_NAME + if (chosen_item == "sort_by_stock_amount") + { + setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_WEIGHT); + } + else if (chosen_item == "sort_by_name") + { + setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME); + } + else if (chosen_item == "sort_by_recent") + { + setSortOrder(LLInventoryFilter::SO_DATE); + } + } + // Filter option + else if (chosen_item == "show_only_listing_folders") + { + mFilterListingFoldersOnly = !mFilterListingFoldersOnly; + // Set each panel with that filter flag + LLTabContainer* tabs_panel = getChild("marketplace_filter_tabs"); + LLInventoryPanel* panel = (LLInventoryPanel*)tabs_panel->getPanelByName("All Items"); + panel->getFilter().setFilterMarketplaceListingFolders(mFilterListingFoldersOnly); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Active Items"); + panel->getFilter().setFilterMarketplaceListingFolders(mFilterListingFoldersOnly); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Inactive Items"); + panel->getFilter().setFilterMarketplaceListingFolders(mFilterListingFoldersOnly); + panel = (LLInventoryPanel*)tabs_panel->getPanelByName("Unassociated Items"); + panel->getFilter().setFilterMarketplaceListingFolders(mFilterListingFoldersOnly); + } +} + +bool LLPanelMarketplaceListings::onViewSortMenuItemCheck(const LLSD& userdata) +{ + std::string chosen_item = userdata.asString(); + + if ((chosen_item == "sort_by_stock_amount") || (chosen_item == "sort_by_name") || (chosen_item == "sort_by_recent")) + { + if (chosen_item == "sort_by_stock_amount") + { + return (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_WEIGHT); + } + else if (chosen_item == "sort_by_name") + { + return (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME); + } + else if (chosen_item == "sort_by_recent") + { + return (mSortOrder & LLInventoryFilter::SO_DATE); + } + } + else if (chosen_item == "show_only_listing_folders") + { + return mFilterListingFoldersOnly; + } + return false; +} + +///---------------------------------------------------------------------------- +/// LLMarketplaceListingsAddedObserver helper class +///---------------------------------------------------------------------------- + +class LLMarketplaceListingsAddedObserver : public LLInventoryCategoryAddedObserver +{ +public: + LLMarketplaceListingsAddedObserver(LLFloaterMarketplaceListings * marketplace_listings_floater) + : LLInventoryCategoryAddedObserver() + , mMarketplaceListingsFloater(marketplace_listings_floater) + { + } + + void done() + { + for (cat_vec_t::iterator it = mAddedCategories.begin(); it != mAddedCategories.end(); ++it) + { + LLViewerInventoryCategory* added_category = *it; + + LLFolderType::EType added_category_type = added_category->getPreferredType(); + + if (added_category_type == LLFolderType::FT_MARKETPLACE_LISTINGS) + { + mMarketplaceListingsFloater->initializeMarketPlace(); + } + } + } + +private: + LLFloaterMarketplaceListings * mMarketplaceListingsFloater; +}; + +///---------------------------------------------------------------------------- +/// LLFloaterMarketplaceListings +///---------------------------------------------------------------------------- + +LLFloaterMarketplaceListings::LLFloaterMarketplaceListings(const LLSD& key) +: LLFloater(key) +, mCategoriesObserver(NULL) +, mCategoryAddedObserver(NULL) +, mRootFolderId(LLUUID::null) +, mInventoryStatus(NULL) +, mInventoryInitializationInProgress(NULL) +, mInventoryPlaceholder(NULL) +, mInventoryText(NULL) +, mInventoryTitle(NULL) +, mPanelListings(NULL) +, mPanelListingsSet(false) +{ + //buildFromFile("floater_marketplace_listings.xml"); + mFactoryMap["panel_marketplace_listing"] = LLCallbackMap([&](void*) { return mPanelListings = new LLPanelMarketplaceListings; }); + LLUICtrlFactory::instance().buildFloater(this, "floater_marketplace_listings.xml", &getFactoryMap()); +} + +LLFloaterMarketplaceListings::~LLFloaterMarketplaceListings() +{ + if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver)) + { + gInventory.removeObserver(mCategoriesObserver); + } + delete mCategoriesObserver; + + if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) + { + gInventory.removeObserver(mCategoryAddedObserver); + } + delete mCategoryAddedObserver; +} + +BOOL LLFloaterMarketplaceListings::postBuild() +{ + mInventoryStatus = getChild("marketplace_status"); + mInventoryInitializationInProgress = getChild("initialization_progress_indicator"); + mInventoryPlaceholder = getChild("marketplace_listings_inventory_placeholder_panel"); + mInventoryText = mInventoryPlaceholder->getChild("marketplace_listings_inventory_placeholder_text"); + mInventoryTitle = mInventoryPlaceholder->getChild("marketplace_listings_inventory_placeholder_title"); + + LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLFloaterMarketplaceListings::onFocusReceived, this)); + + // Observe category creation to catch marketplace listings creation (moot if already existing) + mCategoryAddedObserver = new LLMarketplaceListingsAddedObserver(this); + gInventory.addObserver(mCategoryAddedObserver); + + // Fetch aggressively so we can interact with listings right onOpen() + fetchContents(); + + return TRUE; +} + +void LLFloaterMarketplaceListings::onClose(bool app_quitting) +{ + LLFloater::onClose(app_quitting); +} + +void LLFloaterMarketplaceListings::onOpen(/*const LLSD& key*/) +{ + // + // Initialize the Market Place or go update the marketplace listings + // + if (LLMarketplaceData::instance().getSLMStatus() <= MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE) + { + initializeMarketPlace(); + } + else + { + updateView(); + } +} + +void LLFloaterMarketplaceListings::onFocusReceived() +{ + updateView(); +} + +void LLFloaterMarketplaceListings::fetchContents() +{ + if (mRootFolderId.notNull() && + (LLMarketplaceData::instance().getSLMDataFetched() != MarketplaceFetchCodes::MARKET_FETCH_LOADING) && + (LLMarketplaceData::instance().getSLMDataFetched() != MarketplaceFetchCodes::MARKET_FETCH_DONE)) + { + LLMarketplaceData::instance().setDataFetchedSignal(boost::bind(&LLFloaterMarketplaceListings::updateView, this)); + LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_LOADING); + LLInventoryModelBackgroundFetch::instance().start(mRootFolderId); + LLMarketplaceData::instance().getSLMListings(); + } +} + +void LLFloaterMarketplaceListings::setRootFolder() +{ + if ((LLMarketplaceData::instance().getSLMStatus() != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) && + (LLMarketplaceData::instance().getSLMStatus() != MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT)) + { + // If we are *not* a merchant or we have no market place connection established yet, do nothing + return; + } + + // We are a merchant. Get the Marketplace listings folder, create it if needs be. + LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true); + if (marketplacelistings_id.isNull()) + { + // We should never get there unless the inventory fails badly + LL_ERRS("SLM") << "Inventory problem: failure to create the marketplace listings folder for a merchant!" << LL_ENDL; + return; + } + + // No longer need to observe new category creation + if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) + { + gInventory.removeObserver(mCategoryAddedObserver); + delete mCategoryAddedObserver; + mCategoryAddedObserver = NULL; + } + llassert(!mCategoryAddedObserver); + + if (marketplacelistings_id == mRootFolderId) + { + LL_WARNS("SLM") << "Inventory warning: Marketplace listings folder already set" << LL_ENDL; + return; + } + + mRootFolderId = marketplacelistings_id; +} + +void LLFloaterMarketplaceListings::setPanels() +{ + if (mRootFolderId.isNull()) + { + return; + } + + // Consolidate Marketplace listings + // We shouldn't have to do that but with a client/server system relying on a "well known folder" convention, + // things get messy and conventions get broken down eventually + gInventory.consolidateForType(mRootFolderId, LLFolderType::FT_MARKETPLACE_LISTINGS); + + // Now that we do have a non NULL root, we can build the inventory panels + mPanelListings->buildAllPanels(); + + // Create observer for marketplace listings modifications + if (!mCategoriesObserver) + { + mCategoriesObserver = new LLInventoryCategoriesObserver(); + llassert(mCategoriesObserver); + gInventory.addObserver(mCategoriesObserver); + mCategoriesObserver->addCategory(mRootFolderId, boost::bind(&LLFloaterMarketplaceListings::onChanged, this)); + } + + // Get the content of the marketplace listings folder + fetchContents(); + + // Flag that this is done + mPanelListingsSet = true; +} + +void LLFloaterMarketplaceListings::initializeMarketPlace() +{ + LLMarketplaceData::instance().initializeSLM(boost::bind(&LLFloaterMarketplaceListings::updateView, this)); +} + +S32 LLFloaterMarketplaceListings::getFolderCount() +{ + if (mPanelListings && mRootFolderId.notNull()) + { + LLInventoryModel::cat_array_t * cats; + LLInventoryModel::item_array_t * items; + gInventory.getDirectDescendentsOf(mRootFolderId, cats, items); + + return (cats->size() + items->size()); + } + else + { + return 0; + } +} + +void LLFloaterMarketplaceListings::setStatusString(const std::string& statusString) +{ + mInventoryStatus->setText(statusString); +} + +void LLFloaterMarketplaceListings::updateView() +{ + U32 mkt_status = LLMarketplaceData::instance().getSLMStatus(); + bool is_merchant = (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) || (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT); + U32 data_fetched = LLMarketplaceData::instance().getSLMDataFetched(); + + // Get or create the root folder if we are a merchant and it hasn't been done already + if (mRootFolderId.isNull() && is_merchant) + { + setRootFolder(); + } + + // Update the bottom initializing status and progress dial if we are initializing or if we're a merchant and still loading + if ((mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) || (is_merchant && (data_fetched <= MarketplaceFetchCodes::MARKET_FETCH_LOADING)) ) + { + // Just show the loading indicator in that case and fetch the data (fetch will be skipped if it's already loading) + mInventoryInitializationInProgress->setVisible(true); + mPanelListings->setVisible(FALSE); + fetchContents(); + return; + } + else + { + mInventoryInitializationInProgress->setVisible(false); + } + + // Update the middle portion : tabs or messages + if (getFolderCount() > 0) + { + if (!mPanelListingsSet) + { + // We need to rebuild the tabs cleanly the first time we make them visible + setPanels(); + } + mPanelListings->setVisible(TRUE); + mInventoryPlaceholder->setVisible(FALSE); + } + else + { + mPanelListings->setVisible(FALSE); + mInventoryPlaceholder->setVisible(TRUE); + + std::string text; + std::string title; + std::string tooltip; + + const LLSD& subs = LLMarketplaceData::instance().getMarketplaceStringSubstitutions(); + + // Update the top message or flip to the tabs and folders view + // *TODO : check those messages and create better appropriate ones in strings.xml + if (mRootFolderId.notNull()) + { + // "Marketplace listings is empty!" message strings + text = LLTrans::getString("InventoryMarketplaceListingsNoItems", subs); + title = LLTrans::getString("InventoryMarketplaceListingsNoItemsTitle"); + tooltip = LLTrans::getString("InventoryMarketplaceListingsNoItemsTooltip"); + } + else if (mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) + { + // "Initializing!" message strings + text = LLTrans::getString("InventoryOutboxInitializing", subs); + title = LLTrans::getString("InventoryOutboxInitializingTitle"); + tooltip = LLTrans::getString("InventoryOutboxInitializingTooltip"); + } + else if (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT) + { + // "Not a merchant!" message strings + text = LLTrans::getString("InventoryOutboxNotMerchant", subs); + title = LLTrans::getString("InventoryOutboxNotMerchantTitle"); + tooltip = LLTrans::getString("InventoryOutboxNotMerchantTooltip"); + } + else + { + // "Errors!" message strings + text = LLTrans::getString("InventoryMarketplaceError", subs); + title = LLTrans::getString("InventoryOutboxErrorTitle"); + tooltip = LLTrans::getString("InventoryOutboxErrorTooltip"); + } + + mInventoryText->setValue(text); + mInventoryTitle->setValue(title); + mInventoryPlaceholder->getParent()->setToolTip(tooltip); + } +} + +bool LLFloaterMarketplaceListings::isAccepted(EAcceptance accept) +{ + return (accept >= ACCEPT_YES_COPY_SINGLE); +} + +BOOL LLFloaterMarketplaceListings::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + // If there's no panel to accept drops or no existing marketplace listings folder, we refuse all drop + if (!mPanelListings || mRootFolderId.isNull()) + { + return FALSE; + } + + tooltip_msg = ""; + + // Pass to the children + LLView * handled_view = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + BOOL handled = (handled_view != NULL); + + // If no one handled it or it was not accepted and we drop on an empty panel, we try to accept it at the floater level + // as if it was dropped on the marketplace listings root folder + if ((!handled || !isAccepted(*accept)) && !mPanelListings->getVisible() && mRootFolderId.notNull()) + { + if (!mPanelListingsSet) + { + setPanels(); + } + LLFolderView* root_folder = mPanelListings->getRootFolder(); + handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); + } + + return handled; +} + +BOOL LLFloaterMarketplaceListings::handleHover(S32 x, S32 y, MASK mask) +{ + return LLFloater::handleHover(x, y, mask); +} + +void LLFloaterMarketplaceListings::onMouseLeave(S32 x, S32 y, MASK mask) +{ + LLFloater::onMouseLeave(x, y, mask); +} + +void LLFloaterMarketplaceListings::onChanged() +{ + LLViewerInventoryCategory* category = gInventory.getCategory(mRootFolderId); + if (mRootFolderId.notNull() && category) + { + updateView(); + } + else + { + // Invalidate the marketplace listings data + mRootFolderId.setNull(); + } +} + +//----------------------------------------------------------------------------- +// LLFloaterAssociateListing +//----------------------------------------------------------------------------- + +// Tell if a listing has one only version folder +bool hasUniqueVersionFolder(const LLUUID& folder_id) +{ + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(folder_id, categories, items); + return (categories->size() == 1); +} + +LLFloaterAssociateListing::LLFloaterAssociateListing(const LLSD& key) +: LLFloater(key) +, mUUID() +{ + //buildFromFile("floater_associate_listing.xml"); + LLUICtrlFactory::instance().buildFloater(this, "floater_associate_listing.xml"); +} + +LLFloaterAssociateListing::~LLFloaterAssociateListing() +{ + gFocusMgr.releaseFocusIfNeeded( this ); +} + +BOOL LLFloaterAssociateListing::postBuild() +{ + getChild("OK")->setCommitCallback(boost::bind(&LLFloaterAssociateListing::apply, this, TRUE)); + getChild("Cancel")->setCommitCallback(boost::bind(&LLFloaterAssociateListing::cancel, this)); + getChild("listing_id")->setPrevalidate(&LLLineEditor::prevalidateNonNegativeS32); + center(); + + return LLFloater::postBuild(); +} + +BOOL LLFloaterAssociateListing::handleKeyHere(KEY key, MASK mask) +{ + if (key == KEY_RETURN && mask == MASK_NONE) + { + apply(); + return TRUE; + } + else if (key == KEY_ESCAPE && mask == MASK_NONE) + { + cancel(); + return TRUE; + } + + return LLFloater::handleKeyHere(key, mask); +} + +// static +void LLFloaterAssociateListing::show(LLFloaterAssociateListing* floater, const LLSD& folder_id) +{ + if (!floater) return; + + floater->mUUID = folder_id.asUUID(); + + floater->open(); +} + +// Callback for apply if DAMA required... +void LLFloaterAssociateListing::callback_apply(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + apply(FALSE); + } +} + +void LLFloaterAssociateListing::apply(BOOL user_confirm) +{ + if (mUUID.notNull()) + { + S32 id = (S32)getChild("listing_id")->getValue().asInteger(); + if (id > 0) + { + // Check if the id exists in the merchant SLM DB: note that this record might exist in the LLMarketplaceData + // structure even if unseen in the UI, for instance, if its listing_uuid doesn't exist in the merchant inventory + LLUUID listing_uuid = LLMarketplaceData::instance().getListingFolder(id); + if (listing_uuid.notNull() && user_confirm && LLMarketplaceData::instance().getActivationState(listing_uuid) && !hasUniqueVersionFolder(mUUID)) + { + // Look for user confirmation before unlisting + LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLFloaterAssociateListing::callback_apply, this, _1, _2)); + return; + } + // Associate the id with the user chosen folder + LLMarketplaceData::instance().associateListing(mUUID,listing_uuid,id); + } + else + { + LLNotificationsUtil::add("AlertMerchantListingInvalidID"); + } + } + close(); +} + +void LLFloaterAssociateListing::cancel() +{ + close(); +} + +//----------------------------------------------------------------------------- +// LLFloaterMarketplaceValidation +//----------------------------------------------------------------------------- + +// Note: The key is the UUID of the folder to validate. +// Validates the whole marketplace listings content if UUID is null. + +LLFloaterMarketplaceValidation::LLFloaterMarketplaceValidation(const LLSD& key) +: LLFloater(), + mKey(key), + mEditor(NULL) +{ + //buildFromFile("floater_marketplace_validation.xml"); + LLUICtrlFactory::instance().buildFloater(this, "floater_marketplace_validation.xml"); +} + +BOOL LLFloaterMarketplaceValidation::postBuild() +{ + childSetAction("OK", onOK, this); + + // This widget displays the validation messages + mEditor = getChild("validation_text"); + mEditor->setEnabled(FALSE); + mEditor->setFocus(TRUE); + mEditor->setValue(LLSD()); + + return TRUE; +} + +LLFloaterMarketplaceValidation::~LLFloaterMarketplaceValidation() +{ +} + +// virtual +void LLFloaterMarketplaceValidation::draw() +{ + // draw children + LLFloater::draw(); +} + +void LLFloaterMarketplaceValidation::onOpen(/*const LLSD& key*/) +{ + // Clear the messages + clearMessages(); + + // Get the folder UUID to validate. Use the whole marketplace listing if none provided. + LLUUID cat_id(mKey.asUUID()); + if (cat_id.isNull()) + { + cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + } + + // Validates the folder + if (cat_id.notNull()) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + validate_marketplacelistings(cat, boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3), false); + } + + // Handle the listing folder being processed + handleCurrentListing(); + + // Dump result to the editor panel + if (mEditor) + { + mEditor->setValue(LLSD()); + if (mMessages.empty()) + { + // Display a no error message + mEditor->appendText(LLTrans::getString("Marketplace Validation No Error"), false, false); + } + else + { + // Print out all the messages to the panel + message_list_t::iterator mCurrentLine = mMessages.begin(); + bool new_line = false; + while (mCurrentLine != mMessages.end()) + { + // Errors are printed in bold, other messages in normal font + LLStyleSP style(new LLStyle); + style->setColor(mEditor->getReadOnlyFgColor()); + style->mBold = mCurrentLine->mErrorLevel == LLError::LEVEL_ERROR; + mEditor->appendText(mCurrentLine->mMessage, false, new_line, style); + new_line = true; + mCurrentLine++; + } + } + } + // We don't need the messages anymore + clearMessages(); +} + +// static +void LLFloaterMarketplaceValidation::onOK( void* userdata ) +{ + // destroys this object + LLFloaterMarketplaceValidation* self = (LLFloaterMarketplaceValidation*) userdata; + self->clearMessages(); + self->close(); +} + +void LLFloaterMarketplaceValidation::appendMessage(std::string& message, S32 depth, LLError::ELevel log_level) +{ + // Dump previous listing messages if we're starting a new listing + if (depth == 1) + { + handleCurrentListing(); + } + + // Store the message in the current listing message list + Message current_message; + current_message.mErrorLevel = log_level; + current_message.mMessage = message; + mCurrentListingMessages.push_back(current_message); + mCurrentListingErrorLevel = (mCurrentListingErrorLevel < log_level ? log_level : mCurrentListingErrorLevel); +} + +// Move the current listing messages to the general list if needs be and reset the current listing data +void LLFloaterMarketplaceValidation::handleCurrentListing() +{ + // Dump the current folder messages to the general message list if level warrants it + if (mCurrentListingErrorLevel > LLError::LEVEL_INFO) + { + message_list_t::iterator mCurrentLine = mCurrentListingMessages.begin(); + while (mCurrentLine != mCurrentListingMessages.end()) + { + mMessages.push_back(*mCurrentLine); + mCurrentLine++; + } + } + + // Reset the current listing + mCurrentListingMessages.clear(); + mCurrentListingErrorLevel = LLError::LEVEL_INFO; +} + +void LLFloaterMarketplaceValidation::clearMessages() +{ + mMessages.clear(); + mCurrentListingMessages.clear(); + mCurrentListingErrorLevel = LLError::LEVEL_INFO; +} + +/* Singu Note: What even is this? Where is this used?! +//----------------------------------------------------------------------------- +// LLFloaterItemProperties +//----------------------------------------------------------------------------- + +LLFloaterItemProperties::LLFloaterItemProperties(const LLSD& key) +: LLFloater(key) +{ +} + +LLFloaterItemProperties::~LLFloaterItemProperties() +{ +} + +BOOL LLFloaterItemProperties::postBuild() +{ + // On the standalone properties floater, we have no need for a back button... + LLPanel* panel = getChild("item_panel"); + LLButton* back_btn = panel->getChild("back_btn"); + back_btn->setVisible(FALSE); + + return LLFloater::postBuild(); +} + +void LLFloaterItemProperties::onOpen(const LLSD& key) +{ + // Tell the panel which item it needs to visualize + LLPanel* panel = getChild("item_panel"); + panel->setItemID(mKey["id"].asUUID()); +} +*/ diff --git a/indra/newview/llfloatermarketplacelistings.h b/indra/newview/llfloatermarketplacelistings.h new file mode 100644 index 000000000..22896a34e --- /dev/null +++ b/indra/newview/llfloatermarketplacelistings.h @@ -0,0 +1,236 @@ +/** + * @file llfloatermarketplacelistings.h + * @brief Implementation of the marketplace listings floater and panels + * @author merov@lindenlab.com + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * ABILITY 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_LLFLOATERMARKETPLACELISTINGS_H +#define LL_LLFLOATERMARKETPLACELISTINGS_H + +#include "llfloater.h" +#include "llinventoryfilter.h" +#include "llinventorypanel.h" +#include "llnotificationptr.h" +#include "llmodaldialog.h" +#include "lltexteditor.h" + +class LLInventoryCategoriesObserver; +class LLInventoryCategoryAddedObserver; +class LLTextBox; +class LLView; +class LLFilterEditor; + +class LLFloaterMarketplaceListings; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLPanelMarketplaceListings +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLPanelMarketplaceListings : public LLPanel +{ +public: + LLPanelMarketplaceListings(); + ~LLPanelMarketplaceListings(); + BOOL postBuild(); + BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + void draw(); + LLFolderView* getRootFolder() { return mRootFolder; } + bool allowDropOnRoot(); + + void buildAllPanels(); + +private: + friend struct MarketplaceViewSortAction; + friend struct MarketplaceViewSortCheckItem; + + LLInventoryPanel* buildInventoryPanel(const std::string& childname, const std::string& filename); + + // UI callbacks + void onViewSortMenuItemClicked(const LLSD& userdata); + bool onViewSortMenuItemCheck(const LLSD& userdata); + void onAddButtonClicked(); + void onAuditButtonClicked(); + void onSelectionChange(LLInventoryPanel *panel, const std::deque& items, BOOL user_action); + void onTabChange(); + void onFilterEdit(const std::string& search_string); + + void setSortOrder(U32 sort_order); + + LLFolderView* mRootFolder; + LLButton* mAuditBtn; + class LLMenuGL* mMenu; + LLFilterEditor* mFilterEditor; + std::string mFilterSubString; + bool mFilterListingFoldersOnly; + U32 mSortOrder; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFloaterMarketplaceListings +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLFloaterMarketplaceListings : public LLFloater, public LLFloaterSingleton +{ +public: + LLFloaterMarketplaceListings(const LLSD& key); + ~LLFloaterMarketplaceListings(); + + void initializeMarketPlace(); + + // virtuals + BOOL postBuild(); + BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + + void showNotification(const LLNotificationPtr& notification); + + BOOL handleHover(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + +protected: + void setRootFolder(); + void setPanels(); + void fetchContents(); + + void setStatusString(const std::string& statusString); + + void onClose(bool app_quitting); + void onOpen(/*const LLSD& key*/); + void onFocusReceived(); + void onChanged(); + + bool isAccepted(EAcceptance accept); + + void updateView(); + +private: + friend struct MarketplaceViewSortAction; + friend struct MarketplaceViewSortCheckItem; + S32 getFolderCount(); + + LLInventoryCategoriesObserver * mCategoriesObserver; + LLInventoryCategoryAddedObserver * mCategoryAddedObserver; + + LLTextBox * mInventoryStatus; + LLView * mInventoryInitializationInProgress; + LLView * mInventoryPlaceholder; + LLTextEditor * mInventoryText; + LLTextBox * mInventoryTitle; + + LLUUID mRootFolderId; + LLPanelMarketplaceListings * mPanelListings; + bool mPanelListingsSet; +}; + +//----------------------------------------------------------------------------- +// LLFloaterAssociateListing +//----------------------------------------------------------------------------- +class LLFloaterAssociateListing : public LLFloater, public LLUISingleton +{ +friend class LLUISingleton; +public: + virtual BOOL postBuild(); + virtual BOOL handleKeyHere(KEY key, MASK mask); + + static bool visible(LLFloaterAssociateListing* floater, const LLSD& key) { return floater && !floater->isMinimized() && floater->isInVisibleChain(); } + static void show(LLFloaterAssociateListing* floater, const LLSD& folder_id); + static void hide(LLFloaterAssociateListing* floater, const LLSD& key) { if (floater) floater->close(); } + +private: + LLFloaterAssociateListing(const LLSD& key); + virtual ~LLFloaterAssociateListing(); + + // UI Callbacks + void apply(BOOL user_confirm = TRUE); + void cancel(); + void callback_apply(const LLSD& notification, const LLSD& response); + + LLUUID mUUID; +}; + +//----------------------------------------------------------------------------- +// LLFloaterMarketplaceValidation +//----------------------------------------------------------------------------- +// Note: The key is the UUID of the folder to validate. Validates the whole +// marketplace listings content if UUID is null. +// Note: For the moment, we just display the validation text. Eventually, we should +// get the validation triggered on the server and display the html report. +// *TODO : morph into an html/text window using the pattern in llfloatertos + +class LLFloaterMarketplaceValidation : public LLFloater +{ +public: + LLFloaterMarketplaceValidation(const LLSD& key); + virtual ~LLFloaterMarketplaceValidation(); + + virtual BOOL postBuild(); + virtual void draw(); + virtual void onOpen(/*const LLSD& key*/); + + void clearMessages(); + void appendMessage(std::string& message, S32 depth, LLError::ELevel log_level); + static void onOK( void* userdata ); + +private: + struct Message { + LLError::ELevel mErrorLevel; + std::string mMessage; + }; + typedef std::vector message_list_t; + + void handleCurrentListing(); + + message_list_t mCurrentListingMessages; + LLError::ELevel mCurrentListingErrorLevel; + + message_list_t mMessages; + + LLSD mKey; + LLTextEditor* mEditor; +}; + +//----------------------------------------------------------------------------- +// LLFloaterItemProperties +//----------------------------------------------------------------------------- + +class LLFloaterItemProperties : public LLFloater +{ +public: + LLFloaterItemProperties(const LLSD& key); + virtual ~LLFloaterItemProperties(); + + BOOL postBuild(); + virtual void onOpen(/*const LLSD& key*/); + +private: +}; + +#endif // LL_LLFLOATERMARKETPLACELISTINGS_H diff --git a/indra/newview/llfloaterobjectiminfo.cpp b/indra/newview/llfloaterobjectiminfo.cpp index e1a0806f8..3610cab14 100644 --- a/indra/newview/llfloaterobjectiminfo.cpp +++ b/indra/newview/llfloaterobjectiminfo.cpp @@ -79,7 +79,7 @@ BOOL LLFloaterObjectIMInfo::postBuild() { getChild("Mute")->setCommitCallback(boost::bind(&LLFloaterObjectIMInfo::onClickMute, this)); getChild("OwnerName")->setClickedCallback(boost::bind(boost::ref(mGroupOwned) ? boost::bind(LLGroupActions::show, boost::ref(mOwnerID)) : boost::bind(show_avatar_profile, boost::ref(mOwnerID)))); - getChild("Slurl")->setClickedCallback(boost::bind(LLUrlAction::executeSLURL, boost::bind(std::plus(), "secondlife:///app/worldmap/", boost::ref(mSLurl)))); + getChild("Slurl")->setClickedCallback(boost::bind(LLUrlAction::executeSLURL, boost::bind(std::plus(), "secondlife:///app/worldmap/", boost::ref(mSLurl)), true)); return true; } diff --git a/indra/newview/llfloateroutbox.cpp b/indra/newview/llfloateroutbox.cpp deleted file mode 100644 index 22dbe1d87..000000000 --- a/indra/newview/llfloateroutbox.cpp +++ /dev/null @@ -1,688 +0,0 @@ -/** - * @file llfloateroutbox.cpp - * @brief Implementation of the merchant outbox window - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llfloateroutbox.h" - -#include "llfolderview.h" -#include "llinventorybridge.h" -#include "llinventorymodelbackgroundfetch.h" -#include "llinventoryobserver.h" -#include "llinventorypanel.h" -#include "llmarketplacefunctions.h" -#include "llnotifications.h" -#include "llnotificationsutil.h" -#include "lltextbox.h" -#include "lltexteditor.h" -//#include "lltransientfloatermgr.h" -#include "lltrans.h" -#include "llviewernetwork.h" -//#include "llwindowshade.h" - -#define USE_WINDOWSHADE_DIALOGS 0 - - -///---------------------------------------------------------------------------- -/// LLOutboxNotification class -///---------------------------------------------------------------------------- - -class LLOutboxNotification -{ -public: - bool processNotification(const LLSD& notify) - { - LLFloaterOutbox* outbox_floater = LLFloaterOutbox::getInstance(); - - outbox_floater->showNotification(notify); - - return false; - } -}; - -///---------------------------------------------------------------------------- -/// LLOutboxAddedObserver helper class -///---------------------------------------------------------------------------- - -class LLOutboxAddedObserver : public LLInventoryCategoryAddedObserver -{ -public: - LLOutboxAddedObserver(LLFloaterOutbox * outboxFloater) - : LLInventoryCategoryAddedObserver() - , mOutboxFloater(outboxFloater) - { - } - - void done() - { - for (cat_vec_t::iterator it = mAddedCategories.begin(); it != mAddedCategories.end(); ++it) - { - LLViewerInventoryCategory* added_category = *it; - - LLFolderType::EType added_category_type = added_category->getPreferredType(); - - if (added_category_type == LLFolderType::FT_OUTBOX) - { - mOutboxFloater->initializeMarketPlace(); - } - } - } - -private: - LLFloaterOutbox * mOutboxFloater; -}; - -///---------------------------------------------------------------------------- -/// LLFloaterOutbox -///---------------------------------------------------------------------------- - -LLFloaterOutbox::LLFloaterOutbox(const LLSD& key) - : LLFloater(key) - , mCategoriesObserver(NULL) - , mCategoryAddedObserver(NULL) - , mImportBusy(false) - , mImportButton(NULL) - , mInventoryFolderCountText(NULL) - , mInventoryImportInProgress(NULL) - , mInventoryPlaceholder(NULL) - , mInventoryText(NULL) - , mInventoryTitle(NULL) - , mOutboxId(LLUUID::null) - , mOutboxItemCount(0) - , mOutboxTopLevelDropZone(NULL) -// , mWindowShade(NULL) -{ - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_merchant_outbox.xml"); - -} - -LLFloaterOutbox::~LLFloaterOutbox() -{ - if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver)) - { - gInventory.removeObserver(mCategoriesObserver); - } - delete mCategoriesObserver; - - if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) - { - gInventory.removeObserver(mCategoryAddedObserver); - } - delete mCategoryAddedObserver; -} - -BOOL LLFloaterOutbox::postBuild() -{ - mInventoryFolderCountText = getChild("outbox_folder_count"); - mInventoryImportInProgress = getChild("import_progress_indicator"); - mInventoryPlaceholder = getChild("outbox_inventory_placeholder_panel"); - mInventoryText = mInventoryPlaceholder->getChild("outbox_inventory_placeholder_text"); - // For some reason, adding style doesn't work unless this is true. - mInventoryText->setParseHTML(TRUE); - - mInventoryTitle = mInventoryPlaceholder->getChild("outbox_inventory_placeholder_title"); - - mImportButton = getChild("outbox_import_btn"); - mImportButton->setCommitCallback(boost::bind(&LLFloaterOutbox::onImportButtonClicked, this)); - - // Set up the outbox inventory view - LLInventoryPanel* inventory_panel = getChild("panel_outbox_inventory"); - mOutboxInventoryPanel = inventory_panel->getInventoryPanelHandle(); - llassert(mOutboxInventoryPanel.get() != NULL); - - mOutboxTopLevelDropZone = getChild("outbox_generic_drag_target"); - - LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLFloaterOutbox::onFocusReceived, this)); - - // Observe category creation to catch outbox creation (moot if already existing) - mCategoryAddedObserver = new LLOutboxAddedObserver(this); - gInventory.addObserver(mCategoryAddedObserver); - - return TRUE; -} - -void LLFloaterOutbox::cleanOutbox() -{ - // Note: we cannot delete the mOutboxInventoryPanel as that point - // as this is called through callback observers of the panel itself. - // Doing so would crash rapidly. - - // Invalidate the outbox data - mOutboxId.setNull(); - mOutboxItemCount = 0; -} - -void LLFloaterOutbox::onClose(bool app_quitting) -{ -/* - if (mWindowShade) - { - delete mWindowShade; - - mWindowShade = NULL; - } -*/ - setVisible(FALSE); - //destroy(); //Don't because outbox should be persistent. -} - -void LLFloaterOutbox::onOpen() -{ - // - // Initialize the Market Place or go update the outbox - // - - if (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED) - { - initializeMarketPlace(); - } - else - { - setupOutbox(); - } - - // - // Update the floater view - // - updateView(); - - // - // Trigger fetch of outbox contents - // - fetchOutboxContents(); -} - -void LLFloaterOutbox::onFocusReceived() -{ - fetchOutboxContents(); -} - -void LLFloaterOutbox::fetchOutboxContents() -{ - if (mOutboxId.notNull()) - { - LLInventoryModelBackgroundFetch::instance().start(mOutboxId); - } -} - -void LLFloaterOutbox::setupOutbox() -{ - if (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) - { - // If we are *not* a merchant or we have no market place connection established yet, do nothing - return; - } - - // We are a merchant. Get the outbox, create it if needs be. - LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, true); - if (outbox_id.isNull()) - { - // We should never get there unless inventory fails badly - LL_ERRS() << "Inventory problem: failure to create the outbox for a merchant!" << LL_ENDL; - return; - } - - // Consolidate Merchant Outbox - // We shouldn't have to do that but with a client/server system relying on a "well known folder" convention, things get messy and conventions get broken down eventually - gInventory.consolidateForType(outbox_id, LLFolderType::FT_OUTBOX); - - if (outbox_id == mOutboxId) - { - - LL_WARNS() << "Inventory warning: Merchant outbox already set" << LL_ENDL; - return; - } - mOutboxId = outbox_id; - - // No longer need to observe new category creation - if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) - { - gInventory.removeObserver(mCategoryAddedObserver); - delete mCategoryAddedObserver; - mCategoryAddedObserver = NULL; - } - llassert(!mCategoryAddedObserver); - - // Create observer for outbox modifications : clear the old one and create a new one - if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver)) - { - gInventory.removeObserver(mCategoriesObserver); - delete mCategoriesObserver; - } - mCategoriesObserver = new LLInventoryCategoriesObserver(); - gInventory.addObserver(mCategoriesObserver); - mCategoriesObserver->addCategory(mOutboxId, boost::bind(&LLFloaterOutbox::onOutboxChanged, this)); - llassert(mCategoriesObserver); - - // Set up the outbox inventory view - // Singu Note: we handle this in postBuild, grabbing the panel from the built xml. - LLInventoryPanel* inventory_panel = mOutboxInventoryPanel.get(); - - // Reshape the inventory to the proper size - LLRect inventory_placeholder_rect = mInventoryPlaceholder->getRect(); - inventory_panel->setShape(inventory_placeholder_rect); - - // Set the sort order newest to oldest - inventory_panel->setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME); - inventory_panel->getFilter()->markDefault(); - - // Get the content of the outbox - fetchOutboxContents(); -} - -void LLFloaterOutbox::initializeMarketPlace() -{ - // - // Initialize the marketplace import API - // - LLMarketplaceInventoryImporter& importer = LLMarketplaceInventoryImporter::instance(); - - if (!importer.isInitialized()) - { - importer.setInitializationErrorCallback(boost::bind(&LLFloaterOutbox::initializationReportError, this, _1, _2)); - importer.setStatusChangedCallback(boost::bind(&LLFloaterOutbox::importStatusChanged, this, _1)); - importer.setStatusReportCallback(boost::bind(&LLFloaterOutbox::importReportResults, this, _1, _2)); - importer.initialize(); - } -} - -void LLFloaterOutbox::setStatusString(const std::string& statusString) -{ - llassert(mInventoryFolderCountText != NULL); - - mInventoryFolderCountText->setText(statusString); -} - -void LLFloaterOutbox::updateFolderCount() -{ - if (mOutboxInventoryPanel.get() && mOutboxId.notNull()) - { - S32 item_count = 0; - - if (mOutboxId.notNull()) - { - LLInventoryModel::cat_array_t * cats; - LLInventoryModel::item_array_t * items; - gInventory.getDirectDescendentsOf(mOutboxId, cats, items); - - item_count = cats->size() + items->size(); - } - - mOutboxItemCount = item_count; - } - else - { - // If there's no outbox, the number of items in it should be set to 0 for consistency - mOutboxItemCount = 0; - } - - if (!mImportBusy) - { - updateFolderCountStatus(); - } -} - -void LLFloaterOutbox::updateFolderCountStatus() -{ - if (mOutboxInventoryPanel.get() && mOutboxId.notNull()) - { - switch (mOutboxItemCount) - { - case 0: setStatusString(getString("OutboxFolderCount0")); break; - case 1: setStatusString(getString("OutboxFolderCount1")); break; - default: - { - std::string item_count_str = llformat("%d", mOutboxItemCount); - - LLStringUtil::format_map_t args; - args["[NUM]"] = item_count_str; - - setStatusString(getString("OutboxFolderCountN", args)); - break; - } - } - } - - mImportButton->setEnabled(mOutboxItemCount > 0); -} - -void LLFloaterOutbox::updateView() -{ - //updateView() is called twice the first time. - updateFolderCount(); - LLInventoryPanel* panel = mOutboxInventoryPanel.get(); - - if (mOutboxItemCount > 0) - { - panel->setVisible(TRUE); - mInventoryPlaceholder->setVisible(FALSE); - mOutboxTopLevelDropZone->setVisible(TRUE); - } - else - { - if (panel) - { - panel->setVisible(FALSE); - } - - // Show the drop zone if there is an outbox folder - mOutboxTopLevelDropZone->setVisible(mOutboxId.notNull()); - - mInventoryPlaceholder->setVisible(TRUE); - - std::string outbox_text; - std::string outbox_text2; - std::string outbox_title; - std::string outbox_tooltip; - - const LLSD& subs = getMarketplaceStringSubstitutions(); - U32 mkt_status = LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus(); - - // Text styles for marketplace hyperlinks - std::string subs_link; - std::string subs_text; - - if (mOutboxId.notNull()) - { - // Does the outbox needs recreation? - if ((mOutboxInventoryPanel.get() == NULL) || !gInventory.getCategory(mOutboxId)) - { - setupOutbox(); - } - // "Outbox is empty!" message strings - outbox_text = LLTrans::getString("InventoryOutboxNoItems"); - subs_link = "[MARKETPLACE_DASHBOARD_URL]"; - subs_text = " " + LLTrans::getString("InventoryOutboxNoItemsSubs"); - outbox_text2 = LLTrans::getString("InventoryOutboxNoItems2"); - outbox_title = LLTrans::getString("InventoryOutboxNoItemsTitle"); - outbox_tooltip = LLTrans::getString("InventoryOutboxNoItemsTooltip"); - } - else if (mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) - { - // "Initializing!" message strings - outbox_text = LLTrans::getString("InventoryOutboxInitializing"); - subs_link = "[MARKETPLACE_CREATE_STORE_URL]"; - subs_text = " " + LLTrans::getString("InventoryOutboxInitializingSubs"); - outbox_text2 = LLTrans::getString("InventoryOutboxInitializing2"); - outbox_title = LLTrans::getString("InventoryOutboxInitializingTitle"); - outbox_tooltip = LLTrans::getString("InventoryOutboxInitializingTooltip"); - } - else if (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT) - { - // "Not a merchant!" message strings - outbox_text = LLTrans::getString("InventoryOutboxNotMerchant"); - subs_link = "[MARKETPLACE_CREATE_STORE_URL]"; - subs_text = " " + LLTrans::getString("InventoryOutboxNotMerchantSubs"); - outbox_text2 = LLTrans::getString("InventoryOutboxNotMerchant2"); - outbox_title = LLTrans::getString("InventoryOutboxNotMerchantTitle"); - outbox_tooltip = LLTrans::getString("InventoryOutboxNotMerchantTooltip"); - } - else - { - // "Errors!" message strings - outbox_text = LLTrans::getString("InventoryOutboxError"); - subs_link = "[MARKETPLACE_CREATE_STORE_URL]"; - subs_text = " " + LLTrans::getString("InventoryOutboxErrorSubs"); - outbox_text2 = " " + LLTrans::getString("InventoryOutboxError2"); - outbox_title = LLTrans::getString("InventoryOutboxErrorTitle"); - outbox_tooltip = LLTrans::getString("InventoryOutboxErrorTooltip"); - } - - mInventoryText->clear(); - const LLColor4 color = gColors.getColor("TextFgReadOnlyColor"); - mInventoryText->appendColoredText(outbox_text, false, false, color); - LLStringUtil::format(subs_link, subs); - LLStyleSP subs_link_style(new LLStyle); - subs_link_style->setLinkHREF(subs_link); - subs_link_style->setColor(gSavedSettings.getColor4("HTMLLinkColor")); - mInventoryText->appendStyledText(subs_text, false, false, subs_link_style); - mInventoryText->appendColoredText(outbox_text2, false, false, color); - mInventoryTitle->setValue(outbox_title); - mInventoryPlaceholder->getParent()->setToolTip(outbox_tooltip); - } -} - -bool isAccepted(EAcceptance accept) -{ - return (accept >= ACCEPT_YES_COPY_SINGLE); -} - -BOOL LLFloaterOutbox::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - if ((mOutboxInventoryPanel.get() == NULL) || - //(mWindowShade && mWindowShade->isShown()) || - LLMarketplaceInventoryImporter::getInstance()->isImportInProgress() || - mOutboxId.isNull()) - { - return FALSE; - } - - LLView * handled_view = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - BOOL handled = (handled_view != NULL); - - // Determine if the mouse is inside the inventory panel itself or just within the floater - bool pointInInventoryPanel = false; - bool pointInInventoryPanelChild = false; - LLInventoryPanel* panel = mOutboxInventoryPanel.get(); - LLFolderView * root_folder = panel->getRootFolder(); - if (panel->getVisible()) - { - S32 inv_x, inv_y; - localPointToOtherView(x, y, &inv_x, &inv_y, panel); - - pointInInventoryPanel = panel->getRect().pointInRect(inv_x, inv_y); - - LLView * inventory_panel_child_at_point = panel->childFromPoint(inv_x, inv_y, true); - pointInInventoryPanelChild = (inventory_panel_child_at_point != root_folder); - } - - // Pass all drag and drop for this floater to the outbox inventory control - if (!handled || !isAccepted(*accept)) - { - // Handle the drag and drop directly to the root of the outbox if we're not in the inventory panel - // (otherwise the inventory panel itself will handle the drag and drop operation, without any override) - if (!pointInInventoryPanel) - { - handled = root_folder->handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - } - - mOutboxTopLevelDropZone->setBackgroundVisible(handled && !drop && isAccepted(*accept)); - } - else - { - mOutboxTopLevelDropZone->setBackgroundVisible(!pointInInventoryPanelChild); - } - - return handled; -} - -BOOL LLFloaterOutbox::handleHover(S32 x, S32 y, MASK mask) -{ - mOutboxTopLevelDropZone->setBackgroundVisible(FALSE); - - return LLFloater::handleHover(x, y, mask); -} -/* -void LLFloaterOutbox::onMouseLeave(S32 x, S32 y, MASK mask) -{ - mOutboxTopLevelDropZone->setBackgroundVisible(FALSE); - - LLFloater::onMouseLeave(x, y, mask); -} -*/ -void LLFloaterOutbox::onImportButtonClicked() -{ - if (mOutboxInventoryPanel.get()) - { - mOutboxInventoryPanel.get()->clearSelection(); - } - - mImportBusy = LLMarketplaceInventoryImporter::instance().triggerImport(); -} - -void LLFloaterOutbox::onOutboxChanged() -{ - LLViewerInventoryCategory* category = gInventory.getCategory(mOutboxId); - if (mOutboxId.notNull() && category) - { - fetchOutboxContents(); - updateView(); - } - else - { - cleanOutbox(); - } -} - -void LLFloaterOutbox::importReportResults(U32 status, const LLSD& content) -{ - if (status == MarketplaceErrorCodes::IMPORT_DONE) - { - LLNotificationsUtil::add("OutboxImportComplete"); - } - else if (status == MarketplaceErrorCodes::IMPORT_DONE_WITH_ERRORS) - { - const LLSD& subs = getMarketplaceStringSubstitutions(); - - LLNotificationsUtil::add("OutboxImportHadErrors", subs); - } - else - { - char status_string[16]; - sprintf(status_string, "%d", status); - - LLSD subs; - subs["[ERROR_CODE]"] = status_string; - - LLNotificationsUtil::add("OutboxImportFailed", subs); - } - - updateView(); -} - -void LLFloaterOutbox::importStatusChanged(bool inProgress) -{ - if (mOutboxId.isNull() && (LLMarketplaceInventoryImporter::getInstance()->getMarketPlaceStatus() == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT)) - { - setupOutbox(); - } - - if (inProgress) - { - if (mImportBusy) - { - setStatusString(getString("OutboxImporting")); - } - else - { - setStatusString(getString("OutboxInitializing")); - } - - mImportBusy = true; - mImportButton->setEnabled(false); - mInventoryImportInProgress->setVisible(true); - } - else - { - setStatusString(""); - mImportBusy = false; - mImportButton->setEnabled(mOutboxItemCount > 0); - mInventoryImportInProgress->setVisible(false); - } - - updateView(); -} - -void LLFloaterOutbox::initializationReportError(U32 status, const LLSD& content) -{ - if (status >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) - { - char status_string[16]; - sprintf(status_string, "%d", status); - - LLSD subs; - subs["[ERROR_CODE]"] = status_string; - - LLNotificationsUtil::add("OutboxInitFailed", subs); - } - - updateView(); -} - -void LLFloaterOutbox::showNotification(const LLSD& notify) -{ - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if (!notification) - { - LL_ERRS() << "Unable to find outbox notification!" << notify.asString() << LL_ENDL; - - return; - } - -#if USE_WINDOWSHADE_DIALOGS - - if (mWindowShade) - { - delete mWindowShade; - } - - LLRect floater_rect = getLocalRect(); - floater_rect.mTop -= getHeaderHeight(); - floater_rect.stretch(-5, 0); - - LLWindowShade::Params params; - params.name = "notification_shade"; - params.rect = floater_rect; - params.follows.flags = FOLLOWS_ALL; - params.modal = true; - params.can_close = false; - params.shade_color = LLColor4::white % 0.25f; - params.text_color = LLColor4::white; - - mWindowShade = LLUICtrlFactory::create(params); - - addChild(mWindowShade); - mWindowShade->show(notification); - -#else //USE_WINDOWSHADE_DIALOGS -/* - LLNotificationsUI::LLEventHandler * handler = - LLNotificationsUI::LLNotificationManager::instance().getHandlerForNotification("alertmodal"); - - LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast(handler); - llassert(sys_handler); - - sys_handler->processNotification(notify); -*/ - LLNotifications::instance().add(notification); -#endif //USE_WINDOWSHADE_DIALOGS -} - diff --git a/indra/newview/llfloateroutbox.h b/indra/newview/llfloateroutbox.h deleted file mode 100644 index 61a11c50d..000000000 --- a/indra/newview/llfloateroutbox.h +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @file llfloateroutbox.h - * @brief LLFloaterOutbox - * class definition - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * ABILITY 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_LLFLOATEROUTBOX_H -#define LL_LLFLOATEROUTBOX_H - -#include "llfloater.h" -#include "llfoldertype.h" -#include "llnotificationptr.h" - - -class LLButton; -class LLInventoryCategoriesObserver; -class LLInventoryCategoryAddedObserver; -class LLInventoryPanel; -class LLLoadingIndicator; -class LLNotification; -class LLTextBox; -class LLTextEditor; -class LLView; -//class LLWindowShade; - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFloaterOutbox -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFloaterOutbox : public LLFloater, public LLFloaterSingleton -{ -public: - LLFloaterOutbox(const LLSD& key); - ~LLFloaterOutbox(); - - void initializeMarketPlace(); - - // virtuals - BOOL postBuild(); - BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - - void showNotification(const LLSD& notify); - - BOOL handleHover(S32 x, S32 y, MASK mask); -// void onMouseLeave(S32 x, S32 y, MASK mask); - -protected: - void setupOutbox(); - void cleanOutbox(); - void fetchOutboxContents(); - - void importReportResults(U32 status, const LLSD& content); - void importStatusChanged(bool inProgress); - void initializationReportError(U32 status, const LLSD& content); - - void onClose(bool app_quitting); - void onOpen(); - - void onFocusReceived(); - - void onImportButtonClicked(); - void onOutboxChanged(); - - void setStatusString(const std::string& statusString); - - void updateFolderCount(); - void updateFolderCountStatus(); - void updateView(); - -private: - LLInventoryCategoriesObserver * mCategoriesObserver; - LLInventoryCategoryAddedObserver * mCategoryAddedObserver; - - bool mImportBusy; - LLButton * mImportButton; - - LLTextBox * mInventoryFolderCountText; - LLView * mInventoryImportInProgress; - LLView * mInventoryPlaceholder; - LLTextEditor * mInventoryText; - LLTextBox * mInventoryTitle; - - LLUUID mOutboxId; - LLHandle mOutboxInventoryPanel; - U32 mOutboxItemCount; - LLPanel * mOutboxTopLevelDropZone; - - //LLWindowShade * mWindowShade; -}; - -#endif // LL_LLFLOATEROUTBOX_H diff --git a/indra/newview/llfloaterpostprocess.cpp b/indra/newview/llfloaterpostprocess.cpp index 22a89e015..70c374d8e 100644 --- a/indra/newview/llfloaterpostprocess.cpp +++ b/indra/newview/llfloaterpostprocess.cpp @@ -52,7 +52,7 @@ LLFloaterPostProcess* LLFloaterPostProcess::sPostProcess = NULL; -LLFloaterPostProcess::LLFloaterPostProcess() : LLFloater(std::string("Post-Process Floater")) +LLFloaterPostProcess::LLFloaterPostProcess(const LLSD&) : LLFloater("Post-Process Floater") { LLUICtrlFactory::getInstance()->buildFloater(this, "floater_post_process.xml"); @@ -91,24 +91,11 @@ LLFloaterPostProcess::LLFloaterPostProcess() : LLFloater(std::string("Post-Proce getChild("PPSaveEffect")->setCommitCallback(boost::bind(&LLFloaterPostProcess::onSaveEffect, this, editBox)); syncMenu(); + LLPostProcess::instance().setSelectedEffectChangeCallback(boost::bind(&LLFloaterPostProcess::syncMenu, this)); } LLFloaterPostProcess::~LLFloaterPostProcess() { - - -} - -LLFloaterPostProcess* LLFloaterPostProcess::instance() -{ - // if we don't have our singleton instance, create it - if (!sPostProcess) - { - sPostProcess = new LLFloaterPostProcess(); - sPostProcess->open(); - sPostProcess->setFocus(TRUE); - } - return sPostProcess; } @@ -122,8 +109,6 @@ void LLFloaterPostProcess::onLoadEffect(LLComboBox* comboBox) LLSD::String effectName(comboBox->getSelectedValue().asString()); LLPostProcess::getInstance()->setSelectedEffect(effectName); - - syncMenu(); } void LLFloaterPostProcess::onSaveEffect(LLLineEditor* editBox) @@ -139,7 +124,6 @@ void LLFloaterPostProcess::onSaveEffect(LLLineEditor* editBox) else { LLPostProcess::getInstance()->saveEffectAs(effectName); - syncMenu(); } } @@ -161,48 +145,40 @@ bool LLFloaterPostProcess::saveAlertCallback(const LLSD& notification, const LLS if (option == 0) { LLPostProcess::getInstance()->saveEffectAs(notification["payload"]["effect_name"].asString()); - - syncMenu(); } return false; } -void LLFloaterPostProcess::show() -{ - // get the instance, make sure the values are synced - // and open the menu - LLFloaterPostProcess* postProcess = instance(); - postProcess->syncMenu(); - postProcess->open(); -} - // virtual void LLFloaterPostProcess::onClose(bool app_quitting) { // just set visibility to false, don't get fancy yet - if (sPostProcess) - { - sPostProcess->setVisible(FALSE); - } + if (app_quitting) + die(); + else + setVisible(FALSE); } -void LLFloaterPostProcess::syncMenu() +void populatePostProcessList(LLComboBox* comboBox) { - // add the combo boxe contents - LLComboBox* comboBox = getChild("PPEffectsCombo"); - comboBox->removeall(); - LLSD::map_const_iterator currEffect; - for(currEffect = LLPostProcess::getInstance()->getAllEffectInfo().beginMap(); - currEffect != LLPostProcess::getInstance()->getAllEffectInfo().endMap(); + const auto& inst(LLPostProcess::instance()); + for(auto currEffect = inst.getAllEffectInfo().beginMap(), end = inst.getAllEffectInfo().endMap(); + currEffect != end; ++currEffect) { comboBox->add(currEffect->first); } // set the current effect as selected. - comboBox->selectByValue(LLPostProcess::getInstance()->getSelectedEffectName()); + comboBox->selectByValue(inst.getSelectedEffectName()); +} + +void LLFloaterPostProcess::syncMenu() +{ + // add the combo boxe contents + populatePostProcessList(getChild("PPEffectsCombo")); const LLSD &tweaks = LLPostProcess::getInstance()->getSelectedEffectInfo(); //Iterate down all uniforms handled by post-process shaders. Update any linked ui elements. @@ -214,7 +190,7 @@ void LLFloaterPostProcess::syncMenu() //llsd["uniform"][1]=>"uniform[1]" for(S32 i=0;isecond.size();++i) { - childSetValue(it->first+"["+boost::lexical_cast(i)+"]",it->second[i]); + childSetValue(it->first+'['+boost::lexical_cast(i)+']',it->second[i]); } } else diff --git a/indra/newview/llfloaterpostprocess.h b/indra/newview/llfloaterpostprocess.h index f49a33981..e2ffbf86e 100644 --- a/indra/newview/llfloaterpostprocess.h +++ b/indra/newview/llfloaterpostprocess.h @@ -49,16 +49,13 @@ class LLPanelFace; /** * Menu for adjusting the post process settings of the world */ -class LLFloaterPostProcess : public LLFloater +class LLFloaterPostProcess : public LLFloater, public LLFloaterSingleton { public: - LLFloaterPostProcess(); + LLFloaterPostProcess(const LLSD&); virtual ~LLFloaterPostProcess(); - /// one and one instance only - static LLFloaterPostProcess* instance(); - /// post process callbacks static void onControlChanged(LLUICtrl* ctrl, const LLSD& v); void onLoadEffect(LLComboBox* comboBox); @@ -68,18 +65,12 @@ public: /// prompts a user when overwriting an effect bool saveAlertCallback(const LLSD& notification, const LLSD& response); - /// show off our menu - static void show(); - /// stuff to do on exit virtual void onClose(bool app_quitting); /// sync up sliders void syncMenu(); -/* - void refresh(); -*/ public: static LLFloaterPostProcess* sPostProcess; diff --git a/indra/newview/llfloaterregionrestarting.cpp b/indra/newview/llfloaterregionrestarting.cpp index 448b85216..3064425e4 100644 --- a/indra/newview/llfloaterregionrestarting.cpp +++ b/indra/newview/llfloaterregionrestarting.cpp @@ -39,8 +39,11 @@ #include "lllandmarklist.h" #include "llviewerregion.h" #include "llworldmap.h" -void emergency_teleport() +void emergency_teleport(const U32& seconds) { + static const LLCachedControl time(gSavedSettings, "EmergencyTeleportSeconds"); + if (seconds > time) return; + static const LLCachedControl landmark(gSavedPerAccountSettings, "EmergencyTeleportLandmark"); if (landmark().empty()) return; LLUUID id(landmark); @@ -97,12 +100,12 @@ LLFloaterRegionRestarting::LLFloaterRegionRestarting(const LLSD& key) : LLEventTimer(1) , mRestartSeconds(NULL) , mSeconds(key["SECONDS"].asInteger()) +, mName(key.has("NAME") ? key["NAME"].asString() : gAgent.getRegion()->getName()) // , mShakeIterations() , mShakeMagnitude() { //buildFromFile("floater_region_restarting.xml"); LLUICtrlFactory::getInstance()->buildFloater(this, "floater_region_restarting.xml"); - mName = key.has("NAME") ? key["NAME"].asString() : gAgent.getRegion()->getName(); // center(); } @@ -120,7 +123,7 @@ LLFloaterRegionRestarting::~LLFloaterRegionRestarting() BOOL LLFloaterRegionRestarting::postBuild() { mRegionChangedConnection = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterRegionRestarting::regionChange, this)); - if (mSeconds <= 20) emergency_teleport(); // For emergency teleports + emergency_teleport(mSeconds); // For emergency teleports LLStringUtil::format_map_t args; std::string text; @@ -158,7 +161,7 @@ void LLFloaterRegionRestarting::refresh() args["[SECONDS]"] = llformat("%d", mSeconds); mRestartSeconds->setValue(getString("RestartSeconds", args)); - if (mSeconds == 20) emergency_teleport(); // For emergency teleports + emergency_teleport(mSeconds); // For emergency teleports if (!mSeconds) return; // Zero means we're done. --mSeconds; } @@ -233,6 +236,6 @@ void LLFloaterRegionRestarting::draw() void LLFloaterRegionRestarting::updateTime(const U32& time) { mSeconds = time; - if (mSeconds <= 20) emergency_teleport(); // For emergency teleports + emergency_teleport(mSeconds); // For emergency teleports sShakeState = SHAKE_START; } diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index fca367826..e029b7ed0 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -111,17 +111,6 @@ LLFloaterReporter::LLFloaterReporter() LLUICtrlFactory::getInstance()->buildFloater(this, "floater_report_abuse.xml"); } -// static -void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg) -{ - U32 region_flags; - msg->getU32("RegionInfo", "RegionFlags", region_flags); - - if (LLFloaterReporter::instanceExists() && LLFloaterReporter::getInstance()->getVisible()) - { - LLNotificationsUtil::add("HelpReportAbuseEmailLL"); - } -} // virtual BOOL LLFloaterReporter::postBuild() { @@ -155,6 +144,7 @@ BOOL LLFloaterReporter::postBuild() mDefaultSummary = getChild("details_edit")->getValue().asString(); + /* Singu Note: We only used this to trigger the notification that's now below, there's no point to this anymore. // send a message and ask for information about this region - // result comes back in processRegionInfo(..) LLMessageSystem* msg = gMessageSystem; @@ -163,6 +153,8 @@ BOOL LLFloaterReporter::postBuild() msg->addUUID("AgentID", gAgent.getID()); msg->addUUID("SessionID", gAgent.getSessionID()); gAgent.sendReliableMessage(); + */ + LLNotificationsUtil::add("HelpReportAbuseEmailLL"); // abuser name is selected from a list diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index 5e9d9ebf8..f48594473 100644 --- a/indra/newview/llfloaterreporter.h +++ b/indra/newview/llfloaterreporter.h @@ -38,7 +38,6 @@ #include "v3math.h" class LLAvatarName; -class LLMessageSystem; class LLViewerTexture; class LLInventoryItem; class LLViewerObject; @@ -104,9 +103,6 @@ public: static void addDescription(const std::string& description, LLMeanCollisionData *mcd = NULL); static void setDescription(const std::string& description, LLMeanCollisionData *mcd = NULL); - // static - static void processRegionInfo(LLMessageSystem* msg); - void setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id); private: diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 009cb3008..82ae608f3 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -3186,16 +3186,9 @@ BOOL LLSnapshotFloaterView::handleKey(KEY key, MASK mask, BOOL called_from_paren return LLFloaterView::handleKey(key, mask, called_from_parent); } - if (called_from_parent) - { - // pass all keystrokes down - LLFloaterView::handleKey(key, mask, called_from_parent); - } - else - { - // bounce keystrokes back down - LLFloaterView::handleKey(key, mask, TRUE); - } + // pass all keystrokes down + // bounce keystrokes back down + LLFloaterView::handleKey(key, mask, TRUE); return TRUE; } diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp index 8c767e64b..a6013fbb3 100644 --- a/indra/newview/llfloatertopobjects.cpp +++ b/indra/newview/llfloatertopobjects.cpp @@ -93,6 +93,7 @@ LLFloaterTopObjects::LLFloaterTopObjects() mCommitCallbackRegistrar.add("TopObjects.CommitObjectsList",boost::bind(&LLFloaterTopObjects::onCommitObjectsList, this)); mCommitCallbackRegistrar.add("TopObjects.TeleportToObject", boost::bind(&LLFloaterTopObjects::onTeleportToObject, this)); + mCommitCallbackRegistrar.add("TopObjects.CamToObject", boost::bind(&LLFloaterTopObjects::onCamToObject, this)); mCommitCallbackRegistrar.add("TopObjects.Kick", boost::bind(&LLFloaterTopObjects::onKick, this)); mCommitCallbackRegistrar.add("TopObjects.Profile", boost::bind(&LLFloaterTopObjects::onProfile, this)); @@ -494,23 +495,11 @@ void LLFloaterTopObjects::onGetByParcelName() void LLFloaterTopObjects::showBeacon() { - LLScrollListCtrl* list = getChild("objects_list"); - if (!list) return; - - LLScrollListItem* first_selected = list->getFirstSelected(); - if (!first_selected) return; - + LLVector3d pos_global = getSelectedPosition(); + if (pos_global.isExactlyZero()) return; + LLScrollListItem* first_selected = getChild("objects_list")->getFirstSelected(); std::string name = first_selected->getColumn(1)->getValue().asString(); - std::string pos_string = first_selected->getColumn(3)->getValue().asString(); - - F32 x, y, z; - S32 matched = sscanf(pos_string.c_str(), "<%g,%g,%g>", &x, &y, &z); - if (matched != 3) return; - - LLVector3 pos_agent(x, y, z); - LLVector3d pos_global = gAgent.getPosGlobalFromAgent(pos_agent); - std::string tooltip(""); - LLTracker::trackLocation(pos_global, name, tooltip, LLTracker::LOCATION_ITEM); + LLTracker::trackLocation(pos_global, name, LLStringUtil::null, LLTracker::LOCATION_ITEM); const LLUUID& taskid = first_selected->getUUID(); if(LLVOAvatar* voavatar = gObjectList.findAvatar(taskid)) @@ -522,26 +511,39 @@ void LLFloaterTopObjects::showBeacon() } } -void LLFloaterTopObjects::onTeleportToObject() +LLVector3d LLFloaterTopObjects::getSelectedPosition() const { LLScrollListCtrl* list = getChild("objects_list"); - if (!list) return; + if (!list) return LLVector3d::zero; LLScrollListItem* first_selected = list->getFirstSelected(); - if (!first_selected) return; + if (!first_selected) return LLVector3d::zero; std::string pos_string = first_selected->getColumn(3)->getValue().asString(); F32 x, y, z; S32 matched = sscanf(pos_string.c_str(), "<%g,%g,%g>", &x, &y, &z); - if (matched != 3) return; + if (matched != 3) return LLVector3d::zero; LLVector3 pos_agent(x, y, z); - LLVector3d pos_global = gAgent.getPosGlobalFromAgent(pos_agent); + return gAgent.getPosGlobalFromAgent(pos_agent); +} +void LLFloaterTopObjects::onTeleportToObject() +{ + LLVector3d pos_global = getSelectedPosition(); + if (pos_global.isExactlyZero()) return; gAgent.teleportViaLocation( pos_global ); } +void LLFloaterTopObjects::onCamToObject() +{ + LLVector3d pos_global = getSelectedPosition(); + if (pos_global.isExactlyZero()) return; + const LLUUID& id = getChild("objects_list")->getFirstSelected()->getUUID(); + gAgentCamera.setCameraPosAndFocusGlobal(pos_global + LLVector3d(3.5,1.35,0.75), pos_global, id); +} + void LLFloaterTopObjects::onKick() { LLScrollListCtrl* list = getChild("objects_list"); diff --git a/indra/newview/llfloatertopobjects.h b/indra/newview/llfloatertopobjects.h index a69de5fc9..9f1b2794d 100644 --- a/indra/newview/llfloatertopobjects.h +++ b/indra/newview/llfloatertopobjects.h @@ -74,7 +74,9 @@ private: void onDisableAll(); void onDisableSelected(); + LLVector3d getSelectedPosition() const; void onTeleportToObject(); + void onCamToObject(); void onKick(); void onProfile(); diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp index a60ca1ca7..3c0fe3af0 100644 --- a/indra/newview/llfloatervoiceeffect.cpp +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -75,7 +75,7 @@ BOOL LLFloaterVoiceEffect::postBuild() LLStyleSP link(new LLStyle); link->setLinkHREF(LLTrans::getString("voice_morphing_url")); link->setColor(gSavedSettings.getColor4("HTMLLinkColor")); - editor->appendStyledText(text, false, false, link); + editor->appendText(text, false, false, link); } mVoiceEffectList = getChild("voice_effect_list"); diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 8cae2ce8b..3fd05a12c 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -66,6 +66,7 @@ // Linden library includes #include "lldbstrings.h" +#include "llfavoritesbar.h" // Singu TODO: Favorites bar. #include "llfocusmgr.h" #include "llfontgl.h" #include "llgl.h" @@ -201,7 +202,7 @@ LLFolderView::LLFolderView( const std::string& name, mNeedsAutoRename(FALSE), mDebugFilters(FALSE), mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately - mFilter( new LLInventoryFilter(name) ), + mFilter(LLInventoryFilter::Params().name(name)), mShowSelectionContext(FALSE), mShowSingleSelection(FALSE), mArrangeGeneration(0), @@ -298,9 +299,6 @@ LLFolderView::~LLFolderView( void ) mFolders.clear(); mItemMap.clear(); - - delete mFilter; - mFilter = NULL; } BOOL LLFolderView::canFocusChildren() const @@ -371,7 +369,7 @@ U32 LLFolderView::toggleSearchType(std::string toggle) if (getFilterSubString().length()) { - mFilter->setModified(LLInventoryFilter::FILTER_RESTART); + mFilter.setModified(LLInventoryFilter::FILTER_RESTART); } return mSearchType; @@ -447,7 +445,7 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen LL_RECORD_BLOCK_TIME(FTM_ARRANGE); - filter_generation = mFilter->getFirstSuccessGeneration(); + filter_generation = mFilter.getFirstSuccessGeneration(); mMinWidth = 0; mHasVisibleChildren = hasFilteredDescendants(filter_generation); @@ -455,7 +453,7 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen mLastArrangeGeneration = getRoot()->getArrangeGeneration(); LLInventoryFilter::EFolderShow show_folder_state = - getRoot()->getFilter()->getShowFolderState(); + getRoot()->getFilter().getShowFolderState(); S32 total_width = LEFT_PAD; S32 running_height = mDebugFilters ? llceil(LLFontGL::getFontMonospace()->getLineHeight()) : 0; @@ -541,7 +539,7 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen const std::string LLFolderView::getFilterSubString(BOOL trim) { - return mFilter->getFilterSubString(trim); + return mFilter.getFilterSubString(trim); } static LLTrace::BlockTimerStatHandle FTM_FILTER("Filter Inventory"); @@ -633,6 +631,10 @@ LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) return NULL; } +LLFolderView::selected_items_t& LLFolderView::getSelectedItems( void ) +{ + return mSelectedItems; +} // Record the selected item and pass it down the hierachy. BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, @@ -745,7 +747,7 @@ void LLFolderView::sanitizeSelection() LLFolderViewItem* original_selected_item = getCurSelectedItem(); // Cache "Show all folders" filter setting - BOOL show_all_folders = (getRoot()->getFilter()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS); + BOOL show_all_folders = (getRoot()->getFilter().getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS); std::vector items_to_remove; selected_items_t::iterator item_iter; @@ -875,12 +877,13 @@ std::set LLFolderView::getSelectionList() const return selection; } -BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source) +bool LLFolderView::startDrag(LLToolDragAndDrop::ESource source) { std::vector types; uuid_vec_t cargo_ids; selected_items_t::iterator item_it; - BOOL can_drag = TRUE; + bool can_drag = true; + if (!mSelectedItems.empty()) { for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) @@ -908,7 +911,7 @@ void LLFolderView::draw() if (mDebugFilters) { std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", - mFilter->getCurrentGeneration(), mFilter->getFirstSuccessGeneration(), mFilter->getFirstRequiredGeneration()); + mFilter.getCurrentGeneration(), mFilter.getFirstSuccessGeneration(), mFilter.getFirstRequiredGeneration()); LLFontGL::getFontMonospace()->renderUTF8(current_filter_string, 0, 2, getRect().getHeight() - LLFontGL::getFontMonospace()->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); @@ -922,31 +925,25 @@ void LLFolderView::draw() } // while dragging, update selection rendering to reflect single/multi drag status - if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) + LLToolDragAndDrop& dad_inst(LLToolDragAndDrop::instance()); + if (dad_inst.hasMouseCapture()) { - EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept(); - if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) - { - setShowSingleSelection(TRUE); - } - else - { - setShowSingleSelection(FALSE); - } + EAcceptance last_accept = dad_inst.getLastAccept(); + setShowSingleSelection(last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE); } else { setShowSingleSelection(FALSE); } - - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size()) + static LLUICachedControl type_ahead_timeout("TypeAheadTimeout", 0); + if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout || !mSearchString.size()) { mSearchString.clear(); } if (hasVisibleChildren() - || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) + || mFilter.getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) { mStatusText.clear(); mStatusTextBox->setVisible( FALSE ); @@ -954,18 +951,13 @@ void LLFolderView::draw() else if (mShowEmptyMessage) { static LLCachedControl sSearchStatusColor(gColors, "InventorySearchStatusColor", LLColor4::white ); - if (LLInventoryModelBackgroundFetch::instance().folderFetchActive() || mCompletedFilterGeneration < mFilter->getFirstSuccessGeneration()) + if (LLInventoryModelBackgroundFetch::instance().folderFetchActive() || mCompletedFilterGeneration < mFilter.getFirstSuccessGeneration()) { mStatusText = LLTrans::getString("Searching"); } else { - // if(getFilter()) - // { - // LLStringUtil::format_map_t args; - // args["[SEARCH_TERM]"] = LLURI::escape(getFilter()->getFilterSubStringOrig()); - mStatusText = LLTrans::getString("InventoryNoMatchingItems"); //, args); - // } + mStatusText = getFilter().getEmptyLookupMessage(); } mStatusTextBox->setWrappedText(mStatusText); mStatusTextBox->setVisible( TRUE ); @@ -977,7 +969,7 @@ void LLFolderView::draw() // get preferable text height... S32 pixel_height = mStatusTextBox->getTextPixelHeight(); - bool height_changed = local_rect.getHeight() != pixel_height; + bool height_changed = (local_rect.getHeight() != pixel_height); if (height_changed) { // ... if it does not match current height, lets rearrange current view. @@ -988,6 +980,8 @@ void LLFolderView::draw() } } + // skip over LLFolderViewFolder::draw since we don't want the folder icon, label, + // and arrow for the root folder LLView::draw(); mDragAndDropThisFrame = FALSE; @@ -1006,7 +1000,7 @@ void LLFolderView::finishRenamingItem( void ) closeRenamer(); - // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible. + // List is re-sorted alphabetically, so scroll to make sure the selected item is visible. scrollToShowSelection(); } @@ -1064,7 +1058,7 @@ void LLFolderView::removeCutItems() } } -void LLFolderView::removeSelectedItems( void ) +void LLFolderView::removeSelectedItems() { if(getVisible() && getEnabled()) { @@ -1075,8 +1069,7 @@ void LLFolderView::removeSelectedItems( void ) // items, since the removal will futz with internal data // structures. std::vector items; - S32 count = mSelectedItems.size(); - if(count == 0) return; + if(mSelectedItems.empty()) return; LLFolderViewItem* item = NULL; selected_items_t::iterator item_it; for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) @@ -1094,7 +1087,7 @@ void LLFolderView::removeSelectedItems( void ) } // iterate through the new container. - count = items.size(); + size_t count = items.size(); LLUUID new_selection_id; if(count == 1) { @@ -1149,7 +1142,7 @@ void LLFolderView::removeSelectedItems( void ) setSelectionFromRoot(NULL, mParentPanel.get()->hasFocus()); } - for(S32 i = 0; i < count; ++i) + for(size_t i = 0; i < count; ++i) { listener = items[i]->getListener(); if(listener && (std::find(listeners.begin(), listeners.end(), listener) == listeners.end())) @@ -1546,25 +1539,37 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) case KEY_PAGE_UP: mSearchString.clear(); - mScrollContainer->pageUp(30); + if (mScrollContainer) + { + mScrollContainer->pageUp(30); + } handled = TRUE; break; case KEY_PAGE_DOWN: mSearchString.clear(); - mScrollContainer->pageDown(30); + if (mScrollContainer) + { + mScrollContainer->pageDown(30); + } handled = TRUE; break; case KEY_HOME: mSearchString.clear(); - mScrollContainer->goToTop(); + if (mScrollContainer) + { + mScrollContainer->goToTop(); + } handled = TRUE; break; case KEY_END: mSearchString.clear(); - mScrollContainer->goToBottom(); + if (mScrollContainer) + { + mScrollContainer->goToBottom(); + } break; case KEY_DOWN: @@ -1587,12 +1592,12 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if (next->isSelected()) { // shrink selection - changeSelectionFromRoot(last_selected, FALSE); + changeSelection(last_selected, FALSE); } else if (last_selected->getParentFolder() == next->getParentFolder()) { // grow selection - changeSelectionFromRoot(next, TRUE); + changeSelection(next, TRUE); } } } @@ -1650,12 +1655,12 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if (prev->isSelected()) { // shrink selection - changeSelectionFromRoot(last_selected, FALSE); + changeSelection(last_selected, FALSE); } else if (last_selected->getParentFolder() == prev->getParentFolder()) { // grow selection - changeSelectionFromRoot(prev, TRUE); + changeSelection(prev, TRUE); } } } @@ -1723,7 +1728,7 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) { mSearchString.erase(mSearchString.size() - 1, 1); } - search(getCurSelectedItem(), mSearchString, FALSE); + search(getCurSelectedItem(), wstring_to_utf8str(mSearchString), FALSE); handled = TRUE; } } @@ -1739,12 +1744,6 @@ BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) return FALSE; } - if (uni_char > 0x7f) - { - LL_WARNS() << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << LL_ENDL; - return FALSE; - } - BOOL handled = FALSE; if (mParentPanel.get()->hasFocus()) { @@ -1757,7 +1756,8 @@ BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) } //do text search - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout")) + static LLUICachedControl type_ahead_timeout("TypeAheadTimeout", 0.f); + if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout) { mSearchString.clear(); } @@ -1766,7 +1766,7 @@ BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) { mSearchString += uni_char; } - search(getCurSelectedItem(), mSearchString, FALSE); + search(getCurSelectedItem(), wstring_to_utf8str(mSearchString), FALSE); handled = TRUE; } @@ -1892,7 +1892,7 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) S32 count = mSelectedItems.size(); LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); if ( handled - && ( count > 0 && (hasVisibleChildren() || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) ) // show menu only if selected items are visible + && ( count > 0 && (hasVisibleChildren() || mFilter.getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) ) // show menu only if selected items are visible && menu ) { updateMenuOptions(menu); @@ -2090,7 +2090,7 @@ BOOL LLFolderView::getShowSelectionContext() return FALSE; } -void LLFolderView::setShowSingleSelection(BOOL show) +void LLFolderView::setShowSingleSelection(bool show) { if (show != mShowSingleSelection) { @@ -2161,6 +2161,11 @@ void LLFolderView::doIdle() { return; } + + // We have all items, now we can save them to favorites + BOOL collectFavoriteItems(LLInventoryModel::item_array_t&); // Singu TODO: Proper Favorites Bar + LLInventoryModel::item_array_t items; + collectFavoriteItems(items); LL_RECORD_BLOCK_TIME(FTM_INVENTORY); @@ -2171,9 +2176,9 @@ void LLFolderView::doIdle() arrangeAll(); } - mFilter->clearModified(); - BOOL filter_modified_and_active = mCompletedFilterGeneration < mFilter->getCurrentGeneration() && - mFilter->isNotDefault(); + mFilter.clearModified(); + BOOL filter_modified_and_active = mCompletedFilterGeneration < mFilter.getCurrentGeneration() && + mFilter.isNotDefault(); mNeedsAutoSelect = filter_modified_and_active && !(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture()); @@ -2199,7 +2204,7 @@ void LLFolderView::doIdle() // Open filtered folders for folder views with mAutoSelectOverride=TRUE. // Used by LLPlacesFolderView. - if (mAutoSelectOverride && !mFilter->getFilterSubString().empty()) + if (mAutoSelectOverride && !mFilter.getFilterSubString().empty()) { LLOpenFilteredFolders filter; applyFunctorRecursively(filter); @@ -2305,8 +2310,8 @@ void LLFolderView::idle(void* user_data) void LLFolderView::dumpSelectionInformation() { - LL_INFOS() << "LLFolderView::dumpSelectionInformation()" << LL_ENDL; - LL_INFOS() << "****************************************" << LL_ENDL; + LL_INFOS() << "LLFolderView::dumpSelectionInformation()" << LL_NEWLINE + << "****************************************" << LL_ENDL; selected_items_t::iterator item_it; for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { @@ -2352,15 +2357,15 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu) } // Successively filter out invalid options - - U32 flags = FIRST_SELECTED_ITEM; + U32 multi_select_flag = (mSelectedItems.size() > 1 ? ITEM_IN_MULTI_SELECTION : 0x0); + U32 flags = multi_select_flag | FIRST_SELECTED_ITEM; for (selected_items_t::iterator item_itor = mSelectedItems.begin(); item_itor != mSelectedItems.end(); ++item_itor) { LLFolderViewItem* selected_item = (*item_itor); selected_item->buildContextMenu(*menu, flags); - flags = 0x0; + flags = multi_select_flag; } // This adds a check for restrictions based on the entire @@ -2502,34 +2507,24 @@ void LLFolderView::onRenamerLost() } } -LLInventoryFilter* LLFolderView::getFilter() -{ - return mFilter; -} - void LLFolderView::setFilterPermMask( PermissionMask filter_perm_mask ) { - mFilter->setFilterPermissions(filter_perm_mask); -} - -bool LLFolderView::getFilterWorn() const -{ - return mFilter->getFilterWorn(); + mFilter.setFilterPermissions(filter_perm_mask); } U32 LLFolderView::getFilterObjectTypes() const { - return mFilter->getFilterObjectTypes(); + return mFilter.getFilterObjectTypes(); } PermissionMask LLFolderView::getFilterPermissions() const { - return mFilter->getFilterPermissions(); + return mFilter.getFilterPermissions(); } BOOL LLFolderView::isFilterModified() { - return mFilter->isNotDefault(); + return mFilter.isNotDefault(); } void delete_selected_item(void* user_data) diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 121109f79..7433e87bb 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -1,3 +1,4 @@ + /** * @file llfolderview.h * @brief Definition of the folder view collection of classes. @@ -49,6 +50,7 @@ #include "lldepthstack.h" #include "lleditmenuhandler.h" #include "llfontgl.h" +#include "llinventoryfilter.h" #include "lltooldraganddrop.h" #include "llviewertexture.h" @@ -87,6 +89,7 @@ public: virtual BOOL canFocusChildren() const; + virtual const LLFolderView* getRoot() const { return this; } virtual LLFolderView* getRoot() { return this; } LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() { return mGroupedItemModel; } @@ -101,13 +104,15 @@ public: void setSelectCallback(const signal_t::slot_type& cb) { mSelectSignal.connect(cb); } void setReshapeCallback(const signal_t::slot_type& cb) { mReshapeSignal.connect(cb); } void setAllowMultiSelect(BOOL allow) { mAllowMultiSelect = allow; } + void setShowEmptyMessage(bool show) { mShowEmptyMessage = show; } + /*void setShowItemLinkOverlays(bool show) { mShowItemLinkOverlays = show; } + void setAllowDropOnRoot(bool show) { mAllowDropOnRoot = show; }*/ - LLInventoryFilter* getFilter(); + LLInventoryFilter& getFilter() { return mFilter; } const std::string getFilterSubString(BOOL trim = FALSE); - bool getFilterWorn() const; U32 getFilterObjectTypes() const; PermissionMask getFilterPermissions() const; - // *NOTE: use getFilter()->getShowFolderState(); + // *NOTE: use getFilter().getShowFolderState(); //LLInventoryFilter::EFolderShow getShowFolderState(); U32 getSortOrder() const; BOOL isFilterModified(); @@ -132,15 +137,16 @@ public: void arrangeAll() { mArrangeGeneration++; } S32 getArrangeGeneration() { return mArrangeGeneration; } - // Apply filters to control visibility of inventory items + // applies filters to control visibility of items virtual void filter( LLInventoryFilter& filter); // Get the last selected item virtual LLFolderViewItem* getCurSelectedItem( void ); + selected_items_t& getSelectedItems( void ); // Record the selected item and pass it down the hierarchy. virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus); + BOOL take_keyboard_focus = TRUE); // Used by menu callbacks void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); @@ -148,19 +154,19 @@ public: // Called once a frame to update the selection if mSelectThisID has been set void updateSelection(); - // This method is used to toggle the selection of an item. - // Walks children and keeps track of selected objects. + // This method is used to toggle the selection of an item. Walks + // children, and keeps track of selected objects. virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); virtual std::set getSelectionList() const; - // Make sure if ancestor is selected, descendents are not + // Make sure if ancestor is selected, descendants are not void sanitizeSelection(); - void clearSelection(); + virtual void clearSelection(); void addToSelectionList(LLFolderViewItem* item); void removeFromSelectionList(LLFolderViewItem* item); - BOOL startDrag(LLToolDragAndDrop::ESource source); + bool startDrag(LLToolDragAndDrop::ESource source); void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; } void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } @@ -179,31 +185,24 @@ public: void autoOpenItem(LLFolderViewFolder* item); void closeAutoOpenedFolders(); BOOL autoOpenTest(LLFolderViewFolder* item); + BOOL isOpen() const { return TRUE; } // root folder always open // Copy & paste - virtual void copy(); virtual BOOL canCopy() const; + virtual void copy(); - virtual void cut(); virtual BOOL canCut() const; + virtual void cut(); - virtual void paste(); virtual BOOL canPaste() const; + virtual void paste(); - virtual void doDelete(); virtual BOOL canDoDelete() const; + virtual void doDelete(); // Public rename functionality - can only start the process void startRenamingSelectedItem( void ); - // These functions were used when there was only one folderview, - // and relied on that concept. This functionality is now handled - // by the listeners and the lldraganddroptool. - //LLFolderViewItem* getMovingItem() { return mMovingItem; } - //void setMovingItem( LLFolderViewItem* item ) { mMovingItem = item; } - //void dragItemIntoFolder( LLFolderViewItem* moving_item, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); - //void dragFolderIntoFolder( LLFolderViewFolder* moving_folder, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); - // LLView functionality ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent ); /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); @@ -218,6 +217,7 @@ public: EAcceptance* accept, std::string& tooltip_msg); /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) { setShowSelectionContext(FALSE); } virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); virtual void draw(); virtual void deleteAllChildren(); @@ -228,12 +228,13 @@ public: LLRect getVisibleRect(); BOOL search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward); - void setShowSelectionContext(BOOL show) { mShowSelectionContext = show; } + void setShowSelectionContext(bool show) { mShowSelectionContext = show; } BOOL getShowSelectionContext(); - void setShowSingleSelection(BOOL show); + void setShowSingleSelection(bool show); BOOL getShowSingleSelection() { return mShowSingleSelection; } F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } bool getUseEllipses() { return mUseEllipses; } + S32 getSelectedCount() { return (S32)mSelectedItems.size(); } void addItemID(const LLUUID& id, LLFolderViewItem* itemp); void removeItemID(const LLUUID& id); @@ -259,7 +260,7 @@ public: virtual S32 notify(const LLSD& info) ; bool useLabelSuffix() { return mUseLabelSuffix; } - void updateMenu(); + virtual void updateMenu(); void saveFolderState(); void restoreFolderState(); @@ -290,35 +291,37 @@ protected: LLHandle mPopupMenuHandle; selected_items_t mSelectedItems; - BOOL mKeyboardSelection; - BOOL mAllowMultiSelect; - BOOL mShowEmptyMessage; - BOOL mShowFolderHierarchy; + bool mKeyboardSelection, + mAllowMultiSelect, + mShowEmptyMessage, + mShowFolderHierarchy, + mNeedsScroll, + mPinningSelectedItem, + mNeedsAutoSelect, + mAutoSelectOverride, + mNeedsAutoRename, + mUseLabelSuffix, + mDragAndDropThisFrame, + mShowSelectionContext, + mShowSingleSelection; + LLUUID mSourceID; // Renaming variables and methods LLFolderViewItem* mRenameItem; // The item currently being renamed LLLineEditor* mRenamer; - BOOL mNeedsScroll; - BOOL mPinningSelectedItem; LLRect mScrollConstraintRect; - BOOL mNeedsAutoSelect; - BOOL mAutoSelectOverride; - BOOL mNeedsAutoRename; - bool mUseLabelSuffix; - - BOOL mDebugFilters; + + bool mDebugFilters; U32 mSortOrder; U32 mSearchType; LLDepthStack mAutoOpenItems; LLFolderViewFolder* mAutoOpenCandidate; LLFrameTimer mAutoOpenTimer; LLFrameTimer mSearchTimer; - std::string mSearchString; - LLInventoryFilter* mFilter; - BOOL mShowSelectionContext; - BOOL mShowSingleSelection; + LLWString mSearchString; + LLInventoryFilter mFilter; LLFrameTimer mMultiSelectionFadeTimer; S32 mArrangeGeneration; @@ -328,8 +331,6 @@ protected: S32 mMinWidth; S32 mRunningHeight; std::map mItemMap; - BOOL mDragAndDropThisFrame; - LLUUID mSelectThisID; // if non null, select this item LLHandle mParentPanel; @@ -353,11 +354,10 @@ public: }; -bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b); -bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b); // Flags for buildContextMenu() const U32 SUPPRESS_OPEN_ITEM = 0x1; const U32 FIRST_SELECTED_ITEM = 0x2; +const U32 ITEM_IN_MULTI_SELECTION = 0x4; #endif // LL_LLFOLDERVIEW_H diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h index f6e64a7d4..026dcf63f 100644 --- a/indra/newview/llfoldervieweventlistener.h +++ b/indra/newview/llfoldervieweventlistener.h @@ -64,6 +64,9 @@ public: virtual void previewItem( void ) = 0; virtual void selectItem(void) = 0; virtual void showProperties(void) = 0; + + virtual BOOL isItemWearable() const { return FALSE; } + virtual BOOL isItemRenameable() const = 0; virtual BOOL renameItem(const std::string& new_name) = 0; virtual void nameOrDescriptionChanged(void) const { } // Singu note: Currently only used by LLWearableBridge. diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index baa6093b0..1f87716f2 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -142,6 +142,10 @@ LLFolderView* LLFolderViewItem::getRoot() return mRoot; } +const LLFolderView* LLFolderViewItem::getRoot() const +{ + return mRoot; +} // Returns true if this object is a child (or grandchild, etc.) of potential_ancestor. BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) { @@ -210,12 +214,12 @@ BOOL LLFolderViewItem::potentiallyVisible() { // we haven't been checked against min required filter // or we have and we passed - return getLastFilterGeneration() < getRoot()->getFilter()->getFirstSuccessGeneration() || getFiltered(); + return getLastFilterGeneration() < getRoot()->getFilter().getFirstSuccessGeneration() || getFiltered(); } BOOL LLFolderViewItem::getFiltered() { - return mPassedFilter && mLastFilterGeneration >= getRoot()->getFilter()->getFirstSuccessGeneration(); + return mPassedFilter && mLastFilterGeneration >= getRoot()->getFilter().getFirstSuccessGeneration(); } BOOL LLFolderViewItem::getFiltered(S32 filter_generation) @@ -324,7 +328,7 @@ void LLFolderViewItem::filterFromRoot( void ) { LLFolderViewItem* root = getRoot(); - root->filter(*((LLFolderView*)root)->getFilter()); + root->filter(((LLFolderView*)root)->getFilter()); } // This function is called when the folder view is dirty. It's @@ -374,12 +378,6 @@ void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection, getRoot()->setSelection(selection, openitem, take_keyboard_focus); } -// helper function to change the selection from the root. -void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected) -{ - getRoot()->changeSelection(selection, selected); -} - std::set LLFolderViewItem::getSelectionList() const { std::set selection; @@ -587,7 +585,8 @@ void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) void LLFolderViewItem::openItem( void ) { - if( mListener ) + if (!mListener) return; + if (mAllowWear || mListener->isItemWearable()) { mListener->openItem(); } @@ -678,7 +677,7 @@ BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) { if(!mIsSelected) { - setSelectionFromRoot(this, FALSE); + getRoot()->setSelection(this, FALSE); } make_ui_sound("UISndClick"); return TRUE; @@ -699,7 +698,7 @@ BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) { if(mask & MASK_CONTROL) { - changeSelectionFromRoot(this, !mIsSelected); + getRoot()->changeSelection(this, !mIsSelected); } else if (mask & MASK_SHIFT) { @@ -707,12 +706,14 @@ BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) } else { - setSelectionFromRoot(this, FALSE); + getRoot()->setSelection(this, FALSE); } make_ui_sound("UISndClick"); } else { + // If selected, we reserve the decision of deselecting/reselecting to the mouse up moment. + // This is necessary so we maintain selection consistent when starting a drag. mSelectPending = TRUE; } @@ -781,6 +782,7 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) { gViewerWindow->setCursor(UI_CURSOR_NOLOCKED); } + return TRUE; } else @@ -821,7 +823,7 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) //...then select if(mask & MASK_CONTROL) { - changeSelectionFromRoot(this, !mIsSelected); + getRoot()->changeSelection(this, !mIsSelected); } else if (mask & MASK_SHIFT) { @@ -829,7 +831,7 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) } else { - setSelectionFromRoot(this, FALSE); + getRoot()->setSelection(this, FALSE); } } @@ -837,7 +839,10 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) if( hasMouseCapture() ) { - getRoot()->setShowSelectionContext(FALSE); + if (getRoot()) + { + getRoot()->setShowSelectionContext(FALSE); + } gFocusMgr.setMouseCapture( NULL ); } return TRUE; @@ -895,8 +900,12 @@ void LLFolderViewItem::draw() const S32 FOCUS_LEFT = 1; const LLFontGL* font = getLabelFontForStyle(mLabelStyle); - const BOOL in_inventory = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getRootFolderID()); - const BOOL in_library = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getLibraryRootFolderID()); + const LLUUID* id = getListener() ? &getListener()->getUUID() : nullptr; + const BOOL in_inventory = id && gInventory.isObjectDescendentOf(*id, gInventory.getRootFolderID()); + const BOOL in_library = id && !in_inventory && gInventory.isObjectDescendentOf(*id, gInventory.getLibraryRootFolderID()); + + // Don't draw filtered top level marketplace folders + if (in_inventory && !getFiltered() && depth_nesting_in_marketplace(*id) == 1) return; //--------------------------------------------------------------------------------// // Draw open folder arrow @@ -1032,7 +1041,7 @@ void LLFolderViewItem::draw() { color.mV[VALPHA] *= 0.5f; } - LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? + LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter().getCurrentGeneration() ? LLColor4(0.5f, 0.8f, 0.5f, 1.f) : LLColor4(0.8f, 0.5f, 0.5f, 1.f); LLFontGL::getFontMonospace()->renderUTF8(mStatusText, 0, text_left, y, filter_color, @@ -1124,7 +1133,7 @@ LLFolderViewFolder::LLFolderViewFolder( const std::string& name, LLUIImagePtr ic LLUIImagePtr icon_open, LLUIImagePtr icon_link, LLFolderView* root, - LLFolderViewEventListener* listener ): + LLFolderViewEventListener* listener): LLFolderViewItem( name, icon, icon_open, icon_link, 0, root, listener ), // 0 = no create time mIsOpen(FALSE), mExpanderHighlighted(FALSE), @@ -1158,7 +1167,7 @@ void LLFolderViewFolder::setFilteredFolder(bool filtered, S32 filter_generation) bool LLFolderViewFolder::getFilteredFolder(S32 filter_generation) { - return mPassedFolderFilter && mLastFilterGeneration >= getRoot()->getFilter()->getFirstSuccessGeneration(); + return mPassedFolderFilter && mLastFilterGeneration >= getRoot()->getFilter().getFirstSuccessGeneration(); } // addToFolder() returns TRUE if it succeeds. FALSE otherwise @@ -1185,7 +1194,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) mNeedsSort = false; } - mHasVisibleChildren = hasFilteredDescendants(filter_generation); + bool filtered = !getFilteredFolder(filter_generation); // calculate height as a single item (without any children), and reshapes rectangle to match LLFolderViewItem::arrange( width, height, filter_generation ); @@ -1198,12 +1207,53 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) F32 running_height = (F32)*height; F32 target_height = (F32)*height; + bool marketplace_top = mListener && depth_nesting_in_marketplace(mListener->getUUID()) == 1; + // are my children visible? if (needsArrange()) { // set last arrange generation first, in case children are animating // and need to be arranged again mLastArrangeGeneration = getRoot()->getArrangeGeneration(); + + mHasVisibleChildren = !filtered && hasFilteredDescendants(filter_generation); + if (mHasVisibleChildren) + { + // We have to verify that there's at least one child that's not filtered out + bool found = false; + // Try the items first + for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit) + { + LLFolderViewItem* itemp = (*iit); + found = itemp->getFiltered(filter_generation); + if (found) + break; + } + if (!found) + { + // If no item found, try the folders + for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) + { + LLFolderViewFolder* folderp = (*fit); + found = folderp->getListener() + && (folderp->getFiltered(filter_generation) + || (folderp->getFilteredFolder(filter_generation) + && folderp->hasFilteredDescendants(filter_generation))); + if (found) + break; + } + } + + mHasVisibleChildren = found; + } + + // Hide marketplaces top level folders that don't match the filter for this view + if (marketplace_top) + { + setVisible(!filtered); + if (filtered) mCurHeight = target_height = 0; + } + if (mIsOpen) { // Add sizes of children @@ -1278,7 +1328,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) } // animate current height towards target height - if (llabs(mCurHeight - mTargetHeight) > 1.f) + if (!(marketplace_top && filtered) && llabs(mCurHeight - mTargetHeight) > 1.f) { mCurHeight = lerp(mCurHeight, mTargetHeight, LLSmoothInterpolation::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT)); @@ -1547,12 +1597,14 @@ void LLFolderViewFolder::dirtyFilter() { // we're a folder, so invalidate our completed generation setCompletedFilterGeneration(-1, FALSE); + for (auto folder : mFolders) // Dirty our children's filters too, so they get updated. + folder->dirtyFilter(); LLFolderViewItem::dirtyFilter(); } BOOL LLFolderViewFolder::getFiltered() { - return getFilteredFolder(getRoot()->getFilter()->getFirstSuccessGeneration()) + return getFilteredFolder(getRoot()->getFilter().getFirstSuccessGeneration()) && LLFolderViewItem::getFiltered(); } @@ -1569,7 +1621,7 @@ BOOL LLFolderViewFolder::hasFilteredDescendants(S32 filter_generation) BOOL LLFolderViewFolder::hasFilteredDescendants() { - return mMostFilteredDescendantGeneration >= getRoot()->getFilter()->getCurrentGeneration(); + return mMostFilteredDescendantGeneration >= getRoot()->getFilter().getCurrentGeneration(); } // Passes selection information on to children and record selection @@ -1770,7 +1822,7 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo { return; } - if (selecting) + if (selecting && (*it)->getVisible()) { items.push_back(*it); } @@ -1789,7 +1841,7 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo return; } - if (selecting) + if (selecting && (*it)->getVisible()) { items.push_back(*it); } @@ -1811,7 +1863,7 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo return; } - if (selecting) + if (selecting && (*it)->getVisible()) { items.push_back(*it); } @@ -1830,7 +1882,7 @@ void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFo return; } - if (selecting) + if (selecting && (*it)->getVisible()) { items.push_back(*it); } @@ -1938,6 +1990,7 @@ void LLFolderViewFolder::destroyView() while (!mFolders.empty()) { LLFolderViewFolder *folderp = mFolders.back(); + mFolders.pop_back(); folderp->destroyView(); // removes entry from mFolders } @@ -2411,9 +2464,16 @@ BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, EAcceptance* accept, std::string& tooltip_msg) { + if (!mAllowDrop) + { + *accept = ACCEPT_NO; + tooltip_msg = LLTrans::getString("TooltipOutboxCannotDropOnRoot"); + return TRUE; + } + BOOL accepted = mListener && mListener->dragOrDrop(mask, drop, cargo_type, cargo_data); - - if (accepted) + + if (accepted) { mDragAndDropTarget = TRUE; *accept = ACCEPT_YES_MULTI; @@ -2572,9 +2632,9 @@ BOOL LLFolderViewFolder::potentiallyVisible() // folder should be visible by it's own filter status return LLFolderViewItem::potentiallyVisible() // or one or more of its descendants have passed the minimum filter requirement - || hasFilteredDescendants(getRoot()->getFilter()->getFirstSuccessGeneration()) + || hasFilteredDescendants(getRoot()->getFilter().getFirstSuccessGeneration()) // or not all of its descendants have been checked against minimum filter requirement - || getCompletedFilterGeneration() < getRoot()->getFilter()->getFirstSuccessGeneration(); + || getCompletedFilterGeneration() < getRoot()->getFilter().getFirstSuccessGeneration(); } // this does prefix traversal, as folders are listed above their contents @@ -2797,6 +2857,7 @@ bool LLInventorySort::updateSort(U32 order) mByDate = (order & LLInventoryFilter::SO_DATE); mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME); + mFoldersByWeight = (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_WEIGHT); return true; } return false; @@ -2804,39 +2865,9 @@ bool LLInventorySort::updateSort(U32 order) bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b) { - /* TO-DO - // ignore sort order for landmarks in the Favorites folder. - // they should be always sorted as in Favorites bar. See EXT-719 - if (a->getSortGroup() == SG_ITEM - && b->getSortGroup() == SG_ITEM - && a->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK - && b->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) - { - - static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - - LLUUID a_uuid = a->getParentFolder()->getListener()->getUUID(); - LLUUID b_uuid = b->getParentFolder()->getListener()->getUUID(); - - if ((a_uuid == favorites_folder_id && b_uuid == favorites_folder_id)) - { - // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem - // or to LLInvFVBridge - LLViewerInventoryItem* aitem = (static_cast(a->getListener()))->getItem(); - LLViewerInventoryItem* bitem = (static_cast(b->getListener()))->getItem(); - if (!aitem || !bitem) - return false; - S32 a_sort = aitem->getSortField(); - S32 b_sort = bitem->getSortField(); - return a_sort < b_sort; - } - }*/ - // We sort by name if we aren't sorting by date // OR if these are folders and we are sorting folders by name. - bool by_name = (!mByDate - || (mFoldersByName - && (a->getSortGroup() != SG_ITEM))); + bool by_name = ((!mByDate || (mFoldersByName && (a->getSortGroup() != SG_ITEM))) && !mFoldersByWeight); if (a->getSortGroup() != b->getSortGroup()) { @@ -2868,6 +2899,31 @@ bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolde return (compare < 0); } } + else if (mFoldersByWeight) + { + S32 weight_a = compute_stock_count(a->getListener()->getUUID()); + S32 weight_b = compute_stock_count(b->getListener()->getUUID()); + if (weight_a == weight_b) + { + // Equal weight -> use alphabetical order + return (LLStringUtil::compareDict(a->getListener()->getDisplayName(), b->getListener()->getDisplayName()) < 0); + } + else if (weight_a == COMPUTE_STOCK_INFINITE) + { + // No stock -> move a at the end of the list + return false; + } + else if (weight_b == COMPUTE_STOCK_INFINITE) + { + // No stock -> move b at the end of the list + return true; + } + else + { + // Lighter is first (sorted in increasing order of weight) + return (weight_a < weight_b); + } + } else { // BUG: This is very very slow. The getCreationDate() is log n in number diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index 56990360a..c33eb575b 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -61,7 +61,8 @@ public: : mSortOrder(0), mByDate(false), mSystemToTop(false), - mFoldersByName(false) { } + mFoldersByName(false), + mFoldersByWeight(false) { } // Returns true if order has changed bool updateSort(U32 order); @@ -74,6 +75,7 @@ private: bool mByDate; bool mSystemToTop; bool mFoldersByName; + bool mFoldersByWeight; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -139,12 +141,11 @@ protected: BOOL mIsLoading; LLTimer mTimeSinceRequestStart; bool mShowLoadStatus; + bool mAllowDrop; + bool mAllowWear; std::string mSearchable; U32 mSearchType; - - // helper function to change the selection from the root. - void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected); //Sets extra search criteria 'labels' to be compared against by filter. void updateExtraSearchCriteria(); @@ -240,13 +241,16 @@ public: void setShowLoadStatus(bool status) { mShowLoadStatus = status; } + void setAllowDrop(bool allow) { mAllowDrop = allow; } + void setAllowWear(bool allow) { mAllowWear = allow; } + // Call through to the viewed object and return true if it can be // removed. Returns true if it's removed. //virtual BOOL removeRecursively(BOOL single_item); BOOL remove(); // Build an appropriate context menu for the item. Flags unused. - void buildContextMenu(LLMenuGL& menu, U32 flags); + void buildContextMenu(class LLMenuGL& menu, U32 flags); // This method returns the actual name of the thing being // viewed. This method will ask the viewed object itself. @@ -283,12 +287,12 @@ public: virtual void openItem( void ); virtual void preview(void); - // Show children (unfortunate that this is called "open") + // Show children virtual void setOpen(BOOL open = TRUE) {}; - virtual BOOL isOpen() const { return FALSE; } virtual LLFolderView* getRoot(); + virtual const LLFolderView* getRoot() const; BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor ); S32 getIndentation() { return mIndentation; } @@ -532,12 +536,14 @@ public: virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, + BOOL drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg); - BOOL handleDragAndDropToThisFolder(MASK mask, BOOL drop, + BOOL handleDragAndDropToThisFolder(MASK mask, + BOOL drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, @@ -554,6 +560,7 @@ public: items_t::const_iterator getItemsBegin() const { return mItems.begin(); } items_t::const_iterator getItemsEnd() const { return mItems.end(); } items_t::size_type getItemsCount() const { return mItems.size(); } + LLFolderViewFolder* getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse); void gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector& items); }; diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 34d25d588..1d821e3f8 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -66,10 +66,6 @@ void fake_local_chat(std::string msg); // Longest time, in seconds, to wait for all animations to stop playing const F32 MAX_WAIT_ANIM_SECS = 30.f; -// If this gesture is a link, get the base gesture that this link points to, -// otherwise just return this id. -static const LLUUID& get_linked_uuid(const LLUUID& item_id); - // Lightweight constructor. // init() does the heavy lifting. LLGestureMgr::LLGestureMgr() @@ -259,7 +255,7 @@ void LLGestureMgr::activateGestureWithAsset(const LLUUID& item_id, BOOL inform_server, BOOL deactivate_similar) { - const LLUUID& base_item_id = get_linked_uuid(item_id); + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); if( !gAssetStorage ) { @@ -307,7 +303,7 @@ void LLGestureMgr::activateGestureWithAsset(const LLUUID& item_id, void LLGestureMgr::deactivateGesture(const LLUUID& item_id) { - const LLUUID& base_item_id = get_linked_uuid(item_id); + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); item_map_t::iterator it = mActive.find(base_item_id); if (it == mActive.end()) { @@ -352,7 +348,7 @@ void LLGestureMgr::deactivateGesture(const LLUUID& item_id) void LLGestureMgr::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id) { - const LLUUID& base_in_item_id = get_linked_uuid(in_item_id); + const LLUUID& base_in_item_id = gInventory.getLinkedItemID(in_item_id); uuid_vec_t gest_item_ids; // Deactivate all gestures that match @@ -439,7 +435,7 @@ void LLGestureMgr::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& i BOOL LLGestureMgr::isGestureActive(const LLUUID& item_id) { - const LLUUID& base_item_id = get_linked_uuid(item_id); + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); item_map_t::iterator it = mActive.find(base_item_id); return (it != mActive.end()); } @@ -447,7 +443,7 @@ BOOL LLGestureMgr::isGestureActive(const LLUUID& item_id) BOOL LLGestureMgr::isGesturePlaying(const LLUUID& item_id) { - const LLUUID& base_item_id = get_linked_uuid(item_id); + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); item_map_t::iterator it = mActive.find(base_item_id); if (it == mActive.end()) return FALSE; @@ -469,7 +465,7 @@ BOOL LLGestureMgr::isGesturePlaying(LLMultiGesture* gesture) void LLGestureMgr::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id) { - const LLUUID& base_item_id = get_linked_uuid(item_id); + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); item_map_t::iterator it = mActive.find(base_item_id); if (it == mActive.end()) { @@ -510,7 +506,7 @@ void LLGestureMgr::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_ges void LLGestureMgr::replaceGesture(const LLUUID& item_id, const LLUUID& new_asset_id) { - const LLUUID& base_item_id = get_linked_uuid(item_id); + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); item_map_t::iterator it = LLGestureMgr::instance().mActive.find(base_item_id); if (it == mActive.end()) @@ -613,7 +609,7 @@ void LLGestureMgr::playGesture(LLMultiGesture* gesture, bool local) // Convenience function that looks up the item_id for you. void LLGestureMgr::playGesture(const LLUUID& item_id, bool local) { - const LLUUID& base_item_id = get_linked_uuid(item_id); + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); item_map_t::iterator it = mActive.find(base_item_id); if (it == mActive.end()) return; @@ -1337,7 +1333,7 @@ void LLGestureMgr::stopGesture(LLMultiGesture* gesture) void LLGestureMgr::stopGesture(const LLUUID& item_id) { - const LLUUID& base_item_id = get_linked_uuid(item_id); + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); item_map_t::iterator it = mActive.find(base_item_id); if (it == mActive.end()) return; @@ -1493,13 +1489,4 @@ void LLGestureMgr::done() } } -// static -const LLUUID& get_linked_uuid(const LLUUID &item_id) -{ - LLViewerInventoryItem* item = gInventory.getItem(item_id); - if (item && item->getIsLinkType()) - { - return item->getLinkedUUID(); - } - return item_id; -} + diff --git a/indra/newview/llgroupnotify.cpp b/indra/newview/llgroupnotify.cpp index b2b678878..4d70787a7 100644 --- a/indra/newview/llgroupnotify.cpp +++ b/indra/newview/llgroupnotify.cpp @@ -170,23 +170,20 @@ LLGroupNotifyBox::LLGroupNotifyBox(const std::string& subject, DB_GROUP_NOTICE_MSG_STR_LEN, LLStringUtil::null, LLFontGL::getFontSansSerif(), - FALSE); + FALSE, + true); static const LLStyleSP headerstyle(new LLStyle(true,LLColor4::black,"SansSerifBig")); static const LLStyleSP datestyle(new LLStyle(true,LLColor4::black,"serif")); + static const LLStyleSP msgstyle(new LLStyle(true, LLColor4::grey4, LLStringUtil::null)); - text->appendStyledText(subject + "\n",false,false,headerstyle); + text->appendText(subject + '\n',false,false,headerstyle,false); - text->appendStyledText(time_buf,false,false,datestyle); - // Sadly, our LLTextEditor can't handle both styled and unstyled text - // at the same time. Hence this space must be styled. JC - text->appendColoredText(std::string(" "),false,false,LLColor4::grey4); - text->setParseHTML(TRUE); - text->appendColoredText(std::string("\n\n") + message,false,false,LLColor4::grey4); + text->appendText(time_buf,false,false,datestyle,false); + text->appendText(std::string(" \n\n") + message,false,false,msgstyle,false); LLColor4 semi_transparent(1.0f,1.0f,1.0f,0.8f); text->setCursor(0,0); - text->setEnabled(FALSE); text->setWordWrap(TRUE); //text->setTabStop(FALSE); // was interfering with copy-and-paste text->setTabsToNextField(TRUE); @@ -294,7 +291,8 @@ LLGroupNotifyBox::~LLGroupNotifyBox() // virtual BOOL LLGroupNotifyBox::handleRightMouseDown(S32 x, S32 y, MASK mask) { - moveToBack(); + if (!LLPanel::handleRightMouseDown(x, y, mask)) + moveToBack(); return TRUE; } diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 4388c46d8..b80225d3e 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -42,6 +42,7 @@ #include "llavatarnamecache.h" #include "llbutton.h" #include "llcombobox.h" +#include "llfloateravatarpicker.h" #include "llfloaterchat.h" #include "llfloaterinventory.h" #include "llfloaterwebcontent.h" // For web browser display of logs @@ -523,11 +524,14 @@ BOOL LLFloaterIMPanel::postBuild() if (LLComboBox* flyout = findChild("instant_message_flyout")) { flyout->setCommitCallback(boost::bind(&LLFloaterIMPanel::onFlyoutCommit, this, flyout, _2)); - if (is_agent_mappable(mOtherParticipantUUID)) - flyout->add(getString("find on map"), -2); - addDynamics(flyout); - if (gObjectList.findAvatar(mOtherParticipantUUID)) - flyout->add(getString("focus"), -3); + if (mSessionType == P2P_SESSION) + { + if (is_agent_mappable(mOtherParticipantUUID)) + flyout->add(getString("find on map"), -2); + addDynamics(flyout); + if (gObjectList.findAvatar(mOtherParticipantUUID)) + flyout->add(getString("focus"), -3); + } } if (LLUICtrl* ctrl = findChild("tp_btn")) ctrl->setCommitCallback(boost::bind(static_cast(LLAvatarActions::offerTeleport), mOtherParticipantUUID)); @@ -800,7 +804,7 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, LLColor4 incol // Convert the name to a hotlink and add to message. LLStyleSP source_style = LLStyleMap::instance().lookupAgent(source); source_style->mItalic = is_irc; - mHistoryEditor->appendStyledText(show_name,false,prepend_newline,source_style); + mHistoryEditor->appendText(show_name,false,prepend_newline,source_style, false); } prepend_newline = false; } @@ -811,7 +815,7 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, LLColor4 incol style->setColor(incolor); style->mItalic = is_irc; style->mBold = from_user && gSavedSettings.getBOOL("SingularityBoldGroupModerator") && isModerator(source); - mHistoryEditor->appendStyledText(utf8msg, false, prepend_newline, style); + mHistoryEditor->appendText(utf8msg, false, prepend_newline, style, false); } if (log_to_file @@ -980,6 +984,96 @@ bool LLFloaterIMPanel::isInviteAllowed() const return mSessionType == ADHOC_SESSION; } +void LLFloaterIMPanel::onAddButtonClicked() +{ + LLView * button = findChild("instant_message_flyout"); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterIMPanel::addSessionParticipants, this, _1), TRUE, TRUE, FALSE, root_floater->getName(), button); + if (!picker) + { + return; + } + + // Need to disable 'ok' button when selected users are already in conversation. + picker->setOkBtnEnableCb(boost::bind(&LLFloaterIMPanel::canAddSelectedToChat, this, _1)); + + if (root_floater) + { + root_floater->addDependentFloater(picker); + } +} + +bool LLFloaterIMPanel::canAddSelectedToChat(const uuid_vec_t& uuids) const +{ + switch (mSessionType) + { + case P2P_SESSION: return true; // Don't bother blocking self or peer + case ADHOC_SESSION: + { + // For a conference session we need to check against the list from LLSpeakerMgr, + // because this list may change when participants join or leave the session. + + LLSpeakerMgr::speaker_list_t speaker_list; + LLIMSpeakerMgr* speaker_mgr = getSpeakerManager(); + if (speaker_mgr) + { + speaker_mgr->getSpeakerList(&speaker_list, true); + } + + for (const auto& id : uuids) + for (const LLPointer& speaker : speaker_list) + if (id == speaker->mID) + return false; + } + return true; + default: return false; + } +} + +void LLFloaterIMPanel::addSessionParticipants(const uuid_vec_t& uuids) +{ + if (mSessionType == P2P_SESSION) + { + LLSD payload; + LLSD args; + + LLNotificationsUtil::add("ConfirmAddingChatParticipants", args, payload, + boost::bind(&LLFloaterIMPanel::addP2PSessionParticipants, this, _1, _2, uuids)); + } + else inviteToSession(uuids); +} + +void LLFloaterIMPanel::addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) + { + return; + } + + LLVoiceChannel* voice_channel = LLActiveSpeakerMgr::getInstance()->getVoiceChannel(); + + // first check whether this is a voice session + bool is_voice_call = voice_channel != nullptr && voice_channel->getSessionID() == mSessionUUID && voice_channel->isActive(); + + uuid_vec_t temp_ids; + + // Add the initial participant of a P2P session + temp_ids.push_back(mOtherParticipantUUID); + temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); + + // Start a new ad hoc voice call if we invite new participants to a P2P call, + // or start a text chat otherwise. + if (is_voice_call) + { + LLAvatarActions::startAdhocCall(temp_ids); + } + else + { + LLAvatarActions::startConference(temp_ids); + } +} + void LLFloaterIMPanel::removeDynamics(LLComboBox* flyout) { flyout->remove(mDing ? getString("ding on") : getString("ding off")); @@ -1012,30 +1106,38 @@ void LLFloaterIMPanel::onFlyoutCommit(LLComboBox* flyout, const LLSD& value) { if (value.isUndefined() || value.asInteger() == 0) { - LLAvatarActions::showProfile(mOtherParticipantUUID); - return; + switch (mSessionType) + { + case GROUP_SESSION: LLGroupActions::show(mOtherParticipantUUID); return; + case P2P_SESSION: LLAvatarActions::showProfile(mOtherParticipantUUID); return; + default: onClickHistory(); return; // If there's no profile for this type, we should be the history button. + } } - int option = value.asInteger(); - if (option == 1) onClickHistory(); - else if (option == 2) LLAvatarActions::offerTeleport(mOtherParticipantUUID); - else if (option == 3) LLAvatarActions::teleportRequest(mOtherParticipantUUID); - else if (option == 4) LLAvatarActions::pay(mOtherParticipantUUID); - else if (option == 5) LLAvatarActions::inviteToGroup(mOtherParticipantUUID); - else if (option == -1) copy_profile_uri(mOtherParticipantUUID); - else if (option == -2) LLAvatarActions::showOnMap(mOtherParticipantUUID); - else if (option == -3) gAgentCamera.lookAtObject(mOtherParticipantUUID); - else if (option >= 6) // Options that use dynamic items + switch (int option = value.asInteger()) + { + case 1: onClickHistory(); break; + case 2: LLAvatarActions::offerTeleport(mOtherParticipantUUID); break; + case 3: LLAvatarActions::teleportRequest(mOtherParticipantUUID); break; + case 4: LLAvatarActions::pay(mOtherParticipantUUID); break; + case 5: LLAvatarActions::inviteToGroup(mOtherParticipantUUID); break; + case -1: copy_profile_uri(mOtherParticipantUUID); break; + case -2: LLAvatarActions::showOnMap(mOtherParticipantUUID); break; + case -3: gAgentCamera.lookAtObject(mOtherParticipantUUID); break; + case -4: onAddButtonClicked(); break; + default: // Options >= 6 use dynamic items { - // First remove them all removeDynamics(flyout); // Toggle as requested, adjust the strings - if (option == 6) mDing = !mDing; - else if (option == 7) mRPMode = !mRPMode; - else if (option == 8) LLAvatarActions::isFriend(mOtherParticipantUUID) ? LLAvatarActions::removeFriendDialog(mOtherParticipantUUID) : LLAvatarActions::requestFriendshipDialog(mOtherParticipantUUID); - else if (option == 9) LLAvatarActions::toggleBlock(mOtherParticipantUUID); + switch (option) + { + case 6: mDing = !mDing; break; + case 7: mRPMode = !mRPMode; break; + case 8: LLAvatarActions::isFriend(mOtherParticipantUUID) ? LLAvatarActions::removeFriendDialog(mOtherParticipantUUID) : LLAvatarActions::requestFriendshipDialog(mOtherParticipantUUID); break; + case 9: LLAvatarActions::toggleBlock(mOtherParticipantUUID); break; + } // Last add them back addDynamics(flyout); @@ -1044,6 +1146,7 @@ void LLFloaterIMPanel::onFlyoutCommit(LLComboBox* flyout, const LLSD& value) if (flyout->remove(focus)) // If present, reorder to bottom. flyout->add(focus, -3); } + } } void show_log_browser(const std::string& name, const std::string& id) @@ -1124,7 +1227,7 @@ void deliver_message(const std::string& utf8_text, if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id))) { // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice. - sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text); +// sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text); } if(!sent) diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index e3789b4ab..6a2e54dbe 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -165,6 +165,11 @@ private: // test if local agent can add agents. bool isInviteAllowed() const; + void onAddButtonClicked(); + void addSessionParticipants(const uuid_vec_t& uuids); + void addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids); + bool canAddSelectedToChat(const uuid_vec_t& uuids) const; + // Called whenever the user starts or stops typing. // Sends the typing state to the other user if necessary. void setTyping(bool typing); diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index ac3dea484..6dba5b453 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -1097,31 +1097,39 @@ bool LLIMMgr::isNonFriendSessionNotified(const LLUUID& session_id) return mNotifiedNonFriendSessions.end() != mNotifiedNonFriendSessions.find(session_id); } +std::string LLIMMgr::getOfflineMessage(const LLUUID& id) +{ + std::string full_name; + if (LLAvatarNameCache::getNSName(id, full_name)) + { + LLUIString offline = LLTrans::getString("offline_message"); + offline.setArg("[NAME]", full_name); + return offline; + } + return LLStringUtil::null; +} + void LLIMMgr::noteOfflineUsers( LLFloaterIMPanel* floater, const std::vector& ids) { - S32 count = ids.size(); - if(count == 0) + if(ids.empty()) { const std::string& only_user = LLTrans::getString("only_user_message"); floater->addHistoryLine(only_user, gSavedSettings.getColor4("SystemChatColor")); } else { - const LLRelationship* info = NULL; + const LLRelationship* info = nullptr; LLAvatarTracker& at = LLAvatarTracker::instance(); - for(S32 i = 0; i < count; ++i) + for(const auto& id : ids) { - info = at.getBuddyInfo(ids.at(i)); - std::string full_name; - if (info - && !info->isOnline() - && LLAvatarNameCache::getNSName(ids.at(i), full_name)) + info = at.getBuddyInfo(id); + if (info && !info->isOnline()) { - LLUIString offline = LLTrans::getString("offline_message"); - offline.setArg("[NAME]", full_name); - floater->addHistoryLine(offline, gSavedSettings.getColor4("SystemChatColor")); + auto offline(getOfflineMessage(id)); + if (!offline.empty()) + floater->addHistoryLine(offline, gSavedSettings.getColor4("SystemChatColor")); } } } diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index b5a0f56e4..12aa856bd 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -188,6 +188,8 @@ public: bool isNonFriendSessionNotified(const LLUUID& session_id); + static std::string getOfflineMessage(const LLUUID& id); + private: // create a panel and update internal representation for // consistency. Returns the pointer, caller (the class instance diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index 6666d8a1a..16e0b9c6c 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -44,6 +44,7 @@ #include "llinventorymodelbackgroundfetch.h" #include "llinventorypanel.h" #include "llmakeoutfitdialog.h" +#include "llmarketplacefunctions.h" #include "llnotificationsutil.h" #include "llpanelmaininventory.h" #include "llpanelobjectinventory.h" @@ -60,37 +61,155 @@ extern LLUUID gAgentID; using namespace LLOldEvents; +bool contains_nocopy_items(const LLUUID& id); + namespace LLInventoryAction { - bool doToSelected(LLFolderView* folder, std::string action); + void callback_doToSelected(const LLSD& notification, const LLSD& response, LLFolderView* folder, const std::string& action); + void callback_copySelected(const LLSD& notification, const LLSD& response, class LLFolderView* root, const std::string& action); + bool doToSelected(LLFolderView* root, std::string action, BOOL user_confirm = TRUE); + + void buildMarketplaceFolders(LLFolderView* root); + void updateMarketplaceFolders(); + std::list sMarketplaceFolders; // Marketplace folders that will need update once the action is completed } typedef LLMemberListener object_inventory_listener_t; typedef LLMemberListener inventory_listener_t; typedef LLMemberListener inventory_panel_listener_t; -bool LLInventoryAction::doToSelected(LLFolderView* folder, std::string action) +// Callback for doToSelected if DAMA required... +void LLInventoryAction::callback_doToSelected(const LLSD& notification, const LLSD& response, LLFolderView* folder, const std::string& action) { - if (!folder) + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + doToSelected(folder, action, false); + } +} + +void LLInventoryAction::callback_copySelected(const LLSD& notification, const LLSD& response, class LLFolderView* root, const std::string& action) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES, Move no copy item(s) + { + doToSelected(root, "copy_or_move_to_marketplace_listings", false); + } + else if (option == 1) // NO, Don't move no copy item(s) (leave them behind) + { + doToSelected(root, "copy_to_marketplace_listings", false); + } +} + +// Succeeds iff all selected items are bridges to objects, in which +// case returns their corresponding uuids. +bool get_selection_object_uuids(LLFolderView *root, uuid_vec_t& ids) +{ + uuid_vec_t results; + //S32 no_object = 0; + std::set selectedItems = root->getSelectionList(); + for(std::set::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) + { + const LLUUID& id(*it); + + if(id.notNull()) + { + results.push_back(id); + } + else + { + return false; //non_object++; + } + } + //if (non_object == 0) + { + ids = results; return true; + } + //return false; +} + +bool LLInventoryAction::doToSelected(LLFolderView* root, std::string action, BOOL user_confirm) +{ + if (!root) + return true; + + std::set selected_items = root->getSelectionList(); + + // Prompt the user and check for authorization for some marketplace active listing edits + if (user_confirm && (("delete" == action) || ("cut" == action) || ("rename" == action) || ("properties" == action) || ("task_properties" == action) || ("open" == action))) + { + std::set::iterator set_iter = std::find_if(selected_items.begin(), selected_items.end(), boost::bind(&depth_nesting_in_marketplace, _1) >= 0); + if (set_iter != selected_items.end()) + { + if ("open" == action) + { + if (get_can_item_be_worn(*set_iter)) + { + // Wearing an object from any listing, active or not, is verbotten + LLNotificationsUtil::add("AlertMerchantListingCannotWear"); + return true; + } + // Note: we do not prompt for change when opening items (e.g. textures or note cards) on the marketplace... + } + else if (LLMarketplaceData::instance().isInActiveFolder(*set_iter) || + LLMarketplaceData::instance().isListedAndActive(*set_iter)) + { + // If item is in active listing, further confirmation is required + if ((("cut" == action) || ("delete" == action)) && (LLMarketplaceData::instance().isListed(*set_iter) || LLMarketplaceData::instance().isVersionFolder(*set_iter))) + { + // Cut or delete of the active version folder or listing folder itself will unlist the listing so ask that question specifically + LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, root, action)); + return true; + } + // Any other case will simply modify but not unlist a listing + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, root, action)); + return true; + } + // Cutting or deleting a whole listing needs confirmation as SLM will be archived and inaccessible to the user + else if (LLMarketplaceData::instance().isListed(*set_iter) && (("cut" == action) || ("delete" == action))) + { + LLNotificationsUtil::add("ConfirmListingCutOrDelete", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, root, action)); + return true; + } + } + } + // Copying to the marketplace needs confirmation if nocopy items are involved + if (("copy_to_marketplace_listings" == action)) + { + std::set::iterator set_iter = selected_items.begin(); + if (contains_nocopy_items(*set_iter)) + { + LLNotificationsUtil::add("ConfirmCopyToMarketplace", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_copySelected, _1, _2, root, action)); + return true; + } + } + + // Keep track of the marketplace folders that will need update of their status/name after the operation is performed + buildMarketplaceFolders(root); + LLInventoryModel* model = &gInventory; if ("rename" == action) { - folder->startRenamingSelectedItem(); + root->startRenamingSelectedItem(); + // Update the marketplace listings that have been affected by the operation + updateMarketplaceFolders(); return true; } - else if ("delete" == action) + + if ("delete" == action) { - folder->removeSelectedItems(); + root->removeSelectedItems(); + + // Update the marketplace listings that have been affected by the operation + updateMarketplaceFolders(); return true; } - else if ("copy" == action || "cut" == action) + if ("copy" == action || "cut" == action) { LLInventoryClipboard::instance().reset(); } - std::set selected_items = folder->getSelectionList(); - LLMultiFloater* multi_floaterp = NULL; if (("task_open" == action || "open" == action) && selected_items.size() > 1) @@ -116,14 +235,30 @@ bool LLInventoryAction::doToSelected(LLFolderView* folder, std::string action) std::set::iterator set_iter; - for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) - { - LLFolderViewItem* folder_item = folder->getItemByID(*set_iter); - if(!folder_item) continue; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); - if(!bridge) continue; - bridge->performAction(model, action); + // This rather warty piece of code is to allow items to be removed + // from the avatar in a batch, eliminating redundant + // updateAppearanceFromCOF() requests further down the line. (MAINT-4918) + // + // There are probably other cases where similar batching would be + // desirable, but the current item-by-item performAction() + // approach would need to be reworked. + uuid_vec_t object_uuids_to_remove; + if (isRemoveAction(action) && get_selection_object_uuids(root, object_uuids_to_remove)) + { + LLAppearanceMgr::instance().removeItemsFromAvatar(object_uuids_to_remove); + } + else + { + for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + LLFolderViewItem* folder_item = root->getItemByID(*set_iter); + if(!folder_item) continue; + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); + if(!bridge) continue; + + bridge->performAction(model, action); + } } LLFloater::setFloaterHost(NULL); @@ -135,7 +270,94 @@ bool LLInventoryAction::doToSelected(LLFolderView* folder, std::string action) return true; } -struct LLNewWindow : public inventory_listener_t +void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) +{ + // Make a list of all marketplace folders containing the elements in the selected list + // as well as the elements themselves. + // Once those elements are updated (cut, delete in particular but potentially any action), their + // containing folder will need to be updated as well as their initially containing folder. For + // instance, moving a stock folder from a listed folder to another will require an update of the + // target listing *and* the original listing. So we need to keep track of both. + // Note: do not however put the marketplace listings root itself in this list or the whole marketplace data will be rebuilt. + sMarketplaceFolders.clear(); + const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplacelistings_id.isNull()) + { + return; + } + + std::set selected_items = root->getSelectionList(); + for (std::set::const_iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + const LLInventoryObject* obj(gInventory.getObject(*set_iter)); + if (!obj) continue; + if (gInventory.isObjectDescendentOf(obj->getParentUUID(), marketplacelistings_id)) + { + const LLUUID& parent_id = obj->getParentUUID(); + if (parent_id != marketplacelistings_id) + { + sMarketplaceFolders.push_back(parent_id); + } + const LLUUID& curr_id = obj->getUUID(); + if (curr_id != marketplacelistings_id) + { + sMarketplaceFolders.push_back(curr_id); + } + } + } + // Suppress dupes in the list so we wo't update listings twice + sMarketplaceFolders.sort(); + sMarketplaceFolders.unique(); +} + +void LLInventoryAction::updateMarketplaceFolders() +{ + while (!sMarketplaceFolders.empty()) + { + update_marketplace_category(sMarketplaceFolders.back()); + sMarketplaceFolders.pop_back(); + } +} + + + +class LLDoToSelectedPanel : public object_inventory_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLPanelObjectInventory *panel = mPtr; + LLFolderView* folder = panel->getRootFolder(); + if(!folder) return true; + + return LLInventoryAction::doToSelected(folder, userdata.asString()); + } +}; + +class LLDoToSelectedFloater : public inventory_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLInventoryPanel *panel = mPtr->getPanel(); + LLFolderView* folder = panel->getRootFolder(); + if(!folder) return true; + + return LLInventoryAction::doToSelected(folder, userdata.asString()); + } +}; + +class LLDoToSelected : public inventory_panel_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLInventoryPanel *panel = mPtr; + LLFolderView* folder = panel->getRootFolder(); + if(!folder) return true; + + return LLInventoryAction::doToSelected(folder, userdata.asString()); + } +}; + +class LLNewWindow : public inventory_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { @@ -472,12 +694,12 @@ struct LLAttachObject : public inventory_panel_listener_t void init_object_inventory_panel_actions(LLPanelObjectInventory *panel) { - (new LLBindMemberListener(panel, "Inventory.DoToSelected", boost::bind(&LLInventoryAction::doToSelected, boost::bind(&LLPanelObjectInventory::getRootFolder, panel), _2))); + (new LLBindMemberListener(panel, "Inventory.DoToSelected", boost::bind(&LLInventoryAction::doToSelected, boost::bind(&LLPanelObjectInventory::getRootFolder, panel), _2, true))); } void init_inventory_actions(LLPanelMainInventory *floater) { - (new LLBindMemberListener(floater, "Inventory.DoToSelected", boost::bind(&LLInventoryAction::doToSelected, boost::bind(&LLPanelMainInventory::getRootFolder, floater), _2))); + (new LLBindMemberListener(floater, "Inventory.DoToSelected", boost::bind(&LLInventoryAction::doToSelected, boost::bind(&LLPanelMainInventory::getRootFolder, floater), _2, true))); (new LLBindMemberListener(floater, "Inventory.CloseAllFolders", boost::bind(&LLInventoryPanel::closeAllFolders, boost::bind(&LLPanelMainInventory::getPanel, floater)))); (new LLBindMemberListener(floater, "Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "", LLFolderType::FT_TRASH))); (new LLBindMemberListener(floater, "Inventory.DoCreate", boost::bind(&do_create, &gInventory, boost::bind(&LLPanelMainInventory::getPanel, floater), _2, (LLFolderBridge*)0))); @@ -490,7 +712,7 @@ void init_inventory_actions(LLPanelMainInventory *floater) void init_inventory_panel_actions(LLInventoryPanel *panel) { - (new LLBindMemberListener(panel, "Inventory.DoToSelected", boost::bind(&LLInventoryAction::doToSelected, boost::bind(&LLInventoryPanel::getRootFolder, panel), _2))); + (new LLBindMemberListener(panel, "Inventory.DoToSelected", boost::bind(&LLInventoryAction::doToSelected, boost::bind(&LLInventoryPanel::getRootFolder, panel), _2, true))); (new LLAttachObject())->registerListener(panel, "Inventory.AttachObject"); (new LLBindMemberListener(panel, "Inventory.CloseAllFolders", boost::bind(&LLInventoryPanel::closeAllFolders, panel))); (new LLBindMemberListener(panel, "Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH))); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 537358377..49693f6c2 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -42,8 +42,10 @@ #include "llattachmentsmgr.h" #include "llavataractions.h" #include "llcallingcard.h" +#include "llfavoritesbar.h" // management of favorites folder #include "llfirstuse.h" #include "llfloatercustomize.h" +#include "llfloatermarketplacelistings.h" #include "llfloateropenobject.h" #include "llfloaterproperties.h" #include "llfloaterworldmap.h" @@ -72,6 +74,7 @@ #include "llselectmgr.h" #include "lltooldraganddrop.h" #include "lltrans.h" +#include "llurlaction.h" #include "llviewerassettype.h" #include "llviewerfoldertype.h" #include "llviewermenu.h" @@ -92,11 +95,6 @@ #include "rlvlocks.h" // [/RLVa:KB] -// Marketplace outbox current disabled -#define ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU 1 -#define ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU 1 -#define BLOCK_WORN_ITEMS_IN_OUTBOX 1 - typedef std::pair two_uuids_t; typedef std::list two_uuids_list_t; @@ -119,7 +117,7 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*); bool confirm_attachment_rez(const LLSD& notification, const LLSD& response); static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); -void build_context_menu_folder_options(LLInventoryModel* mModel, const LLUUID& mUUID, menuentry_vec_t& mItems, menuentry_vec_t& mDisabledItems); +void build_context_menu_folder_options(LLInventoryModel* mModel, const LLUUID& mUUID, menuentry_vec_t& items, menuentry_vec_t& disabled_items); // Helper functions @@ -133,16 +131,6 @@ bool isRemoveAction(const std::string& action) return ("take_off" == action || "detach" == action || "deactivate" == action); } -bool isMarketplaceCopyAction(const std::string& action) -{ - return (("copy_to_outbox" == action) || ("move_to_outbox" == action)); -} - -bool isMarketplaceSendAction(const std::string& action) -{ - return ("send_to_marketplace" == action); -} - // Used by LLFolderBridge as callback for directory fetching recursion class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver { @@ -211,7 +199,6 @@ const std::string& LLInvFVBridge::getDisplayName() const { buildDisplayName(); } - return mDisplayName; } @@ -278,8 +265,42 @@ BOOL LLInvFVBridge::isLibraryItem() const BOOL LLInvFVBridge::cutToClipboard() { if(isItemMovable()) + { + const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const BOOL cut_from_marketplacelistings = gInventory.isObjectDescendentOf(mUUID, marketplacelistings_id); + + if (cut_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(mUUID) || + LLMarketplaceData::instance().isListedAndActive(mUUID))) + { + // Prompt the user if cutting from a marketplace active listing + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLInvFVBridge::callback_cutToClipboard, this, _1, _2)); + } + else + { + // Otherwise just perform the cut + return perform_cutToClipboard(); + } + } + return FALSE; +} + +// Callback for cutToClipboard if DAMA required... +BOOL LLInvFVBridge::callback_cutToClipboard(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + return perform_cutToClipboard(); + } + return FALSE; +} + +BOOL LLInvFVBridge::perform_cutToClipboard() +{ + if (isItemMovable()) { LLInventoryClipboard::instance().cut(mUUID); + LLFolderView::removeCutItems(); return true; } return false; @@ -296,15 +317,23 @@ BOOL LLInvFVBridge::copyToClipboard() const return FALSE; } -// *TODO: make sure this does the right thing void LLInvFVBridge::showProperties() { - show_item_profile(mUUID); - - // Disable old properties floater; this is replaced by the sidepanel. - /* - LLFloaterReg::showInstance("properties", mUUID); - */ + if (isMarketplaceListingsFolder()) + { + show_item_profile(mUUID); + LLFloater* floater_properties = LLFloaterProperties::find(mUUID, LLUUID::null); + if (floater_properties) + { + floater_properties->setVisible(true); + floater_properties->setFrontmost(true); + //floater_properties->setVisibleAndFrontmost(); + } + } + else + { + show_item_profile(mUUID); + } } void LLInvFVBridge::removeBatch(std::vector& batch) @@ -320,7 +349,7 @@ void LLInvFVBridge::removeBatch(std::vector& batch) U32 i,j; for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if (item) @@ -333,7 +362,7 @@ void LLInvFVBridge::removeBatch(std::vector& batch) } for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if (cat) @@ -374,7 +403,7 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector& // being deleted. for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -387,7 +416,7 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector& for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -428,7 +457,7 @@ void LLInvFVBridge::removeBatchNoCheck(std::vector& for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.at(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if(cat) @@ -509,7 +538,7 @@ BOOL LLInvFVBridge::isClipboardPasteable() const { const LLUUID &item_id = objects.at(i); - // Folders are pasteable if all items in there are copyable + // Folders are pastable if all items in there are copyable const LLInventoryCategory *cat = model->getCategory(item_id); if (cat) { @@ -520,7 +549,7 @@ BOOL LLInvFVBridge::isClipboardPasteable() const continue; } - // Each item must be copyable to be pasteable + // Each item must be copyable to be pastable LLItemBridge item_br(panel, mRoot, item_id); if (!item_br.isItemCopyable()) return FALSE; @@ -678,7 +707,7 @@ void hide_context_entries(LLMenuGL& menu, // between two separators). if (found) { - const bool is_entry_separator = (dynamic_cast(menu_item) != NULL); + const bool is_entry_separator = !branchp && (dynamic_cast(menu_item) != NULL); found = !(is_entry_separator && is_previous_entry_separator); is_previous_entry_separator = is_entry_separator; } @@ -751,7 +780,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, if (!isInboxFolder()) { items.push_back(std::string("Rename")); - if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0) + if (!isItemRenameable() || ((flags & FIRST_SELECTED_ITEM) == 0)) { disabled_items.push_back(std::string("Rename")); } @@ -789,14 +818,19 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Cut")); } - if (canListOnMarketplace()) + if (canListOnMarketplace() && !isMarketplaceListingsFolder() && !isInboxFolder()) { items.push_back(std::string("Marketplace Separator")); - items.push_back(std::string("Merchant Copy")); - if (!canListOnMarketplaceNow()) + if (gMenuHolder->getChild("MarketplaceListings")->getVisible()) { - disabled_items.push_back(std::string("Merchant Copy")); + items.push_back(std::string("Marketplace Copy")); + items.push_back(std::string("Marketplace Move")); + if (!canListOnMarketplaceNow()) + { + disabled_items.push_back(std::string("Marketplace Copy")); + disabled_items.push_back(std::string("Marketplace Move")); + } } } } @@ -804,7 +838,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, bool paste_as_copy = false; // If Paste As Copy is on the menu, Paste As Link will always show up disabled, so don't bother. // Don't allow items to be pasted directly into the COF or the inbox/outbox - if (!isCOFFolder() && !isInboxFolder() && !isOutboxFolder()) + if (!isCOFFolder() && !isInboxFolder()) { items.push_back(std::string("Paste")); // Paste as copy if we have links. @@ -849,10 +883,6 @@ void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if(isItemInTrash()) { addTrashContextMenuOptions(items, disabled_items); - } - else if(isOutboxFolder()) - { - addOutboxContextMenuOptions(flags, items, disabled_items); } else { @@ -968,30 +998,126 @@ void LLInvFVBridge::addOpenRightClickMenuOption(menuentry_vec_t &items) items.push_back(std::string("Open")); } -void LLInvFVBridge::addOutboxContextMenuOptions(U32 flags, +void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags, menuentry_vec_t &items, menuentry_vec_t &disabled_items) { - items.push_back(std::string("Rename")); - items.push_back(std::string("Delete")); - - if ((flags & FIRST_SELECTED_ITEM) == 0) + S32 depth = depth_nesting_in_marketplace(mUUID); + if (depth == 1) { - disabled_items.push_back(std::string("Rename")); - } - -#if ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU - if (isOutboxFolderDirectParent()) - { - items.push_back(std::string("Marketplace Separator")); - items.push_back(std::string("Marketplace Send")); - - if (!canListOnMarketplaceNow() || (flags & FIRST_SELECTED_ITEM) == 0) + // Options available at the Listing Folder level + items.push_back(std::string("Marketplace Create Listing")); + items.push_back(std::string("Marketplace Associate Listing")); + items.push_back(std::string("Marketplace Check Listing")); + items.push_back(std::string("Marketplace List")); + items.push_back(std::string("Marketplace Unlist")); + if (LLMarketplaceData::instance().isUpdating(mUUID,depth) || ((flags & FIRST_SELECTED_ITEM) == 0)) { - disabled_items.push_back(std::string("Marketplace Send")); + // During SLM update, disable all marketplace related options + // Also disable all if multiple selected items + disabled_items.push_back(std::string("Marketplace Create Listing")); + disabled_items.push_back(std::string("Marketplace Associate Listing")); + disabled_items.push_back(std::string("Marketplace Check Listing")); + disabled_items.push_back(std::string("Marketplace List")); + disabled_items.push_back(std::string("Marketplace Unlist")); + } + else + { + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + items.push_back(std::string("Marketplace Get Listing")); + } + if (LLMarketplaceData::instance().isListed(mUUID)) + { + disabled_items.push_back(std::string("Marketplace Create Listing")); + disabled_items.push_back(std::string("Marketplace Associate Listing")); + items.push_back(std::string("Marketplace Copy ID")); + if (LLMarketplaceData::instance().getVersionFolder(mUUID).isNull()) + { + disabled_items.push_back(std::string("Marketplace List")); + disabled_items.push_back(std::string("Marketplace Unlist")); + } + else + { + if (LLMarketplaceData::instance().getActivationState(mUUID)) + { + disabled_items.push_back(std::string("Marketplace List")); + } + else + { + disabled_items.push_back(std::string("Marketplace Unlist")); + } + } + } + else + { + disabled_items.push_back(std::string("Marketplace List")); + disabled_items.push_back(std::string("Marketplace Unlist")); + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + disabled_items.push_back(std::string("Marketplace Get Listing")); + } + } } } -#endif // ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU + if (depth == 2) + { + // Options available at the Version Folder levels and only for folders + LLInventoryCategory* cat = gInventory.getCategory(mUUID); + if (cat && LLMarketplaceData::instance().isListed(cat->getParentUUID())) + { + items.push_back(std::string("Marketplace Activate")); + items.push_back(std::string("Marketplace Deactivate")); + if (LLMarketplaceData::instance().isUpdating(mUUID,depth) || ((flags & FIRST_SELECTED_ITEM) == 0)) + { + // During SLM update, disable all marketplace related options + // Also disable all if multiple selected items + disabled_items.push_back(std::string("Marketplace Activate")); + disabled_items.push_back(std::string("Marketplace Deactivate")); + } + else + { + if (LLMarketplaceData::instance().isVersionFolder(mUUID)) + { + disabled_items.push_back(std::string("Marketplace Activate")); + if (LLMarketplaceData::instance().getActivationState(mUUID)) + { + disabled_items.push_back(std::string("Marketplace Deactivate")); + } + } + else + { + disabled_items.push_back(std::string("Marketplace Deactivate")); + } + } + } + } + + items.push_back(std::string("Marketplace Edit Listing")); + LLUUID listing_folder_id = nested_parent_id(mUUID,depth); + LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(listing_folder_id); + + if (depth >= 2) + { + // Prevent creation of new folders if the max count has been reached on this version folder (active or not) + LLUUID local_version_folder_id = nested_parent_id(mUUID,depth-1); + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(local_version_folder_id, categories, items, FALSE); + if (categories.size() >= gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + { + disabled_items.push_back(std::string("New Folder")); + } + } + + // Options available at all levels on items and categories + if (!LLMarketplaceData::instance().isListed(listing_folder_id) || version_folder_id.isNull()) + { + disabled_items.push_back(std::string("Marketplace Edit Listing")); + } + + // Separator + items.push_back(std::string("Marketplace Listings Separator")); } // *TODO: remove this @@ -1037,13 +1163,13 @@ LLInventoryObject* LLInvFVBridge::getInventoryObject() const LLInventoryModel* LLInvFVBridge::getInventoryModel() const { LLInventoryPanel* panel = mInventoryPanel.get(); - return panel ? panel->getModel() : NULL; + return panel ? panel->getModel() : nullptr; } LLInventoryFilter* LLInvFVBridge::getInventoryFilter() const { LLInventoryPanel* panel = mInventoryPanel.get(); - return panel ? panel->getFilter() : NULL; + return panel ? &panel->getFilter() : nullptr; } BOOL LLInvFVBridge::isItemInTrash() const @@ -1109,39 +1235,16 @@ BOOL LLInvFVBridge::isInboxFolder() const return gInventory.isObjectDescendentOf(mUUID, inbox_id); } -BOOL LLInvFVBridge::isOutboxFolder() const +BOOL LLInvFVBridge::isMarketplaceListingsFolder() const { - const LLUUID outbox_id = getOutboxFolder(); + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - if (outbox_id.isNull()) + if (folder_id.isNull()) { return FALSE; } - return gInventory.isObjectDescendentOf(mUUID, outbox_id); -} - -BOOL LLInvFVBridge::isOutboxFolderDirectParent() const -{ - BOOL outbox_is_parent = FALSE; - - const LLInventoryCategory *cat = gInventory.getCategory(mUUID); - - if (cat) - { - const LLUUID outbox_id = getOutboxFolder(); - - outbox_is_parent = (outbox_id.notNull() && (outbox_id == cat->getParentUUID())); - } - - return outbox_is_parent; -} - -const LLUUID LLInvFVBridge::getOutboxFolder() const -{ - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - - return outbox_id; + return gInventory.isObjectDescendentOf(mUUID, folder_id); } BOOL LLInvFVBridge::isItemPermissive() const @@ -1271,11 +1374,18 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, case LLAssetType::AT_CATEGORY: if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) { - // Create a link folder handler instead. + // Create a link folder handler instead new_listener = new LLLinkFolderBridge(inventory, root, uuid); - break; } - new_listener = new LLFolderBridge(inventory, root, uuid); + else if (actual_asset_type == LLAssetType::AT_MARKETPLACE_FOLDER) + { + // Create a marketplace folder handler + new_listener = new LLMarketplaceFolderBridge(inventory, root, uuid); + } + else + { + new_listener = new LLFolderBridge(inventory, root, uuid); + } break; case LLAssetType::AT_LINK: case LLAssetType::AT_LINK_FOLDER: @@ -1295,6 +1405,9 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, //LL_WARNS() << LLAssetType::lookup(asset_type) << " asset type is unhandled for uuid " << uuid << LL_ENDL; break; + case LLAssetType::AT_SETTINGS: + break; + default: LL_INFOS() << "Unhandled asset type (llassetstorage.h): " << (S32)asset_type << " (" << LLAssetType::lookup(asset_type) << ")" << LL_ENDL; @@ -1318,6 +1431,22 @@ void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) } } +void LLInvFVBridge::removeObject(LLInventoryModel *model, const LLUUID &uuid) +{ + // Keep track of the parent + LLInventoryItem* itemp = model->getItem(uuid); + LLUUID parent_id = (itemp ? itemp->getParentUUID() : LLUUID::null); + // Remove the object + model->removeObject(uuid); + // Get the parent updated + if (parent_id.notNull()) + { + LLViewerInventoryCategory* parent_cat = model->getCategory(parent_id); + model->updateCategory(parent_cat); + model->notifyObservers(); + } +} + bool LLInvFVBridge::canShare() const { bool can_share = false; @@ -1348,11 +1477,9 @@ bool LLInvFVBridge::canShare() const bool LLInvFVBridge::canListOnMarketplace() const { -#if ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU - LLInventoryModel * model = getInventoryModel(); - const LLViewerInventoryCategory * cat = model->getCategory(mUUID); + LLViewerInventoryCategory * cat = model->getCategory(mUUID); if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) { return false; @@ -1363,16 +1490,6 @@ bool LLInvFVBridge::canListOnMarketplace() const return false; } - if (getOutboxFolder().isNull()) - { - return false; - } - - if (isInboxFolder() || isOutboxFolder()) - { - return false; - } - LLViewerInventoryItem * item = model->getItem(mUUID); if (item) { @@ -1388,24 +1505,12 @@ bool LLInvFVBridge::canListOnMarketplace() const } return true; - -#else - return false; -#endif } bool LLInvFVBridge::canListOnMarketplaceNow() const { -#if ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU - bool can_list = true; - // Do not allow listing while import is in progress - if (LLMarketplaceInventoryImporter::instanceExists()) - { - can_list = !LLMarketplaceInventoryImporter::instance().isImportInProgress(); - } - const LLInventoryObject* obj = getInventoryObject(); can_list &= (obj != NULL); @@ -1422,30 +1527,34 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const can_list = !object_folderp->isLoading(); } } - + if (can_list) { - // Get outbox id - const LLUUID & outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - LLFolderViewItem * outbox_itemp = mRoot->getItemByID(outbox_id); - - if (outbox_itemp) + std::string error_msg; + LLInventoryModel* model = getInventoryModel(); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplacelistings_id.notNull()) { - MASK mask = 0x0; - BOOL drop = FALSE; - EDragAndDropType cargo_type = LLViewerAssetType::lookupDragAndDropType(obj->getActualType()); - void * cargo_data = (void *) obj; - - can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data); + LLViewerInventoryCategory * master_folder = model->getCategory(marketplacelistings_id); + LLInventoryCategory *cat = model->getCategory(mUUID); + if (cat) + { + can_list = can_move_folder_to_marketplace(master_folder, master_folder, cat, error_msg); + } + else + { + LLInventoryItem *item = model->getItem(mUUID); + can_list = (item ? can_move_item_to_marketplace(master_folder, master_folder, item, error_msg) : false); + } + } + else + { + can_list = false; } } } - - return can_list; -#else - return false; -#endif + return can_list; } @@ -1520,7 +1629,6 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - LLFolderView::removeCutItems(); return; } else if ("copy" == action) @@ -1563,15 +1671,18 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) folder_view_itemp->getListener()->pasteLinkFromClipboard(); return; } - else if (isMarketplaceCopyAction(action)) + else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action)) { - LL_INFOS() << "Copy item to marketplace action!" << LL_ENDL; - LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + // Note: For a single item, if it's not a copy, then it's a move + move_item_to_marketplacelistings(itemp, marketplacelistings_id, ("copy_to_marketplace_listings" == action)); + } + else if ("marketplace_edit_listing" == action) + { + std::string url = LLMarketplaceData::instance().getListingURL(mUUID); + LLUrlAction::openURL(url); } } @@ -1657,7 +1768,6 @@ LLUIImagePtr LLItemBridge::getIcon() const } return LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_OBJECT); - } LLUIImagePtr LLItemBridge::getIconOverlay() const @@ -1749,9 +1859,7 @@ std::string LLItemBridge::getLabelSuffix() const suffix += NO_XFER; } - BOOL temp = item->getPermissions().getGroup() == gAgent.getID(); - - if (temp) + if (item->getPermissions().getGroup() == gAgent.getID()) { suffix += TEMPO; } @@ -1981,6 +2089,27 @@ void LLFolderBridge::buildDisplayName() const mDisplayName.assign(getName()); } +std::string LLFolderBridge::getLabelSuffix() const +{ + /* Singu TODO? + static LLCachedControl folder_loading_message_delay(gSavedSettings, "FolderLoadingMessageWaitTime", 0.5f); + + if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= folder_loading_message_delay()) + { + return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str()); + } + */ + + return LLInvFVBridge::getLabelSuffix(); +} + +LLFontGL::StyleFlags LLFolderBridge::getLabelStyle() const +{ + return LLFontGL::NORMAL; +} + + + // Iterate through a folder's children to determine if // all the children are removable. class LLIsItemRemovable : public LLFolderViewFunctor @@ -2018,6 +2147,11 @@ BOOL LLFolderBridge::isItemRemovable() const } } + if (isMarketplaceListingsFolder() && LLMarketplaceData::instance().getActivationState(mUUID)) + { + return FALSE; + } + return TRUE; } @@ -2167,84 +2301,10 @@ BOOL LLFolderBridge::isClipboardPasteableAsLink() const } -static BOOL can_move_to_outbox(LLInventoryItem* inv_item) -{ - // Collapse links directly to items/folders - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; - LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); - if (linked_item != NULL) - { - inv_item = linked_item; - } - - bool allow_transfer = inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); - if (!allow_transfer) - { - return false; - } - -#if BLOCK_WORN_ITEMS_IN_OUTBOX - bool worn = get_is_item_worn(inv_item->getUUID()); - if (worn) - { - return false; - } -#endif - - bool calling_card = (LLAssetType::AT_CALLINGCARD == inv_item->getType()); - if (calling_card) - { - return false; - } - - return true; -} - - -int get_folder_levels(LLInventoryCategory* inv_cat) -{ - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items); - - int max_child_levels = 0; - - for (size_t i=0; i < cats->size(); ++i) - { - LLInventoryCategory* category = cats->at(i); - max_child_levels = llmax(max_child_levels, get_folder_levels(category)); - } - - return 1 + max_child_levels; -} - -int get_folder_path_length(const LLUUID& ancestor_id, const LLUUID& descendant_id) -{ - int depth = 0; - - if (ancestor_id == descendant_id) return depth; - - const LLInventoryCategory* category = gInventory.getCategory(descendant_id); - - while(category) - { - LLUUID parent_id = category->getParentUUID(); - - if (parent_id.isNull()) break; - - depth++; - - if (parent_id == ancestor_id) return depth; - - category = gInventory.getCategory(parent_id); - } - - LL_WARNS() << "get_folder_path_length() couldn't trace a path from the descendant to the ancestor" << LL_ENDL; - return -1; -} BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, - BOOL drop) + BOOL drop, + BOOL user_confirm) { LLInventoryModel* model = getInventoryModel(); @@ -2256,11 +2316,12 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, const LLUUID &cat_id = inv_cat->getUUID(); const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID from_folder_uuid = inv_cat->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); - const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); - const BOOL move_is_from_outbox = model->isObjectDescendentOf(cat_id, outbox_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(cat_id, marketplacelistings_id); // check to make sure source is agent inventory, and is represented there. LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); @@ -2272,9 +2333,11 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, { const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); + //const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); - const BOOL move_is_into_outfit = getCategory() && (getCategory()->getPreferredType() == LLFolderType::FT_OUTFIT); + //const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); + const BOOL move_is_into_outfit = /*move_is_into_my_outfits ||*/ (getCategory() && getCategory()->getPreferredType() == LLFolderType::FT_OUTFIT); const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); //-------------------------------------------------------------------------------- @@ -2282,7 +2345,20 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, // BOOL is_movable = TRUE; + std::string tooltip_msg; + if (is_movable && (marketplacelistings_id == cat_id)) + { + is_movable = FALSE; + tooltip_msg = LLTrans::getString("TooltipOutboxCannotMoveRoot"); + } + if (is_movable && move_is_from_marketplacelistings && LLMarketplaceData::instance().getActivationState(cat_id)) + { + // If the incoming folder is listed and active (and is therefore either the listing or the version folder), + // then moving is *not* allowed + is_movable = FALSE; + tooltip_msg = LLTrans::getString("TooltipOutboxDragActive"); + } // Can't move a folder into itself if (is_movable && (mUUID == cat_id)) { @@ -2313,6 +2389,12 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, is_movable = FALSE; // tooltip? } + if (is_movable && (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)) + { + // One cannot move a folder into a stock folder + is_movable = FALSE; + // tooltip? + } LLInventoryModel::cat_array_t descendent_categories; LLInventoryModel::item_array_t descendent_items; @@ -2386,90 +2468,13 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, is_movable = RlvFolderLocks::instance().canMoveFolder(cat_id, mUUID); } // [/RLVa:KB] - if (is_movable && move_is_into_outbox) + + if (is_movable && move_is_into_marketplacelistings) { - const int nested_folder_levels = get_folder_path_length(outbox_id, mUUID) + get_folder_levels(inv_cat); - - if (nested_folder_levels > (S32)gSavedSettings.getU32("InventoryOutboxMaxFolderDepth")) - { - is_movable = FALSE; - } - else - { - int dragged_folder_count = descendent_categories.size(); - int existing_item_count = 0; - int existing_folder_count = 0; - - const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(outbox_id, mUUID); - - if (master_folder != NULL) - { - if (model->isObjectDescendentOf(cat_id, master_folder->getUUID())) - { - // Don't use count because we're already inside the same category anyway - dragged_folder_count = 0; - } - else - { - existing_folder_count = 1; // Include the master folder in the count! - - // If we're in the drop operation as opposed to the drag without drop, we are doing a - // single category at a time so don't block based on the total amount of cargo data items - if (drop) - { - dragged_folder_count += 1; - } - else - { - // NOTE: The cargo id's count is a total of categories AND items but we err on the side of - // prevention rather than letting too many folders into the hierarchy of the outbox, - // when we're dragging the item to a new parent - dragged_folder_count += LLToolDragAndDrop::instance().getCargoIDsCount(); - } - } - - // Tally the total number of categories and items inside the master folder - - LLInventoryModel::cat_array_t existing_categories; - LLInventoryModel::item_array_t existing_items; - - model->collectDescendents(master_folder->getUUID(), existing_categories, existing_items, FALSE); - - existing_folder_count += existing_categories.size(); - existing_item_count += existing_items.size(); - } - else - { - // Assume a single category is being dragged to the outbox since we evaluate one at a time - // when not putting them under a parent item. - dragged_folder_count += 1; - } - - const int nested_folder_count = existing_folder_count + dragged_folder_count; - const int nested_item_count = existing_item_count + descendent_items.size(); - - if (nested_folder_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) - { - is_movable = FALSE; - } - else if (nested_item_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxItemCount")) - { - is_movable = FALSE; - } - - if (is_movable == TRUE) - { - for (size_t i=0; i < descendent_items.size(); ++i) - { - LLInventoryItem* item = descendent_items[i]; - if (!can_move_to_outbox(item)) - { - is_movable = FALSE; - break; - } - } - } - } + const LLViewerInventoryCategory* master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID); + LLViewerInventoryCategory * dest_folder = getCategory(); + S32 bundle_size = (drop ? 1 : LLToolDragAndDrop::instance().getCargoCount()); + is_movable = can_move_folder_to_marketplace(master_folder, dest_folder, inv_cat, tooltip_msg, bundle_size); } // @@ -2479,6 +2484,48 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, if (accept && drop) { + // Dropping in or out of marketplace needs (sometimes) confirmation + if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) + { + if (move_is_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(cat_id) || + LLMarketplaceData::instance().isListedAndActive(cat_id))) + { + if (LLMarketplaceData::instance().isListed(cat_id) || LLMarketplaceData::instance().isVersionFolder(cat_id)) + { + // Move the active version folder or listing folder itself outside marketplace listings will unlist the listing so ask that question specifically + LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + } + else + { + // Any other case will simply modify but not unlist an active listed listing + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + } + return true; + } + if (move_is_from_marketplacelistings && LLMarketplaceData::instance().isVersionFolder(cat_id)) + { + // Moving the version folder from its location will deactivate it. Ask confirmation. + LLNotificationsUtil::add("ConfirmMerchantClearVersion", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + if (move_is_into_marketplacelistings && LLMarketplaceData::instance().isInActiveFolder(mUUID)) + { + // Moving something in an active listed listing will modify it. Ask confirmation. + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + if (move_is_from_marketplacelistings && LLMarketplaceData::instance().isListed(cat_id)) + { + // Moving a whole listing folder will result in archival of SLM data. Ask confirmation. + LLNotificationsUtil::add("ConfirmListingCutOrDelete", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + if (move_is_into_marketplacelistings && !move_is_from_marketplacelistings) + { + LLNotificationsUtil::add("ConfirmMerchantMoveInventory", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropCategoryIntoFolder, this, _1, _2, inv_cat)); + return true; + } + } // Look for any gestures and deactivate them if (move_is_into_trash) { @@ -2519,9 +2566,9 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } } } - else if (move_is_into_outbox && !move_is_from_outbox) + else if (move_is_into_marketplacelistings) { - copy_folder_to_outbox(inv_cat, mUUID, cat_id, LLToolDragAndDrop::getOperationId()); + move_folder_to_marketplacelistings(inv_cat, mUUID); } else { @@ -2538,11 +2585,38 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, mUUID, move_is_into_trash); } + if (move_is_from_marketplacelistings) + { + // If we are moving a folder at the listing folder level (i.e. its parent is the marketplace listings folder) + if (from_folder_uuid == marketplacelistings_id) + { + // Clear the folder from the marketplace in case it is a listing folder + if (LLMarketplaceData::instance().isListed(cat_id)) + { + LLMarketplaceData::instance().clearListing(cat_id); + } + } + else + { + // If we move from within an active (listed) listing, check that it's still valid, if not, unlist + LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); + if (version_folder_id.notNull()) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); + if (!validate_marketplacelistings(cat, NULL)) + { + LLMarketplaceData::instance().activateListing(version_folder_id,false); + } + } + // In all cases, update the listing we moved from so suffix are updated + update_marketplace_category(from_folder_uuid); + } + } } } else if (LLToolDragAndDrop::SOURCE_WORLD == source) { - if (move_is_into_outbox) + if (move_is_into_marketplacelistings) { accept = FALSE; } @@ -2553,7 +2627,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) { - if (move_is_into_outbox) + if (move_is_into_marketplacelistings) { accept = FALSE; } @@ -2923,7 +2997,6 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - LLFolderView::removeCutItems(); return; } else if ("copy" == action) @@ -2955,6 +3028,125 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) restoreItem(); return; } + else if ("marketplace_list" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 1) + { + LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(mUUID); + LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); + mMessage = ""; + if (!validate_marketplacelistings(cat, boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3))) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + else + { + LLMarketplaceData::instance().activateListing(mUUID,true); + } + } + return; + } + else if ("marketplace_activate" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 2) + { + LLInventoryCategory* category = gInventory.getCategory(mUUID); + mMessage = ""; + if (!validate_marketplacelistings(category,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),false,2)) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantFolderActivationFailed", subs); + } + else + { + LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID); + } + } + return; + } + else if ("marketplace_unlist" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 1) + { + LLMarketplaceData::instance().activateListing(mUUID, false,1); + } + return; + } + else if ("marketplace_deactivate" == action) + { + if (depth_nesting_in_marketplace(mUUID) == 2) + { + LLInventoryCategory* category = gInventory.getCategory(mUUID); + LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), LLUUID::null, 1); + } + return; + } + else if ("marketplace_create_listing" == action) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(mUUID); + mMessage = ""; + bool validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),false); + if (!validates) + { + mMessage = ""; + validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),true); + if (validates) + { + LLNotificationsUtil::add("MerchantForceValidateListing"); + } + } + + if (!validates) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + else + { + LLMarketplaceData::instance().createListing(mUUID); + } + return; + } + else if ("marketplace_disassociate_listing" == action) + { + LLMarketplaceData::instance().clearListing(mUUID); + return; + } + else if ("marketplace_get_listing" == action) + { + // This is used only to exercise the SLM API but won't be shown to end users + LLMarketplaceData::instance().getListing(mUUID); + return; + } + else if ("marketplace_associate_listing" == action) + { + LLFloaterAssociateListing::showInstance(mUUID); + return; + } + else if ("marketplace_check_listing" == action) + { + LLSD data(mUUID); + new LLFloaterMarketplaceValidation(data); // new.. but floaters clean themselves up. + return; + } + else if ("marketplace_edit_listing" == action) + { + std::string url = LLMarketplaceData::instance().getListingURL(mUUID); + if (!url.empty()) + { + LLUrlAction::openURL(url); + } + return; + } + else if ("marketplace_copy_id") + { + auto id = LLMarketplaceData::instance().getListingID(mUUID); + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(std::to_string(id))); + } // Move displaced inventory to lost and found else if ("move_to_lost_and_found" == action) { @@ -2969,28 +3161,31 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) removeSystemFolder(); } #endif - else if (isMarketplaceCopyAction(action)) + else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action)) { - LL_INFOS() << "Copy folder to marketplace action!" << LL_ENDL; - LLInventoryCategory * cat = gInventory.getCategory(mUUID); if (!cat) return; - - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - copy_folder_to_outbox(cat, outbox_id, cat->getUUID(), LLToolDragAndDrop::getOperationId()); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + move_folder_to_marketplacelistings(cat, marketplacelistings_id, ("move_to_marketplace_listings" != action), (("copy_or_move_to_marketplace_listings" == action))); } -#if ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU - else if (isMarketplaceSendAction(action)) +} + +void LLFolderBridge::gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level) +{ + if (log_level >= LLError::LEVEL_ERROR) { - LL_INFOS() << "Send to marketplace action!" << LL_ENDL; - - /*LLInventoryCategory * cat = gInventory.getCategory(mUUID); - if (!cat) return; - - send_to_marketplace(cat);*/ - LLMarketplaceInventoryImporter::instance().triggerImport(); + if (!mMessage.empty()) + { + // Currently, we do not gather all messages as it creates very long alerts + // Users can get to the whole list of errors on a listing using the "Check for Errors" audit button or "Check listing" right click menu + //mMessage += "\n"; + return; + } + // Take the leading spaces out... + std::string::size_type start = message.find_first_not_of(" "); + // Append the message + mMessage += message.substr(start, message.length() - start); } -#endif // ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU } void LLFolderBridge::openItem() @@ -3060,25 +3255,30 @@ LLFolderType::EType LLFolderBridge::getPreferredType() const // Icons for folders are based on the preferred type LLUIImagePtr LLFolderBridge::getIcon() const { - LLFolderType::EType preferred_type = LLFolderType::FT_NONE; - LLViewerInventoryCategory* cat = getCategory(); - if(cat) - { - preferred_type = cat->getPreferredType(); - } - return getIcon(preferred_type); -} - -// static -LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) -{ - return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, FALSE)); + return getFolderIcon(FALSE); } LLUIImagePtr LLFolderBridge::getIconOpen() const { - return LLUI::getUIImage(LLViewerFolderType::lookupIconName(getPreferredType(), TRUE)); + return getFolderIcon(TRUE); +} +LLUIImagePtr LLFolderBridge::getFolderIcon(BOOL is_open) const +{ + LLFolderType::EType preferred_type = getPreferredType(); + S32 depth = depth_nesting_in_marketplace(mUUID); + if ((preferred_type == LLFolderType::FT_NONE) && (depth == 2)) + { + // We override the type when in the marketplace listings folder and only for version folder + preferred_type = LLFolderType::FT_MARKETPLACE_VERSION; + } + return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, is_open)); +} + +// static : use by LLLinkFolderBridge to get the closed type icons +LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) +{ + return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, FALSE)); } LLUIImagePtr LLFolderBridge::getIconOverlay() const @@ -3160,54 +3360,114 @@ bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& re void LLFolderBridge::pasteFromClipboard(bool only_copies) { LLInventoryModel* model = getInventoryModel(); - if(model && isClipboardPasteable()) + if (model && isClipboardPasteable()) + { + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const BOOL paste_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + + BOOL cut_from_marketplacelistings = FALSE; + const auto& clipboard(LLInventoryClipboard::instance()); + if (clipboard.isCutMode()) + { + //Items are not removed from folder on "cut", so we need update listing folder on "paste" operation + std::vector objects; + clipboard.retrieve(objects); + for (std::vector::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) + { + const LLUUID& item_id = (*iter); + if(gInventory.isObjectDescendentOf(item_id, marketplacelistings_id) && (LLMarketplaceData::instance().isInActiveFolder(item_id) || + LLMarketplaceData::instance().isListedAndActive(item_id))) + { + cut_from_marketplacelistings = TRUE; + break; + } + } + } + if (cut_from_marketplacelistings || (paste_into_marketplacelistings && !LLMarketplaceData::instance().isListed(mUUID) && LLMarketplaceData::instance().isInActiveFolder(mUUID))) + { + // Prompt the user if pasting in a marketplace active version listing (note that pasting right under the listing folder root doesn't need a prompt) + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_pasteFromClipboard, this, _1, _2, only_copies)); + } + else + { + // Otherwise just do the paste + perform_pasteFromClipboard(only_copies); + } + } +} + +// Callback for pasteFromClipboard if DAMA required... +void LLFolderBridge::callback_pasteFromClipboard(const LLSD& notification, const LLSD& response, bool only_copies) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + perform_pasteFromClipboard(only_copies); + } +} + +void LLFolderBridge::perform_pasteFromClipboard(bool only_copies) +{ + LLInventoryModel* model = getInventoryModel(); + if (model && isClipboardPasteable()) { const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); - const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); std::vector objects; LLInventoryClipboard::instance().retrieve(objects); - if (move_is_into_outbox) + LLViewerInventoryCategory* dest_folder = getCategory(); + if (move_is_into_marketplacelistings) { - LLFolderViewItem * outbox_itemp = mRoot->getItemByID(mUUID); - - if (outbox_itemp) + std::string error_msg; + const LLViewerInventoryCategory* master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID); + int index = 0; + for (std::vector::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) { - //LLToolDragAndDrop::instance().setCargoCount(objects.size()); + const LLUUID& item_id = (*iter); + LLInventoryItem *item = model->getItem(item_id); + LLInventoryCategory *cat = model->getCategory(item_id); - BOOL can_list = TRUE; - - for (std::vector::const_iterator iter = objects.begin(); - (iter != objects.end()) && (can_list == TRUE); - ++iter) + if (item && !can_move_item_to_marketplace(master_folder, dest_folder, item, error_msg, objects.size() - index, true)) { - const LLUUID& item_id = (*iter); - LLInventoryItem *item = model->getItem(item_id); - - if (item) - { - MASK mask = 0x0; - BOOL drop = FALSE; - EDragAndDropType cargo_type = LLViewerAssetType::lookupDragAndDropType(item->getActualType()); - void * cargo_data = (void *) item; - //std::string tooltip_msg; - - can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data/*, tooltip_msg*/); - } + break; } - - //LLToolDragAndDrop::instance().resetCargoCount(); - - if (can_list == FALSE) + if (cat && !can_move_folder_to_marketplace(master_folder, dest_folder, cat, error_msg, objects.size() - index, true, true)) { - // Notify user of failure somehow -- play error sound? modal dialog? + break; + } + ++index; + } + if (!error_msg.empty()) + { + LLSD subs; + subs["[ERROR_CODE]"] = error_msg; + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return; + } + } + else + { + // Check that all items can be moved into that folder : for the moment, only stock folder mismatch is checked + for (std::vector::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) + { + const LLUUID& item_id = (*iter); + LLInventoryItem *item = model->getItem(item_id); + LLInventoryCategory *cat = model->getCategory(item_id); + + if ((item && !dest_folder->acceptItem(item)) || (cat && (dest_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK))) + { + std::string error_msg = LLTrans::getString("TooltipOutboxMixedStock"); + LLSD subs; + subs["[ERROR_CODE]"] = error_msg; + LLNotificationsUtil::add("StockPasteFailed", subs); return; } } @@ -3238,11 +3498,24 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) // move_inventory_item() is not enough, as we have to update inventory locally too if (LLAssetType::AT_CATEGORY == obj->getType()) { - LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id); + LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *)model->getCategory(item_id); llassert(vicat); if (vicat) { - changeCategoryParent(model, vicat, parent_id, FALSE); + // Clear the cut folder from the marketplace if it is a listing folder + if (LLMarketplaceData::instance().isListed(item_id)) + { + LLMarketplaceData::instance().clearListing(item_id); + } + if (move_is_into_marketplacelistings) + { + move_folder_to_marketplacelistings(vicat, parent_id); + } + else + { + //changeCategoryParent() implicitly calls dirtyFilter + changeCategoryParent(model, vicat, parent_id, FALSE); + } } } else @@ -3251,7 +3524,19 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) llassert(viitem); if (viitem) { - changeItemParent(model, viitem, parent_id, FALSE); + if (move_is_into_marketplacelistings) + { + if (!move_item_to_marketplacelistings(viitem, parent_id)) + { + // Stop pasting into the marketplace as soon as we get an error + break; + } + } + else + { + //changeItemParent() implicitly calls dirtyFilter + changeItemParent(model, viitem, parent_id, FALSE); + } } } } @@ -3265,34 +3550,57 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) // Do a "copy" to "paste" a regular copy clipboard if (LLAssetType::AT_CATEGORY == obj->getType()) { - LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id); + LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *)model->getCategory(item_id); llassert(vicat); if (vicat) { - copy_inventory_category(model, vicat, parent_id); + if (move_is_into_marketplacelistings) + { + move_folder_to_marketplacelistings(vicat, parent_id, true); + } + else + { + copy_inventory_category(model, vicat, parent_id); + } } } -// [SL:KB] - Patch: Inventory-Links | Checked: 2010-04-12 (Catznip-2.2.0a) | Added: Catznip-2.0.0a - else if (!only_copies && LLAssetType::lookupIsLinkType(item->getActualType())) + // [SL:KB] - Patch: Inventory-Links | Checked: 2010-04-12 (Catznip-2.2.0a) | Added: Catznip-2.0.0a + else if (!move_is_into_marketplacelistings && !only_copies && LLAssetType::lookupIsLinkType(item->getActualType())) { link_inventory_object( parent_id, item, NULL); } -// [/SL:KB] + // [/SL:KB] + else + { + LLViewerInventoryItem* viitem = dynamic_cast(item); + llassert(viitem); + if (viitem) + { + if (move_is_into_marketplacelistings) + { + if (!move_item_to_marketplacelistings(viitem, parent_id, true)) + { + // Stop pasting into the marketplace as soon as we get an error + break; + } + } else { copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), - item->getUUID(), - parent_id, - std::string(), - LLPointer(NULL)); + item->getUUID(), + parent_id, + std::string(), + LLPointer(NULL)); } } } + } + } } // Change mode to copy for next paste LLInventoryClipboard::instance().setCutMode(false); @@ -3305,15 +3613,15 @@ void LLFolderBridge::pasteLinkFromClipboard() if(model) { const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); - const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); - if (move_is_into_outbox) + if (move_is_into_marketplacelistings) { // Notify user of failure somehow -- play error sound? modal dialog? return; @@ -3377,9 +3685,9 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items LLInventoryModel* model = getInventoryModel(); llassert(model != NULL); - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); - + const LLUUID& trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + const LLUUID& lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + const LLUUID& marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); if (lost_and_found_id == mUUID) { @@ -3393,6 +3701,35 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items disabled_items.push_back(std::string("New Clothes")); disabled_items.push_back(std::string("New Body Parts")); } + if (isMarketplaceListingsFolder()) + { + addMarketplaceContextMenuOptions(flags, items, disabled_items); + if (LLMarketplaceData::instance().isUpdating(mUUID)) + { + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("Rename")); + disabled_items.push_back(std::string("Cut")); + disabled_items.push_back(std::string("Copy")); + disabled_items.push_back(std::string("Paste")); + disabled_items.push_back(std::string("Delete")); + } + } + if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("New Script")); + disabled_items.push_back(std::string("New Note")); + disabled_items.push_back(std::string("New Gesture")); + disabled_items.push_back(std::string("New Clothes")); + disabled_items.push_back(std::string("New Body Parts")); + } + if (marketplace_listings_id == mUUID) + { + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("Rename")); + disabled_items.push_back(std::string("Cut")); + disabled_items.push_back(std::string("Delete")); + } if(trash_id == mUUID) { // This is the trash. @@ -3404,10 +3741,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items items.clear(); // clear any items that used to exist addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) - { - addOutboxContextMenuOptions(flags, items, disabled_items); - } else if(isAgentInventory()) // do not allow creating in library { LLViewerInventoryCategory *cat = getCategory(); @@ -3416,16 +3749,20 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items const bool is_cof(isCOFFolder()); if (!is_cof && cat && (cat->getPreferredType() != LLFolderType::FT_OUTFIT)) { - LLInventoryPanel* panel = mInventoryPanel.get(); - if(panel && !panel->getFilterWorn()) - if (!isInboxFolder() && !isOutboxFolder()) // don't allow creation in inbox or outbox + if (!isInboxFolder()) // don't allow creation in inbox { - items.push_back(std::string("New Folder")); - items.push_back(std::string("New Script")); - items.push_back(std::string("New Note")); - items.push_back(std::string("New Gesture")); - items.push_back(std::string("New Clothes")); - items.push_back(std::string("New Body Parts")); + { + items.push_back(std::string("New Folder")); + } + if (!isMarketplaceListingsFolder()) + { + items.push_back(std::string("New Folder")); + items.push_back(std::string("New Script")); + items.push_back(std::string("New Note")); + items.push_back(std::string("New Gesture")); + items.push_back(std::string("New Clothes")); + items.push_back(std::string("New Body Parts")); + } } getClipboardEntries(false, items, disabled_items, flags); } @@ -3482,7 +3819,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items disabled_items.push_back(std::string("Delete System Folder")); } - if (!isOutboxFolder() && !isItemInTrash()) // + if (!isMarketplaceListingsFolder() && !isItemInTrash()) // { items.push_back(std::string("Share")); if (!canShare()) @@ -3490,9 +3827,10 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items disabled_items.push_back(std::string("Share")); } } + // Add menu items that are dependent on the contents of the folder. LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); - if (category) + if (category && (marketplace_listings_id != mUUID)) { uuid_vec_t folders; folders.push_back(category->getUUID()); @@ -3520,7 +3858,6 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& // Build folder specific options back up LLInventoryModel* model = getInventoryModel(); - if (isOutboxFolder()) return; if (!isAgentInventory()) return; build_context_menu_folder_options(model, mUUID, items, disabled_items); @@ -3549,6 +3886,15 @@ void build_context_menu_folder_options(LLInventoryModel* model, const LLUUID& mU if (trash_id == mUUID) return; if (model->isObjectDescendentOf(mUUID, trash_id)) return; + if (!get_is_item_removable(model, mUUID)) + { + disabled_items.push_back(std::string("Delete")); + } + + // copied from ::isMarketplaceListingsFolder + const LLUUID listings_folder = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (listings_folder.notNull() && gInventory.isObjectDescendentOf(mUUID, listings_folder)) return; + items.push_back(std::string("Open Folder In New Window")); LLFolderType::EType type = category->getPreferredType(); @@ -3843,6 +4189,124 @@ void LLFolderBridge::modifyOutfit(BOOL append/*, BOOL replace*/) LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, append ); } + +// +=================================================+ +// | LLMarketplaceFolderBridge | +// +=================================================+ + +// LLMarketplaceFolderBridge is a specialized LLFolderBridge for use in Marketplace Inventory panels +LLMarketplaceFolderBridge::LLMarketplaceFolderBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : +LLFolderBridge(inventory, root, uuid) +{ + m_depth = depth_nesting_in_marketplace(mUUID); + m_stockCountCache = COMPUTE_STOCK_NOT_EVALUATED; +} + +LLUIImagePtr LLMarketplaceFolderBridge::getIcon() const +{ + return getMarketplaceFolderIcon(FALSE); +} + +LLUIImagePtr LLMarketplaceFolderBridge::getIconOpen() const +{ + return getMarketplaceFolderIcon(TRUE); +} + +LLUIImagePtr LLMarketplaceFolderBridge::getMarketplaceFolderIcon(BOOL is_open) const +{ + LLFolderType::EType preferred_type = getPreferredType(); + if (!LLMarketplaceData::instance().isUpdating(getUUID())) + { + // Skip computation (expensive) if we're waiting for updates. Use the old value in that case. + m_depth = depth_nesting_in_marketplace(mUUID); + } + if ((preferred_type == LLFolderType::FT_NONE) && (m_depth == 2)) + { + // We override the type when in the marketplace listings folder and only for version folder + preferred_type = LLFolderType::FT_MARKETPLACE_VERSION; + } + return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, is_open)); +} + +std::string LLMarketplaceFolderBridge::getLabelSuffix() const +{ + /* Singu TODO? + static LLCachedControl folder_loading_message_delay(gSavedSettings, "FolderLoadingMessageWaitTime", 0.5f); + + if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= folder_loading_message_delay()) + { + return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str()); + } + */ + + std::string suffix = ""; + // Listing folder case + if (LLMarketplaceData::instance().isListed(getUUID())) + { + suffix = llformat("%d",LLMarketplaceData::instance().getListingID(getUUID())); + if (suffix.empty()) + { + suffix = LLTrans::getString("MarketplaceNoID"); + } + suffix = " (" + suffix + ")"; + if (LLMarketplaceData::instance().getActivationState(getUUID())) + { + suffix += " (" + LLTrans::getString("MarketplaceLive") + ")"; + } + } + // Version folder case + else if (LLMarketplaceData::instance().isVersionFolder(getUUID())) + { + suffix += " (" + LLTrans::getString("MarketplaceActive") + ")"; + } + // Add stock amount + bool updating = LLMarketplaceData::instance().isUpdating(getUUID()); + if (!updating) + { + // Skip computation (expensive) if we're waiting for update anyway. Use the old value in that case. + m_stockCountCache = compute_stock_count(getUUID()); + } + if (m_stockCountCache == 0) + { + suffix += " (" + LLTrans::getString("MarketplaceNoStock") + ")"; + } + else if (m_stockCountCache != COMPUTE_STOCK_INFINITE) + { + if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + suffix += " (" + LLTrans::getString("MarketplaceStock"); + } + else + { + suffix += " (" + LLTrans::getString("MarketplaceMax"); + } + if (m_stockCountCache == COMPUTE_STOCK_NOT_EVALUATED) + { + suffix += "=" + LLTrans::getString("MarketplaceUpdating") + ")"; + } + else + { + suffix += "=" + llformat("%d", m_stockCountCache) + ")"; + } + } + // Add updating suffix + if (updating) + { + suffix += " (" + LLTrans::getString("MarketplaceUpdating") + ")"; + } + return LLInvFVBridge::getLabelSuffix() + suffix; +} + +LLFontGL::StyleFlags LLMarketplaceFolderBridge::getLabelStyle() const +{ + return (LLMarketplaceData::instance().getActivationState(getUUID()) ? LLFontGL::BOLD : LLFontGL::NORMAL); +} + + + + // helper stuff bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv* move_inv) { @@ -3852,7 +4316,7 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response if(option == 0 && object) { - if (cat_and_wear && cat_and_wear->mWear) + if (cat_and_wear && cat_and_wear->mWear) // && !cat_and_wear->mFolderResponded) { LLInventoryObject::object_list_t inventory_objects; object->getInventoryContents(inventory_objects); @@ -3957,16 +4421,39 @@ void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_c } else { - LLPointer cb = NULL; + LLPointer cb = nullptr; link_inventory_object(mUUID, LLConstPointer(inv_item), cb); } } +// Callback for drop item if DAMA required... +void LLFolderBridge::callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + //std::string tooltip_msg; + dragItemIntoFolder(inv_item, TRUE, /*tooltip_msg,*/ FALSE); + } +} + +// Callback for drop category if DAMA required... +void LLFolderBridge::callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_category) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + //std::string tooltip_msg; + dragCategoryIntoFolder(inv_category, TRUE, /*tooltip_msg,*/ FALSE); + } +} + // This is used both for testing whether an item can be dropped // into the folder, as well as performing the actual drop, depending // if drop == TRUE. BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, - BOOL drop) + BOOL drop, + BOOL user_confirm) { LLInventoryModel* model = getInventoryModel(); @@ -3977,19 +4464,23 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLInventoryPanel* destination_panel = mInventoryPanel.get(); if (!destination_panel) return false; + LLInventoryFilter* filter = getInventoryFilter(); + if (!filter) return false; + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); - const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID& marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID from_folder_uuid = inv_item->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_favorites = (mUUID == favorites_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); - const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); - const BOOL move_is_from_outbox = model->isObjectDescendentOf(inv_item->getUUID(), outbox_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); + const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id); LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); BOOL accept = FALSE; @@ -4059,57 +4550,46 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, //-------------------------------------------------------------------------------- // Determine if item can be moved & dropped - // + // Note: if user_confirm is false, we already went through those accept logic test and can skip them accept = TRUE; - if (!is_movable) + if (user_confirm && !is_movable) { accept = FALSE; } - else if ((mUUID == inv_item->getParentUUID()) && !move_is_into_favorites) + else if (user_confirm && (mUUID == inv_item->getParentUUID()) && !move_is_into_favorites) { accept = FALSE; } - else if (move_is_into_current_outfit || move_is_into_outfit) + else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit)) { accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); } - else if (move_is_into_favorites || move_is_into_landmarks) + else if (user_confirm && (/*move_is_into_favorites ||*/ move_is_into_landmarks)) { accept = can_move_to_landmarks(inv_item); } - else if (move_is_into_outbox) + else if (user_confirm && move_is_into_marketplacelistings) { - accept = can_move_to_outbox(inv_item); - - if (accept) - { - const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(outbox_id, mUUID); - - int existing_item_count = LLToolDragAndDrop::instance().getCargoIDsCount(); - - if (master_folder != NULL) - { - LLInventoryModel::cat_array_t existing_categories; - LLInventoryModel::item_array_t existing_items; - - gInventory.collectDescendents(master_folder->getUUID(), existing_categories, existing_items, FALSE); - - existing_item_count += existing_items.size(); - } - - if (existing_item_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxItemCount")) - { - accept = FALSE; - } - } + const LLViewerInventoryCategory* master_folder = model->getFirstDescendantOf(marketplacelistings_id, mUUID); + LLViewerInventoryCategory* dest_folder = getCategory(); + std::string tooltip_msg; + accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, LLToolDragAndDrop::instance().getCargoCount() - LLToolDragAndDrop::instance().getCargoIndex()); + } + + // Check that the folder can accept this item based on folder/item type compatibility (e.g. stock folder compatibility) + if (user_confirm && accept) + { + LLViewerInventoryCategory * dest_folder = getCategory(); + accept = dest_folder->acceptItem(inv_item); } LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); // Check whether the item being dragged from active inventory panel - if (accept && active_panel) + // passes the filter of the destination panel. + if (user_confirm && accept && active_panel) { LLFolderView* active_folder_view = active_panel->getRootFolder(); if (!active_folder_view) return false; @@ -4128,14 +4608,29 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // and then select any item that is dropped only in the panel that it is dropped in if (active_panel && (destination_panel != active_panel)) { - active_panel->unSelectAll(); + active_panel->unSelectAll(); + } + // Dropping in or out of marketplace needs (sometimes) confirmation + if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) + { + if ((move_is_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(inv_item->getUUID()) + || LLMarketplaceData::instance().isListedAndActive(inv_item->getUUID()))) || + (move_is_into_marketplacelistings && LLMarketplaceData::instance().isInActiveFolder(mUUID))) + { + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropItemIntoFolder, this, _1, _2, inv_item)); + return true; + } + if (move_is_into_marketplacelistings && !move_is_from_marketplacelistings) + { + LLNotificationsUtil::add("ConfirmMerchantMoveInventory", LLSD(), LLSD(), boost::bind(&LLFolderBridge::callback_dropItemIntoFolder, this, _1, _2, inv_item)); + return true; + } } //-------------------------------------------------------------------------------- // Destination folder logic // - /* Singu TODO: Favorites // REORDER // (only reorder the item in Favorites folder) if ((mUUID == inv_item->getParentUUID()) && move_is_into_favorites) @@ -4145,13 +4640,13 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { LLUUID srcItemId = inv_item->getUUID(); LLUUID destItemId = itemp->getListener()->getUUID(); - gInventory.rearrangeFavoriteLandmarks(srcItemId, destItemId); + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(srcItemId, destItemId); } } // FAVORITES folder // (copy the item) - else */if (move_is_into_favorites) + else if (move_is_into_favorites) { dropToFavorites(inv_item); } @@ -4161,16 +4656,11 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { dropToOutfit(inv_item, move_is_into_current_outfit); } - else if (move_is_into_outbox) + // MARKETPLACE LISTINGS folder + // Move the item + else if (move_is_into_marketplacelistings) { - if (move_is_from_outbox) - { - move_item_within_outbox(inv_item, mUUID, LLToolDragAndDrop::getOperationId()); - } - else - { - copy_item_to_outbox(inv_item, mUUID, LLUUID::null, LLToolDragAndDrop::getOperationId()); - } + move_item_to_marketplacelistings(inv_item, mUUID); } // NORMAL or TRASH folder // (move the item, restamp if into trash) @@ -4189,7 +4679,21 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, move_is_into_trash); } - // + if (move_is_from_marketplacelistings) + { + // If we move from an active (listed) listing, checks that it's still valid, if not, unlist + LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); + if (version_folder_id.notNull()) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); + if (!validate_marketplacelistings(cat, NULL)) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } + } + } + + // //-------------------------------------------------------------------------------- } } @@ -4233,12 +4737,12 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } // Don't allow to move a single item to Favorites or Landmarks // if it is not a landmark or a link to a landmark. - else if ((move_is_into_favorites || move_is_into_landmarks) + else if ((/*move_is_into_favorites ||*/ move_is_into_landmarks) && !can_move_to_landmarks(inv_item)) { accept = FALSE; } - else if (move_is_into_outbox) + else if (move_is_into_marketplacelistings) { accept = FALSE; } @@ -4268,7 +4772,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) { - if (move_is_into_outbox) + if (move_is_into_marketplacelistings) { accept = FALSE; } @@ -4294,7 +4798,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = TRUE; - if (move_is_into_outbox) + if (move_is_into_marketplacelistings) { accept = FALSE; } @@ -4304,7 +4808,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } // Don't allow to move a single item to Favorites or Landmarks // if it is not a landmark or a link to a landmark. - else if (move_is_into_favorites || move_is_into_landmarks) + else if (/*move_is_into_favorites ||*/ move_is_into_landmarks) { accept = can_move_to_landmarks(inv_item); } @@ -4432,10 +4936,12 @@ void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if(isItemInTrash()) { addTrashContextMenuOptions(items, disabled_items); - } - else if(isOutboxFolder()) + } + else if (isMarketplaceListingsFolder()) { - addOutboxContextMenuOptions(flags, items, disabled_items); + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); } else { @@ -4524,9 +5030,11 @@ void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t items; menuentry_vec_t disabled_items; - if (isOutboxFolder()) + if (isMarketplaceListingsFolder()) { - addOutboxContextMenuOptions(flags, items, disabled_items); + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); } else { @@ -4599,9 +5107,11 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t disabled_items; LL_DEBUGS() << "LLLandmarkBridge::buildContextMenu()" << LL_ENDL; - if(isOutboxFolder()) + if (isMarketplaceListingsFolder()) { - addOutboxContextMenuOptions(flags, items, disabled_items); + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); } else { @@ -4878,10 +5388,12 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if(isItemInTrash()) { addTrashContextMenuOptions(items, disabled_items); - } - else if(isOutboxFolder()) + } + else if (isMarketplaceListingsFolder()) { - items.push_back(std::string("Delete")); + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); } else { @@ -5053,6 +5565,25 @@ void LLNotecardBridge::openItem() } } +void LLNotecardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + LL_DEBUGS() << "LLNotecardBridge::buildContextMenu()" << LL_ENDL; + + if (isMarketplaceListingsFolder()) + { + menuentry_vec_t items; + menuentry_vec_t disabled_items; + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); + hide_context_entries(menu, items, disabled_items); + } + else + { + LLItemBridge::buildContextMenu(menu, flags); + } +} + // +=================================================+ // | LLGestureBridge | // +=================================================+ @@ -5104,7 +5635,7 @@ void LLGestureBridge::performAction(LLInventoryModel* model, std::string action) gInventory.updateItem(item); gInventory.notifyObservers(); } - else if (isRemoveAction(action)) + else if ("deactivate" == action || isRemoveAction(action)) { LLGestureMgr::instance().deactivateGesture(mUUID); @@ -5189,9 +5720,11 @@ void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) + else if (isMarketplaceListingsFolder()) { - items.push_back(std::string("Delete")); + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); } else { @@ -5243,9 +5776,11 @@ void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t disabled_items; LL_DEBUGS() << "LLAnimationBridge::buildContextMenu()" << LL_ENDL; - if(isOutboxFolder()) + if (isMarketplaceListingsFolder()) { - items.push_back(std::string("Delete")); + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); } else { @@ -5280,9 +5815,7 @@ void LLAnimationBridge::performAction(LLInventoryModel* model, std::string actio if ((action == "playworld") || (action == "playlocal")) { // See if we can bring an existing preview to the front - if (LLPreview::show(mUUID)) - return; - + if (LLPreview::show(mUUID)) return; if (getItem()) { LLPreviewAnim::e_activation_type activate = LLPreviewAnim::NONE; @@ -5420,7 +5953,6 @@ std::string LLObjectBridge::getLabelSuffix() const std::string attachment_point_name; if (gAgentAvatarp->getAttachedPointName(mUUID, attachment_point_name)) { - // e.g. "(worn on ...)" / "(attached to ...)" LLStringUtil::format_map_t args; args["[ATTACHMENT_POINT]"] = LLTrans::getString(attachment_point_name); @@ -5456,12 +5988,13 @@ void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attach // Check for duplicate request. if (isAgentAvatarValid() && - (gAgentAvatarp->attachmentWasRequested(item_id) || - gAgentAvatarp->isWearingAttachment(item_id))) + gAgentAvatarp->isWearingAttachment(item_id)) { - LL_WARNS() << "duplicate attachment request, ignoring" << LL_ENDL; + LL_WARNS() << "ATT duplicate attachment request, ignoring" << LL_ENDL; return; } + + LL_DEBUGS("Avatar") << "ATT add rez request for " << item->getName() << " id " << item_id << LL_ENDL; gAgentAvatarp->addAttachmentRequest(item_id); S32 attach_pt = 0; @@ -5526,27 +6059,6 @@ bool confirm_attachment_rez(const LLSD& notification, const LLSD& response) if (itemp) { - /* - { - U8 attachment_pt = notification["payload"]["attachment_point"].asInteger(); - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ObjectData); - msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID()); - msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner()); - msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt); - pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions()); - msg->addStringFast(_PREHASH_Name, itemp->getName()); - msg->addStringFast(_PREHASH_Description, itemp->getDescription()); - msg->sendReliable(gAgent.getRegion()->getHost()); - return false; - } - */ - // Queue up attachments to be sent in next idle tick, this way the // attachments are batched up all into one message versus each attachment // being sent in its own separate attachments message. @@ -5575,10 +6087,12 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if(isItemInTrash()) { addTrashContextMenuOptions(items, disabled_items); - } - else if(isOutboxFolder()) + } + else if (isMarketplaceListingsFolder()) { - items.push_back(std::string("Delete")); + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); } else { @@ -5860,9 +6374,11 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } - else if(isOutboxFolder()) + else if (isMarketplaceListingsFolder()) { - items.push_back(std::string("Delete")); + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); } else { // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere @@ -6235,15 +6751,6 @@ void LLMeshBridge::openItem() } } -void LLMeshBridge::previewItem() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - // preview mesh - } -} - void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { LL_DEBUGS() << "LLMeshBridge::buildContextMenu()" << LL_ENDL; @@ -6260,9 +6767,11 @@ void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) items.push_back(std::string("Restore Item")); } - else if(isOutboxFolder()) + else if (isMarketplaceListingsFolder()) { - addOutboxContextMenuOptions(flags, items, disabled_items); + addMarketplaceContextMenuOptions(flags, items, disabled_items); + items.push_back(std::string("Properties")); + getClipboardEntries(false, items, disabled_items, flags); } else { @@ -6314,7 +6823,7 @@ void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) else { getClipboardEntries(false, items, disabled_items, flags); - if (LLPanelMainInventory::getActiveInventory() && !isOutboxFolder() && isAgentInventory()) + if (LLPanelMainInventory::getActiveInventory() && isAgentInventory()) build_context_menu_folder_options(getInventoryModel(), getFolderID(), items, disabled_items); addDeleteContextMenuOptions(items, disabled_items); } @@ -6709,7 +7218,7 @@ void LLWearableBridgeAction::wearOnAvatar() LLViewerInventoryItem* item = getItem(); if(item) { - if (get_is_item_worn(item)) + if (get_is_item_worn(item->getUUID())) { LLAppearanceMgr::instance().removeItemFromAvatar(item->getUUID()); } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 0249458a3..84bbc985e 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -138,7 +138,7 @@ protected: virtual void addDeleteContextMenuOptions(menuentry_vec_t &items, menuentry_vec_t &disabled_items); virtual void addOpenRightClickMenuOption(menuentry_vec_t &items); - virtual void addOutboxContextMenuOptions(U32 flags, + virtual void addMarketplaceContextMenuOptions(U32 flags, menuentry_vec_t &items, menuentry_vec_t &disabled_items); protected: @@ -153,8 +153,8 @@ protected: BOOL isAgentInventory() const; // false if lost or in the inventory library BOOL isCOFFolder() const; // true if COF or descendant of BOOL isInboxFolder() const; // true if COF or descendant of marketplace inbox - BOOL isOutboxFolderDirectParent() const; - const LLUUID getOutboxFolder() const; + + BOOL isMarketplaceListingsFolder() const; // true if descendant of Marketplace listings folder virtual BOOL isItemPermissive() const; static void changeItemParent(LLInventoryModel* model, @@ -167,7 +167,10 @@ protected: BOOL restamp); void removeBatchNoCheck(std::vector& batch); public: - BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox + + BOOL callback_cutToClipboard(const LLSD& notification, const LLSD& response); + BOOL perform_cutToClipboard(); + protected: LLHandle mInventoryPanel; LLFolderView* mRoot; @@ -178,6 +181,7 @@ protected: void purgeItem(LLInventoryModel *model, const LLUUID &uuid); virtual void buildDisplayName() const {} + void removeObject(LLInventoryModel* model, const LLUUID& uuid); }; class AIFilePicker; @@ -247,8 +251,10 @@ public: mWearables(FALSE) {} - BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop); - BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop); + BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, BOOL user_confirm = TRUE); + BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, BOOL user_confirm = TRUE); + void callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item); + void callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_item); virtual void buildDisplayName() const; @@ -263,8 +269,9 @@ public: virtual LLUIImagePtr getIcon() const; virtual LLUIImagePtr getIconOpen() const; virtual LLUIImagePtr getIconOverlay() const; - static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); + virtual std::string getLabelSuffix() const; + virtual LLFontGL::StyleFlags getLabelStyle() const; virtual BOOL renameItem(const std::string& new_name); @@ -333,10 +340,15 @@ public: static LLHandle sSelf; static void staticFolderOptionsMenu(); -private: +protected: + void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response, bool only_copies); + void perform_pasteFromClipboard(bool only_copies); + void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level); + LLUIImagePtr getFolderIcon(BOOL is_open) const; bool mCallingCards; bool mWearables; + std::string mMessage; LLRootHandle mHandle; }; @@ -422,6 +434,7 @@ public: const LLUUID& uuid) : LLItemBridge(inventory, root, uuid) {} virtual void openItem(); + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); }; class LLGestureBridge : public LLItemBridge @@ -468,7 +481,7 @@ public: virtual LLUIImagePtr getIcon() const; virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); - virtual BOOL isItemWearable() const { return TRUE; } + virtual BOOL isItemWearable() const { return TRUE; } virtual std::string getLabelSuffix() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual BOOL renameItem(const std::string& new_name); @@ -502,7 +515,7 @@ public: virtual LLUIImagePtr getIcon() const; virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); - virtual BOOL isItemWearable() const { return TRUE; } + virtual BOOL isItemWearable() const { return TRUE; } virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual std::string getLabelSuffix() const; virtual BOOL renameItem(const std::string& new_name); @@ -567,7 +580,6 @@ class LLMeshBridge : public LLItemBridge public: virtual LLUIImagePtr getIcon() const; virtual void openItem(); - virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); protected: @@ -635,7 +647,6 @@ class LLRecentInventoryBridgeBuilder : public LLInventoryFolderViewModelBuilder { public: LLRecentInventoryBridgeBuilder() {} - // Overrides FolderBridge for Recent Inventory Panel. // It use base functionality for bridges other than FolderBridge. virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, @@ -647,6 +658,31 @@ public: U32 flags = 0x00) const; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Marketplace Inventory Panel related classes +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMarketplaceFolderBridge : public LLFolderBridge +{ +public: + // Overloads some display related methods specific to folders in a marketplace floater context + LLMarketplaceFolderBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid); + + virtual LLUIImagePtr getIcon() const; + virtual LLUIImagePtr getIconOpen() const; + virtual std::string getLabelSuffix() const; + virtual LLFontGL::StyleFlags getLabelStyle() const; + +private: + LLUIImagePtr getMarketplaceFolderIcon(BOOL is_open) const; + // Those members are mutable because they are cached variables to speed up display, not a state variables + mutable S32 m_depth; + mutable S32 m_stockCountCache; +}; + + void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment, bool replace = false); @@ -674,4 +710,8 @@ public: virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu); }; +// Helper functions to classify actions. +bool isAddAction(const std::string& action); +bool isRemoveAction(const std::string& action); + #endif // LL_LLINVENTORYBRIDGE_H diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index bfa88f0ea..c43103fce 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -29,45 +29,52 @@ #include "llinventoryfilter.h" // viewer includes +#include "llappearancemgr.h" #include "llfoldervieweventlistener.h" #include "llfolderviewitem.h" +#include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" +#include "llinventoryfunctions.h" +#include "llmarketplacefunctions.h" #include "llviewercontrol.h" #include "llfolderview.h" #include "llinventorybridge.h" #include "llviewerfoldertype.h" +#include "llstartup.h" #include "llagentwearables.h" #include "llvoavatarself.h" -#include "llinventoryclipboard.h" // linden library includes +#include "llinventoryclipboard.h" #include "lltrans.h" -LLInventoryFilter::FilterOps::FilterOps() : - mFilterObjectTypes(0xffffffffffffffffULL), - mFilterCategoryTypes(0xffffffffffffffffULL), - mFilterWearableTypes(0xffffffffffffffffULL), - mMinDate(time_min()), - mMaxDate(time_max()), - mHoursAgo(0), - mShowFolderState(SHOW_NON_EMPTY_FOLDERS), - mPermissions(PERM_NONE), - mFilterTypes(FILTERTYPE_OBJECT), - mFilterWorn(false), - mFilterUUID(LLUUID::null), - mFilterLinks(FILTERLINK_INCLUDE_LINKS) +LLInventoryFilter::FilterOps::FilterOps(const Params& p) +: mFilterTypes(p.types), + mFilterObjectTypes(p.object_types), + mFilterWearableTypes(p.wearable_types), + mFilterCategoryTypes(p.category_types), + mFilterLinks(p.links), + mFilterUUID(p.uuid), + mMinDate(p.date_range.min_date), + mMaxDate(p.date_range.max_date), + mHoursAgo(p.hours_ago), + mDateSearchDirection(p.date_search_direction), + mShowFolderState(p.show_folder_state), + mPermissions(p.permissions), + mFilterWornItems(p.worn_items) { } ///---------------------------------------------------------------------------- /// Class LLInventoryFilter ///---------------------------------------------------------------------------- -LLInventoryFilter::LLInventoryFilter(const std::string& name) -: mName(name), +LLInventoryFilter::LLInventoryFilter(const Params& p) +: mFilterOps(p.filter_ops), + mFilterSubString(p.substring), + mName(p.name), mFilterModified(FILTER_NONE), - mFilterOps(), - mFilterSubString(), + mEmptyLookupMessage("InventoryNoMatchingItems"), mCurrentGeneration(0), mFirstRequiredGeneration(0), mFirstSuccessGeneration(0) @@ -76,24 +83,21 @@ LLInventoryFilter::LLInventoryFilter(const std::string& name) mSubStringMatchOffset = std::string::npos; mFilterCount = 0; + // Singu Note: Why aren't we calling fromParams here? // copy mFilterOps into mDefaultFilterOps markDefault(); } -LLInventoryFilter::~LLInventoryFilter() -{ -} - bool LLInventoryFilter::check(LLFolderViewItem* item) { // Clipboard cut items are *always* filtered so we need this value upfront const LLFolderViewEventListener* listener = item->getListener(); const LLUUID item_id = listener ? listener->getUUID() : LLUUID::null; - const bool passed_clipboard = item_id.notNull() ? checkAgainstClipboard(item_id) : true; + const bool passed_clipboard = listener && item_id.notNull() ? checkAgainstClipboard(item_id) : true; // If it's a folder and we're showing all folders, return automatically. - const BOOL is_folder = (dynamic_cast(item) != NULL); + const BOOL is_folder = listener->getInventoryType() == LLInventoryType::IT_CATEGORY; if (is_folder && (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS)) { return passed_clipboard; @@ -101,11 +105,11 @@ bool LLInventoryFilter::check(LLFolderViewItem* item) mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; - const BOOL passed_filtertype = checkAgainstFilterType(item); - const BOOL passed_permissions = checkAgainstPermissions(item); - const BOOL passed_filterlink = checkAgainstFilterLinks(item); - const BOOL passed_wearable = !mFilterOps.mFilterWorn || (gAgentWearables.isWearingItem(item_id) || (gAgentAvatarp && gAgentAvatarp->isWearingAttachment(item_id))); - const BOOL passed = (passed_filtertype && + const bool passed_filtertype = checkAgainstFilterType(item); + const bool passed_permissions = checkAgainstPermissions(item); + const bool passed_filterlink = checkAgainstFilterLinks(item); + const bool passed_wearable = (mFilterOps.mFilterTypes & FILTERTYPE_WORN) != FILTERTYPE_WORN || (gAgentWearables.isWearingItem(item_id) || (gAgentAvatarp && gAgentAvatarp->isWearingAttachment(item_id))); + const bool passed = (passed_filtertype && passed_permissions && passed_filterlink && passed_clipboard && @@ -126,8 +130,7 @@ bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) const const LLFolderViewEventListener* listener = folder->getListener(); if (!listener) { - LL_WARNS() << "Folder view event listener not found." << LL_ENDL; - llassert(false); // crash in development builds + LL_ERRS() << "Folder view event listener not found." << LL_ENDL; return false; } @@ -148,11 +151,73 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const } // when applying a filter, matching folders get their contents downloaded first + // but make sure we are not interfering with pre-download if (isNotDefault() - && !gInventory.isCategoryComplete(folder_id)) + && !gInventory.isCategoryComplete(folder_id) + && LLStartUp::getStartupState() > STATE_WEARABLES_WAIT) { LLInventoryModelBackgroundFetch::instance().start(folder_id); } + + // Marketplace folder filtering + const U32 filterTypes = mFilterOps.mFilterTypes; + const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE | + FILTERTYPE_MARKETPLACE_UNASSOCIATED | FILTERTYPE_MARKETPLACE_LISTING_FOLDER | + FILTERTYPE_NO_MARKETPLACE_ITEMS; + if (filterTypes & marketplace_filter) + { + S32 depth = depth_nesting_in_marketplace(folder_id); + + if (filterTypes & FILTERTYPE_NO_MARKETPLACE_ITEMS) + { + if (depth >= 0) + { + return false; + } + } + + if (filterTypes & FILTERTYPE_MARKETPLACE_LISTING_FOLDER) + { + if (depth > 1) + { + return false; + } + } + + if (depth > 0) + { + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + if (filterTypes & FILTERTYPE_MARKETPLACE_ACTIVE) + { + if (!LLMarketplaceData::instance().getActivationState(listing_uuid)) + { + return false; + } + } + else if (filterTypes & FILTERTYPE_MARKETPLACE_INACTIVE) + { + if (!LLMarketplaceData::instance().isListed(listing_uuid) || LLMarketplaceData::instance().getActivationState(listing_uuid)) + { + return false; + } + } + else if (filterTypes & FILTERTYPE_MARKETPLACE_UNASSOCIATED) + { + if (LLMarketplaceData::instance().isListed(listing_uuid)) + { + return false; + } + } + } + } + + // show folder links + LLViewerInventoryItem* item = gInventory.getItem(folder_id); + if (item && item->getActualType() == LLAssetType::AT_LINK_FOLDER) + { + return passed_clipboard; + } + if (mFilterOps.mFilterTypes & FILTERTYPE_CATEGORY) { // Can only filter categories for items in your inventory @@ -184,6 +249,7 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con // Pass if this item's type is of the correct filter type if (filterTypes & FILTERTYPE_OBJECT) { + // If it has no type, pass it, unless it's a link. if (object_type == LLInventoryType::IT_NONE) { @@ -203,15 +269,10 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con // Pass if this item is the target UUID or if it links to the target UUID if (filterTypes & FILTERTYPE_UUID) { - if (!object) - { - return FALSE; - } + if (!object) return FALSE; if (object->getLinkedUUID() != mFilterOps.mFilterUUID) - { return FALSE; - } } //////////////////////////////////////////////////////////////////////////////// @@ -230,10 +291,18 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con { earliest = 0; } - if (listener->getCreationDate() < earliest || - listener->getCreationDate() > mFilterOps.mMaxDate) + + if (FILTERDATEDIRECTION_NEWER == mFilterOps.mDateSearchDirection || isSinceLogoff()) { - return FALSE; + if (listener->getCreationDate() < earliest || + listener->getCreationDate() > mFilterOps.mMaxDate) + return FALSE; + } + else + { + if (listener->getCreationDate() > earliest || + listener->getCreationDate() > mFilterOps.mMaxDate) + return FALSE; } } @@ -245,9 +314,7 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con LLWearableType::EType type = listener->getWearableType(); if ((0x1LL << type & mFilterOps.mFilterWearableTypes) == 0) { - { - return FALSE; - } + return FALSE; } } @@ -262,7 +329,11 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con if (is_hidden_if_empty) { // Force the fetching of those folders so they are hidden if they really are empty... - gInventory.fetchDescendentsOf(object_id); + // But don't interfere with startup download + if (LLStartUp::getStartupState() > STATE_WEARABLES_WAIT) + { + gInventory.fetchDescendentsOf(object_id); + } LLInventoryModel::cat_array_t* cat_array = NULL; LLInventoryModel::item_array_t* item_array = NULL; @@ -280,9 +351,73 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con } } + //////////////////////////////////////////////////////////////////////////////// + // FILTERTYPE_WORN + // Pass if this item is worn + if (filterTypes & FILTERTYPE_WORN) + { + if (!object) return FALSE; + LLUUID cat_id = object->getParentUUID(); + const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); + return (get_is_item_worn(object_id) // if it's worn + && !LLAppearanceMgr::instance().getIsInCOF(object_id) // if it's not in CoF + && (!cat || cat->getPreferredType() != LLFolderType::FT_OUTFIT) // if it's not in an outfit + && !object->getIsLinkType()); // and it's not a link + } + return TRUE; } +bool LLInventoryFilter::checkAgainstFilterType(const LLInventoryItem* item) const +{ + LLInventoryType::EType object_type = item->getInventoryType(); + const LLUUID object_id = item->getUUID(); + + const U32 filterTypes = mFilterOps.mFilterTypes; + + //////////////////////////////////////////////////////////////////////////////// + // FILTERTYPE_OBJECT + // Pass if this item's type is of the correct filter type + if (filterTypes & FILTERTYPE_OBJECT) + { + // If it has no type, pass it, unless it's a link. + if (object_type == LLInventoryType::IT_NONE) + { + if (item && item->getIsLinkType()) + { + return false; + } + } + else if ((1LL << object_type & mFilterOps.mFilterObjectTypes) == U64(0)) + { + return false; + } + } + + //////////////////////////////////////////////////////////////////////////////// + // FILTERTYPE_UUID + // Pass if this item is the target UUID or if it links to the target UUID + if (filterTypes & FILTERTYPE_UUID) + { + if (!item) return false; + + if (item->getLinkedUUID() != mFilterOps.mFilterUUID) + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // FILTERTYPE_DATE + // Pass if this item is within the date range. + if (filterTypes & FILTERTYPE_DATE) + { + // We don't get the updated item creation date for the task inventory or + // a notecard embedded item. See LLTaskInvFVBridge::getCreationDate(). + return false; + } + + return true; +} + // Items and folders that are on the clipboard or, recursively, in a folder which // is on the clipboard must be filtered out if the clipboard is in the "cut" mode. bool LLInventoryFilter::checkAgainstClipboard(const LLUUID& object_id) const @@ -324,12 +459,23 @@ bool LLInventoryFilter::checkAgainstPermissions(const LLFolderViewItem* item) co return (perm & mFilterOps.mPermissions) == mFilterOps.mPermissions; } +bool LLInventoryFilter::checkAgainstPermissions(const LLInventoryItem* item) const +{ + if (!item) return false; + + LLPointer new_item = new LLViewerInventoryItem(item); + PermissionMask perm = new_item->getPermissionMask(); + new_item = NULL; + + return (perm & mFilterOps.mPermissions) == mFilterOps.mPermissions; +} + bool LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewItem* item) const { const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return TRUE; - const LLUUID object_id = listener->getUUID(); + const LLUUID& object_id = listener->getUUID(); const LLInventoryObject *object = gInventory.getObject(object_id); if (!object) return TRUE; @@ -351,6 +497,11 @@ std::string::size_type LLInventoryFilter::getStringMatchOffset() const return mSubStringMatchOffset; } +bool LLInventoryFilter::isDefault() const +{ + return !isNotDefault(); +} + // has user modified default filter params? bool LLInventoryFilter::isNotDefault() const { @@ -362,13 +513,12 @@ bool LLInventoryFilter::isNotDefault() const not_default |= (mFilterOps.mFilterTypes != mDefaultFilterOps.mFilterTypes); not_default |= (mFilterOps.mFilterLinks != mDefaultFilterOps.mFilterLinks); not_default |= (mFilterSubString.size() > 0); - not_default |= (mFilterOps.mFilterWorn != mDefaultFilterOps.mFilterWorn); not_default |= (mFilterOps.mPermissions != mDefaultFilterOps.mPermissions); not_default |= (mFilterOps.mMinDate != mDefaultFilterOps.mMinDate); not_default |= (mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate); not_default |= (mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo); - return not_default; + return not_default != 0; } bool LLInventoryFilter::isActive() const @@ -379,7 +529,6 @@ bool LLInventoryFilter::isActive() const || mFilterOps.mFilterTypes != FILTERTYPE_OBJECT || mFilterOps.mFilterLinks != FILTERLINK_INCLUDE_LINKS || mFilterSubString.size() - || mFilterOps.mFilterWorn != false || mFilterOps.mPermissions != PERM_NONE || mFilterOps.mMinDate != time_min() || mFilterOps.mMaxDate != time_max() @@ -402,7 +551,7 @@ void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types) current_types = types; if (more_bits_set && fewer_bits_set) { - // neither less or more restrive, both simultaneously + // neither less or more restrictive, both simultaneously // so we need to filter from scratch setModified(FILTER_RESTART); } @@ -443,6 +592,45 @@ void LLInventoryFilter::setFilterEmptySystemFolders() mFilterOps.mFilterTypes |= FILTERTYPE_EMPTYFOLDERS; } +void LLInventoryFilter::setFilterWornItems() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_WORN; +} + +void LLInventoryFilter::setFilterMarketplaceActiveFolders() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_ACTIVE; +} + +void LLInventoryFilter::setFilterMarketplaceInactiveFolders() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_INACTIVE; +} + +void LLInventoryFilter::setFilterMarketplaceUnassociatedFolders() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_UNASSOCIATED; +} + +void LLInventoryFilter::setFilterMarketplaceListingFolders(bool select_only_listing_folders) +{ + if (select_only_listing_folders) + { + mFilterOps.mFilterTypes |= FILTERTYPE_MARKETPLACE_LISTING_FOLDER; + setModified(FILTER_MORE_RESTRICTIVE); + } + else + { + mFilterOps.mFilterTypes &= ~FILTERTYPE_MARKETPLACE_LISTING_FOLDER; + setModified(FILTER_LESS_RESTRICTIVE); + } +} + +void LLInventoryFilter::setFilterNoMarketplaceFolder() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_NO_MARKETPLACE_ITEMS; +} + void LLInventoryFilter::setFilterUUID(const LLUUID& object_id) { if (mFilterOps.mFilterUUID == object_id) @@ -598,12 +786,37 @@ void LLInventoryFilter::setHoursAgo(U32 hours) { bool are_date_limits_valid = mFilterOps.mMinDate == time_min() && mFilterOps.mMaxDate == time_max(); - bool is_increasing = hours > mFilterOps.mHoursAgo; - bool is_increasing_from_zero = is_increasing && !mFilterOps.mHoursAgo && !isSinceLogoff(); - // *NOTE: need to cache last filter time, in case filter goes stale - BOOL less_restrictive = ((are_date_limits_valid && ((is_increasing && mFilterOps.mHoursAgo))) || !hours); - BOOL more_restrictive = ((are_date_limits_valid && (!is_increasing && hours)) || is_increasing_from_zero); + bool less_restrictive = false; + bool more_restrictive = false; + + switch (mFilterOps.mDateSearchDirection) + { + case FILTERDATEDIRECTION_NEWER: + less_restrictive = ((are_date_limits_valid && (hours > mFilterOps.mHoursAgo + && mFilterOps.mHoursAgo)) + || !hours); + + more_restrictive = ((are_date_limits_valid && (hours < mFilterOps.mHoursAgo + && hours)) + || (hours > mFilterOps.mHoursAgo + && !mFilterOps.mHoursAgo + && !isSinceLogoff())); + break; + case FILTERDATEDIRECTION_OLDER: + less_restrictive = ((are_date_limits_valid && (hours < mFilterOps.mHoursAgo + && mFilterOps.mHoursAgo)) + || !hours); + + more_restrictive = ((are_date_limits_valid && (hours > mFilterOps.mHoursAgo + && hours)) + || (hours < mFilterOps.mHoursAgo + && !mFilterOps.mHoursAgo + && !isSinceLogoff())); + break; + default: + break; + } mFilterOps.mHoursAgo = hours; mFilterOps.mMinDate = time_min(); @@ -632,18 +845,31 @@ void LLInventoryFilter::setHoursAgo(U32 hours) } } +void LLInventoryFilter::setDateSearchDirection(U32 direction) +{ + if (direction != mFilterOps.mDateSearchDirection) + { + mFilterOps.mDateSearchDirection = direction; + setModified(FILTER_RESTART); + } +} -void LLInventoryFilter::setFilterLinks(U64 filter_links) +U32 LLInventoryFilter::getDateSearchDirection() const +{ + return mFilterOps.mDateSearchDirection; +} + +void LLInventoryFilter::setFilterLinks(EFilterLink filter_links) { if (mFilterOps.mFilterLinks != filter_links) { - if (mFilterOps.mFilterLinks == FILTERLINK_EXCLUDE_LINKS || - mFilterOps.mFilterLinks == FILTERLINK_ONLY_LINKS) + mFilterOps.mFilterLinks = filter_links; + if (filter_links == FILTERLINK_EXCLUDE_LINKS || + filter_links == FILTERLINK_ONLY_LINKS) setModified(FILTER_MORE_RESTRICTIVE); else setModified(FILTER_LESS_RESTRICTIVE); } - mFilterOps.mFilterLinks = filter_links; } void LLInventoryFilter::setShowFolderState(EFolderShow state) @@ -897,68 +1123,71 @@ const std::string& LLInventoryFilter::getFilterText() { mFilterText += LLTrans::getString("Since Logoff"); } - - if (getFilterWorn()) + + if (getFilterWornItems()) { mFilterText += LLTrans::getString("Worn"); } - + return mFilterText; } -void LLInventoryFilter::toLLSD(LLSD& data) const +LLInventoryFilter& LLInventoryFilter::operator=( const LLInventoryFilter& other ) { - data["filter_types"] = (LLSD::Integer)getFilterObjectTypes(); - data["min_date"] = (LLSD::Integer)getMinDate(); - data["max_date"] = (LLSD::Integer)getMaxDate(); - data["hours_ago"] = (LLSD::Integer)getHoursAgo(); - data["show_folder_state"] = (LLSD::Integer)getShowFolderState(); - data["permissions"] = (LLSD::Integer)getFilterPermissions(); - data["substring"] = (LLSD::String)getFilterSubString(); - data["sort_order"] = (LLSD::Integer)getSortOrder(); - data["since_logoff"] = (LLSD::Boolean)isSinceLogoff(); + setFilterObjectTypes(other.getFilterObjectTypes()); + setDateRange(other.getMinDate(), other.getMaxDate()); + setHoursAgo(other.getHoursAgo()); + setDateSearchDirection(other.getDateSearchDirection()); + setShowFolderState(other.getShowFolderState()); + setFilterPermissions(other.getFilterPermissions()); + setFilterSubString(other.getFilterSubString()); + setDateRangeLastLogoff(other.isSinceLogoff()); + return *this; } -void LLInventoryFilter::fromLLSD(LLSD& data) + +void LLInventoryFilter::toParams(Params& params) const { - if(data.has("filter_types")) + params.filter_ops.types = getFilterObjectTypes(); + params.filter_ops.category_types = getFilterCategoryTypes(); + if (getFilterObjectTypes() & FILTERTYPE_WEARABLE) { - setFilterObjectTypes((U32)data["filter_types"].asInteger()); + params.filter_ops.wearable_types = getFilterWearableTypes(); + } + params.filter_ops.date_range.min_date = getMinDate(); + params.filter_ops.date_range.max_date = getMaxDate(); + params.filter_ops.hours_ago = getHoursAgo(); + params.filter_ops.date_search_direction = getDateSearchDirection(); + params.filter_ops.show_folder_state = getShowFolderState(); + params.filter_ops.permissions = getFilterPermissions(); + params.substring = getFilterSubString(); + params.since_logoff = isSinceLogoff(); +} + +void LLInventoryFilter::fromParams(const Params& params) +{ + if (!params.validateBlock()) + { + return; } - if(data.has("min_date") && data.has("max_date")) + setFilterObjectTypes(params.filter_ops.types); + setFilterCategoryTypes(params.filter_ops.category_types); + if (params.filter_ops.wearable_types.isProvided()) { - setDateRange(data["min_date"].asInteger(), data["max_date"].asInteger()); + setFilterWearableTypes(params.filter_ops.wearable_types); } - if(data.has("hours_ago")) + setDateRange(params.filter_ops.date_range.min_date, params.filter_ops.date_range.max_date); + setHoursAgo(params.filter_ops.hours_ago); + setDateSearchDirection(params.filter_ops.date_search_direction); + setShowFolderState(params.filter_ops.show_folder_state); + setFilterPermissions(params.filter_ops.permissions); + setFilterSubString(params.substring); + setDateRangeLastLogoff(params.since_logoff); + if (params.filter_ops.links.isProvided()) { - setHoursAgo((U32)data["hours_ago"].asInteger()); - } - - if(data.has("show_folder_state")) - { - setShowFolderState((EFolderShow)data["show_folder_state"].asInteger()); - } - - if(data.has("permissions")) - { - setFilterPermissions((PermissionMask)data["permissions"].asInteger()); - } - - if(data.has("substring")) - { - setFilterSubString(std::string(data["substring"].asString())); - } - - if(data.has("sort_order")) - { - setSortOrder((U32)data["sort_order"].asInteger()); - } - - if(data.has("since_logoff")) - { - setDateRangeLastLogoff((bool)data["since_logoff"].asBoolean()); + setFilterLinks(params.filter_ops.links); } } @@ -982,6 +1211,11 @@ U64 LLInventoryFilter::getFilterWearableTypes() const return mFilterOps.mFilterWearableTypes; } +U64 LLInventoryFilter::getFilterWornItems() const +{ + return mFilterOps.mFilterTypes & FILTERTYPE_WORN; +} + bool LLInventoryFilter::hasFilterString() const { return mFilterSubString.size() > 0; @@ -1010,7 +1244,7 @@ U32 LLInventoryFilter::getHoursAgo() const { return mFilterOps.mHoursAgo; } -U64 LLInventoryFilter::getFilterLinks() const +LLInventoryFilter::EFilterLink LLInventoryFilter::getFilterLinks() const { return mFilterOps.mFilterLinks; } @@ -1050,6 +1284,20 @@ S32 LLInventoryFilter::getFirstRequiredGeneration() const return mFirstRequiredGeneration; } +void LLInventoryFilter::setEmptyLookupMessage(const std::string& message) +{ + mEmptyLookupMessage = message; +} + +std::string LLInventoryFilter::getEmptyLookupMessage() const +{ + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(getFilterSubStringOrig()); + + return LLTrans::getString(mEmptyLookupMessage, args); + +} + bool LLInventoryFilter::areDateLimitsSet() { return mFilterOps.mMinDate != time_min() @@ -1062,3 +1310,21 @@ bool LLInventoryFilter::showAllResults() const return hasFilterString(); } + + +bool LLInventoryFilter::FilterOps::DateRange::validateBlock( bool emit_errors /*= true*/ ) const +{ + bool valid = LLInitParam::Block::validateBlock(emit_errors); + if (valid) + { + if (max_date() < min_date()) + { + if (emit_errors) + { + LL_WARNS() << "max_date should be greater or equal to min_date" << LL_ENDL; + } + valid = false; + } + } + return valid; +} diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 6fd95ba0d..789e37dcf 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -32,6 +32,7 @@ class LLFolderViewItem; class LLFolderViewFolder; +class LLInventoryItem; class LLInventoryFilter { @@ -58,7 +59,20 @@ public: FILTERTYPE_UUID = 0x1 << 2, // find the object with UUID and any links to it FILTERTYPE_DATE = 0x1 << 3, // search by date range FILTERTYPE_WEARABLE = 0x1 << 4, // search by wearable type - FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if empty + FILTERTYPE_EMPTYFOLDERS = 0x1 << 5, // pass if folder is not a system folder to be hidden if empty + FILTERTYPE_MARKETPLACE_ACTIVE = 0x1 << 6, // pass if folder is a marketplace active folder + FILTERTYPE_MARKETPLACE_INACTIVE = 0x1 << 7, // pass if folder is a marketplace inactive folder + FILTERTYPE_MARKETPLACE_UNASSOCIATED = 0x1 << 8, // pass if folder is a marketplace non associated (no market ID) folder + FILTERTYPE_MARKETPLACE_LISTING_FOLDER = 0x1 << 9, // pass iff folder is a listing folder + FILTERTYPE_NO_MARKETPLACE_ITEMS = 0x1 << 10, // pass iff folder is not under the marketplace + FILTERTYPE_WORN = 0x1 << 11 // search by worn items + + }; + + enum EFilterDateDirection + { + FILTERDATEDIRECTION_NEWER, + FILTERDATEDIRECTION_OLDER }; enum EFilterLink @@ -73,30 +87,97 @@ public: SO_NAME = 0, // Sort inventory by name SO_DATE = 0x1, // Sort inventory by date SO_FOLDERS_BY_NAME = 0x1 << 1, // Force folder sort by name - SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2 // Force system folders to be on top + SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2, // Force system folders to be on top + SO_FOLDERS_BY_WEIGHT = 0x1 << 3, // Force folder sort by weight, usually, amount of some elements in their descendents }; -struct FilterOps + struct FilterOps { - FilterOps(); + struct DateRange : public LLInitParam::Block + { + Optional min_date, + max_date; + + DateRange() + : min_date("min_date", time_min()), + max_date("max_date", time_max()) + {} + + bool validateBlock(bool emit_errors = true) const; + }; + + struct Params : public LLInitParam::Block + { + Optional types; + Optional object_types, + wearable_types, + category_types, + worn_items; + Optional links; + Optional uuid; + Optional date_range; + Optional hours_ago; + Optional date_search_direction; + Optional show_folder_state; + Optional permissions; + + Params() + : types("filter_types", FILTERTYPE_OBJECT), + object_types("object_types", 0xffffFFFFffffFFFFULL), + wearable_types("wearable_types", 0/*0xffffFFFFffffFFFFULL*/), + category_types("category_types", 0xffffFFFFffffFFFFULL), + worn_items("worn_items", 0xffffFFFFffffFFFFULL), + links("links", FILTERLINK_INCLUDE_LINKS), + uuid("uuid"), + date_range("date_range"), + hours_ago("hours_ago", 0), + date_search_direction("date_search_direction", FILTERDATEDIRECTION_NEWER), + show_folder_state("show_folder_state", SHOW_NON_EMPTY_FOLDERS), + permissions("permissions", PERM_NONE) + { + addSynonym(links, "filter_links"); + } + }; + + FilterOps(const Params& = Params()); + U32 mFilterTypes; - U64 mFilterObjectTypes, // For _OBJECT + U64 mFilterObjectTypes, // For _OBJECT mFilterWearableTypes, - mFilterLinks, - mFilterCategoryTypes; // For _CATEGORY + mFilterCategoryTypes, // For _CATEGORY + mFilterWornItems; + EFilterLink mFilterLinks; LLUUID mFilterUUID; // for UUID time_t mMinDate, mMaxDate; U32 mHoursAgo; + U32 mDateSearchDirection; + EFolderShow mShowFolderState; PermissionMask mPermissions; bool mFilterWorn; }; - LLInventoryFilter(const std::string& name); - virtual ~LLInventoryFilter(); + struct Params : public LLInitParam::Block + { + Optional name; + Optional filter_ops; + Optional substring; + Optional since_logoff; + + Params() + : name("name"), + filter_ops(""), + substring("substring"), + since_logoff("since_logoff") + {} + }; + + LLInventoryFilter(const Params& p = Params()); + LLInventoryFilter(const LLInventoryFilter& other) { *this = other; } + virtual ~LLInventoryFilter() {} // +-------------------------------------------------------------------+ // + Parameters @@ -105,21 +186,25 @@ struct FilterOps U64 getFilterObjectTypes() const; U64 getFilterCategoryTypes() const; U64 getFilterWearableTypes() const; + U64 getFilterWornItems() const; bool isFilterObjectTypesWith(LLInventoryType::EType t) const; void setFilterObjectTypes(U64 types); void setFilterCategoryTypes(U64 types); void setFilterUUID(const LLUUID &object_id); void setFilterWearableTypes(U64 types); void setFilterEmptySystemFolders(); + void setFilterMarketplaceActiveFolders(); + void setFilterMarketplaceInactiveFolders(); + void setFilterMarketplaceUnassociatedFolders(); + void setFilterMarketplaceListingFolders(bool select_only_listing_folders); + void setFilterNoMarketplaceFolder(); void updateFilterTypes(U64 types, U64& current_types); + void setFilterWornItems(); void setFilterSubString(const std::string& string); const std::string& getFilterSubString(BOOL trim = FALSE) const; const std::string& getFilterSubStringOrig() const { return mFilterSubStringOrig; } bool hasFilterString() const; - - void setFilterWorn(bool worn) { mFilterOps.mFilterWorn = worn; } - bool getFilterWorn() const { return mFilterOps.mFilterWorn; } void setFilterPermissions(PermissionMask perms); PermissionMask getFilterPermissions() const; @@ -131,14 +216,17 @@ struct FilterOps void setHoursAgo(U32 hours); U32 getHoursAgo() const; + void setDateSearchDirection(U32 direction); + U32 getDateSearchDirection() const; - void setFilterLinks(U64 filter_link); - U64 getFilterLinks() const; + void setFilterLinks(EFilterLink filter_link); + EFilterLink getFilterLinks() const; // +-------------------------------------------------------------------+ // + Execution And Results // +-------------------------------------------------------------------+ bool check(LLFolderViewItem* item); + bool check(const LLInventoryItem* item); bool checkFolder(const LLFolderViewFolder* folder) const; bool checkFolder(const LLUUID& folder_id) const; @@ -152,6 +240,9 @@ struct FilterOps void setShowFolderState( EFolderShow state); EFolderShow getShowFolderState() const; + void setEmptyLookupMessage(const std::string& message); + std::string getEmptyLookupMessage() const; + void setSortOrder(U32 order); U32 getSortOrder() const; @@ -178,6 +269,7 @@ struct FilterOps // +-------------------------------------------------------------------+ // + Default // +-------------------------------------------------------------------+ + bool isDefault() const; bool isNotDefault() const; void markDefault(); void resetDefault(); @@ -192,13 +284,17 @@ struct FilterOps // +-------------------------------------------------------------------+ // + Conversion // +-------------------------------------------------------------------+ - void toLLSD(LLSD& data) const; - void fromLLSD(LLSD& data); + void toParams(Params& params) const; + void fromParams(const Params& p); + + LLInventoryFilter& operator =(const LLInventoryFilter& other); private: bool areDateLimitsSet(); bool checkAgainstFilterType(const LLFolderViewItem* item) const; + bool checkAgainstFilterType(const LLInventoryItem* item) const; bool checkAgainstPermissions(const LLFolderViewItem* item) const; + bool checkAgainstPermissions(const LLInventoryItem* item) const; bool checkAgainstFilterLinks(const LLFolderViewItem* item) const; bool checkAgainstClipboard(const LLUUID& object_id) const; @@ -223,6 +319,7 @@ private: EFilterModified mFilterModified; std::string mFilterText; + std::string mEmptyLookupMessage; }; #endif diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 203dd7ec8..2d3c58bb3 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -37,6 +37,7 @@ #include "llinventorydefines.h" #include "llsdserialize.h" #include "llspinctrl.h" +#include "lltrans.h" #include "llui.h" #include "message.h" @@ -45,7 +46,7 @@ #include "llappviewer.h" //#include "llfirstuse.h" #include "llfloaterinventory.h" -#include "llfloateroutbox.h" +#include "llfloatermarketplacelistings.h" #include "llfocusmgr.h" #include "llfolderview.h" #include "llgesturemgr.h" @@ -57,6 +58,7 @@ #include "llinventorypanel.h" #include "lllineeditor.h" #include "llmarketplacenotifications.h" +#include "llmarketplacefunctions.h" #include "llmenugl.h" #include "llnotificationsutil.h" #include "llpanelmaininventory.h" @@ -89,6 +91,125 @@ BOOL LLInventoryState::sWearNewClothing = FALSE; LLUUID LLInventoryState::sWearNewClothingTransactionID; +// Helper function : callback to update a folder after inventory action happened in the background +void update_folder_cb(const LLUUID& dest_folder) +{ + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); + gInventory.updateCategory(dest_cat); + gInventory.notifyObservers(); +} + +// Helper function : Count only the copyable items, i.e. skip the stock items (which are no copy) +S32 count_copyable_items(LLInventoryModel::item_array_t& items) +{ + S32 count = 0; + for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) + { + LLViewerInventoryItem* item = *it; + if (item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + count++; + } + } + return count; +} + +// Helper function : Count only the non-copyable items, i.e. the stock items, skip the others +S32 count_stock_items(LLInventoryModel::item_array_t& items) +{ + S32 count = 0; + for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) + { + LLViewerInventoryItem* item = *it; + if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + count++; + } + } + return count; +} + +// Helper function : Count the number of stock folders +S32 count_stock_folders(LLInventoryModel::cat_array_t& categories) +{ + S32 count = 0; + for (LLInventoryModel::cat_array_t::const_iterator it = categories.begin(); it != categories.end(); ++it) + { + LLInventoryCategory* cat = *it; + if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + count++; + } + } + return count; +} + +// Helper funtion : Count the number of items (not folders) in the descending hierarchy +S32 count_descendants_items(const LLUUID& cat_id) +{ + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); + + S32 count = item_array->size(); + + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLViewerInventoryCategory* category = *iter; + count += count_descendants_items(category->getUUID()); + } + + return count; +} + +// Helper function : Returns true if the hierarchy contains nocopy items +bool contains_nocopy_items(const LLUUID& id) +{ + LLInventoryCategory* cat = gInventory.getCategory(id); + + if (cat) + { + // Get the content + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(id,cat_array,item_array); + + // Check all the items: returns true upon encountering a nocopy item + for (LLInventoryModel::item_array_t::iterator iter = item_array->begin(); iter != item_array->end(); iter++) + { + LLInventoryItem* item = *iter; + LLViewerInventoryItem * inv_item = (LLViewerInventoryItem *) item; + if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + return true; + } + } + + // Check all the sub folders recursively + for (LLInventoryModel::cat_array_t::iterator iter = cat_array->begin(); iter != cat_array->end(); iter++) + { + LLViewerInventoryCategory* cat = *iter; + if (contains_nocopy_items(cat->getUUID())) + { + return true; + } + } + } + else + { + LLInventoryItem* item = gInventory.getItem(id); + LLViewerInventoryItem * inv_item = (LLViewerInventoryItem *) item; + if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + return true; + } + } + + // Exit without meeting a nocopy item + return false; +} + // Generates a string containing the path to the item specified by // item_id. void append_path(const LLUUID& id, std::string& path) @@ -110,35 +231,149 @@ void append_path(const LLUUID& id, std::string& path) path.append(temp); } -// Path should already end on '/' if it is not empty. -void append_path_short(LLUUID const& id, std::string& path) +void update_marketplace_folder_hierarchy(const LLUUID cat_id) { - LLInventoryObject const* obj = gInventory.getObject(id); - if (!obj) return; + // When changing the marketplace status of a folder, the only thing that needs to happen is + // for all observers of the folder to, possibly, change the display label of the folder + // so that's the only thing we change on the update mask. + gInventory.addChangedMask(LLInventoryObserver::LABEL, cat_id); - LLUUID const root_id = gInventory.getRootFolderID(); - std::string const forward_slash("/"); + // Update all descendent folders down + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); - std::string result; - while(1) + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) { - LLUUID parent_id = obj->getParentUUID(); - if (parent_id == root_id || - !(obj = gInventory.getCategory(parent_id))) + LLInventoryCategory* category = *iter; + update_marketplace_folder_hierarchy(category->getUUID()); + } + return; +} + +void update_marketplace_category(const LLUUID& cur_uuid, bool perform_consistency_enforcement) +{ + // When changing the marketplace status of an item, we usually have to change the status of all + // folders in the same listing. This is because the display of each folder is affected by the + // overall status of the whole listing. + // Consequently, the only way to correctly update an item anywhere in the marketplace is to + // update the whole listing from its listing root. + // This is not as bad as it seems as we only update folders, not items, and the folder nesting depth + // is limited to 4. + // We also take care of degenerated cases so we don't update all folders in the inventory by mistake. + + if (cur_uuid.isNull() + || gInventory.getCategory(cur_uuid) == NULL + || gInventory.getCategory(cur_uuid)->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + return; + } + + // Grab marketplace listing data for this item + S32 depth = depth_nesting_in_marketplace(cur_uuid); + if (depth > 0) + { + // Retrieve the listing uuid this object is in + LLUUID listing_uuid = nested_parent_id(cur_uuid, depth); + LLViewerInventoryCategory* listing_cat = gInventory.getCategory(listing_uuid); + bool listing_cat_loaded = listing_cat != NULL && listing_cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; + + // Verify marketplace data consistency for this listing + if (perform_consistency_enforcement + && listing_cat_loaded + && LLMarketplaceData::instance().isListed(listing_uuid)) { - break; + LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); + S32 version_depth = depth_nesting_in_marketplace(version_folder_uuid); + if (version_folder_uuid.notNull() && (!gInventory.isObjectDescendentOf(version_folder_uuid, listing_uuid) || (version_depth != 2))) + { + LL_INFOS("SLM") << "Unlist and clear version folder as the version folder is not at the right place anymore!!" << LL_ENDL; + LLMarketplaceData::instance().setVersionFolder(listing_uuid, LLUUID::null,1); + } + else if (version_folder_uuid.notNull() + && gInventory.isCategoryComplete(version_folder_uuid) + && LLMarketplaceData::instance().getActivationState(version_folder_uuid) + && (count_descendants_items(version_folder_uuid) == 0) + && !LLMarketplaceData::instance().isUpdating(version_folder_uuid,version_depth)) + { + LL_INFOS("SLM") << "Unlist as the version folder is empty of any item!!" << LL_ENDL; + LLNotificationsUtil::add("AlertMerchantVersionFolderEmpty"); + LLMarketplaceData::instance().activateListing(listing_uuid, false,1); + } } - std::string temp; - temp.swap(result); - result = obj->getName(); - if (!temp.empty()) + + // Check if the count on hand needs to be updated on SLM + if (perform_consistency_enforcement + && listing_cat_loaded + && (compute_stock_count(listing_uuid) != LLMarketplaceData::instance().getCountOnHand(listing_uuid))) { - result.append(forward_slash); - result.append(temp); + LLMarketplaceData::instance().updateCountOnHand(listing_uuid,1); + } + // Update all descendents starting from the listing root + update_marketplace_folder_hierarchy(listing_uuid); + } + else if (depth == 0) + { + // If this is the marketplace listings root itself, update all descendents + if (gInventory.getCategory(cur_uuid)) + { + update_marketplace_folder_hierarchy(cur_uuid); + } + } + else + { + // If the folder is outside the marketplace listings root, clear its SLM data if needs be + if (perform_consistency_enforcement && LLMarketplaceData::instance().isListed(cur_uuid)) + { + LL_INFOS("SLM") << "Disassociate as the listing folder is not under the marketplace folder anymore!!" << LL_ENDL; + LLMarketplaceData::instance().clearListing(cur_uuid); + } + // Update all descendents if this is a category + if (gInventory.getCategory(cur_uuid)) + { + update_marketplace_folder_hierarchy(cur_uuid); } } - path.append(result); + return; +} + +// Iterate through the marketplace and flag for label change all categories that countain a stock folder (i.e. stock folders and embedding folders up the hierarchy) +void update_all_marketplace_count(const LLUUID& cat_id) +{ + // Get all descendent folders down + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array); + + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLInventoryCategory* category = *iter; + if (category->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + // Listing containing stock folders needs to be updated but not others + // Note: we take advantage of the fact that stock folder *do not* contain sub folders to avoid a recursive call here + update_marketplace_category(category->getUUID()); + } + else + { + // Explore the contained folders recursively + update_all_marketplace_count(category->getUUID()); + } + } +} + +void update_all_marketplace_count() +{ + // Get the marketplace root and launch the recursive exploration + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (!marketplace_listings_uuid.isNull()) + { + update_all_marketplace_count(marketplace_listings_uuid); + } + return; } void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name) @@ -161,7 +396,8 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, - const LLUUID& root_copy_id) + const LLUUID& root_copy_id, + bool move_no_copy_items) { // Create the initial folder LLUUID new_cat_uuid = gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName()); @@ -175,18 +411,41 @@ void copy_inventory_category(LLInventoryModel* model, LLInventoryModel::item_array_t* item_array; gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + // If root_copy_id is null, tell the marketplace model we'll be waiting for new items to be copied over for this folder + if (root_copy_id.isNull()) + { + LLMarketplaceData::instance().setValidationWaiting(root_id,count_descendants_items(cat->getUUID())); + } + // Copy all the items LLInventoryModel::item_array_t item_array_copy = *item_array; for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) { LLInventoryItem* item = *iter; - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - new_cat_uuid, - std::string(), - LLPointer(NULL)); + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid)); + + if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + // If the item is nocopy, we do nothing or, optionally, move it + if (move_no_copy_items) + { + // Reparent the item + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; + gInventory.changeItemParent(viewer_inv_item, new_cat_uuid, true); + } + // Decrement the count in root_id since that one item won't be copied over + LLMarketplaceData::instance().decrementValidationWaiting(root_id); + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + new_cat_uuid, + std::string(), + cb); + } } // Copy all the folders @@ -196,7 +455,7 @@ void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* category = *iter; if (category->getUUID() != root_id) { - copy_inventory_category(model, category, new_cat_uuid, root_id); + copy_inventory_category(model, category, new_cat_uuid, root_id, move_no_copy_items); } } } @@ -253,8 +512,9 @@ BOOL get_is_parent_to_worn_item(const LLUUID& id) return FALSE; } -BOOL get_is_item_worn(const LLInventoryItem *item) +BOOL get_is_item_worn(const LLUUID& id) { + const LLViewerInventoryItem* item = gInventory.getItem(id); if (!item) return FALSE; @@ -288,11 +548,6 @@ BOOL get_is_item_worn(const LLInventoryItem *item) return FALSE; } -BOOL get_is_item_worn(const LLUUID& id) -{ - return get_is_item_worn(gInventory.getItem(id)); -} - BOOL get_can_item_be_worn(const LLUUID& id) { const LLViewerInventoryItem* item = gInventory.getItem(id); @@ -503,174 +758,1007 @@ void show_item_profile(const LLUUID& item_uuid) } } -void open_outbox() +void open_marketplace_listings() { - LLFloaterOutbox::showInstance(); + LLFloaterMarketplaceListings::showInstance(); } -LLUUID create_folder_in_outbox_for_item(LLInventoryItem* item, const LLUUID& destFolderId, S32 operation_id) +///---------------------------------------------------------------------------- +// Marketplace functions +// +// Handles Copy and Move to or within the Marketplace listings folder. +// Handles creation of stock folders, nesting of listings and version folders, +// permission checking and listings validation. +///---------------------------------------------------------------------------- + +S32 depth_nesting_in_marketplace(LLUUID cur_uuid) { - llassert(item); - llassert(destFolderId.notNull()); + // Get the marketplace listings root, exit with -1 (i.e. not under the marketplace listings root) if none + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplace_listings_uuid.isNull()) + { + return -1; + } + // If not a descendent of the marketplace listings root, then the nesting depth is -1 by definition + if (!gInventory.isObjectDescendentOf(cur_uuid, marketplace_listings_uuid)) + { + return -1; + } - LLUUID created_folder_id = gInventory.createNewCategory(destFolderId, LLFolderType::FT_NONE, item->getName()); - gInventory.notifyObservers(); - - LLNotificationsUtil::add("OutboxFolderCreated"); - - return created_folder_id; + // Iterate through the parents till we hit the marketplace listings root + // Note that the marketplace listings root itself will return 0 + S32 depth = 0; + LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); + while (cur_uuid != marketplace_listings_uuid) + { + depth++; + cur_uuid = cur_object->getParentUUID(); + cur_object = gInventory.getCategory(cur_uuid); + } + return depth; } -void move_to_outbox_cb_action(const LLSD& payload) +// Returns the UUID of the marketplace listing this object is in +LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth) { - LLViewerInventoryItem * viitem = gInventory.getItem(payload["item_id"].asUUID()); - LLUUID dest_folder_id = payload["dest_folder_id"].asUUID(); + if (depth < 1) + { + // For objects outside the marketplace listings root (or root itself), we return a NULL UUID + return LLUUID::null; + } + else if (depth == 1) + { + // Just under the root, we return the passed UUID itself if it's a folder, NULL otherwise (not a listing) + LLViewerInventoryCategory* cat = gInventory.getCategory(cur_uuid); + return (cat ? cur_uuid : LLUUID::null); + } - if (viitem) - { - // when moving item directly into outbox create folder with that name - if (dest_folder_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) + // depth > 1 + LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); + while (depth > 1) + { + depth--; + cur_uuid = cur_object->getParentUUID(); + cur_object = gInventory.getCategory(cur_uuid); + } + return cur_uuid; +} + +S32 compute_stock_count(LLUUID cat_uuid, bool force_count /* false */) +{ + // Handle the case of the folder being a stock folder immediately + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid); + if (!cat) + { + // Not a category so no stock count to speak of + return COMPUTE_STOCK_INFINITE; + } + if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) { - S32 operation_id = payload["operation_id"].asInteger(); - dest_folder_id = create_folder_in_outbox_for_item(viitem, dest_folder_id, operation_id); + // If the folder is not completely fetched, we do not want to return any confusing value that could lead to unlisting + // "COMPUTE_STOCK_NOT_EVALUATED" denotes that a stock folder has a count that cannot be evaluated at this time (folder not up to date) + return COMPUTE_STOCK_NOT_EVALUATED; + } + // Note: stock folders are *not* supposed to have nested subfolders so we stop recursion here but we count only items (subfolders will be ignored) + // Note: we *always* give a stock count for stock folders, it's useful even if the listing is unassociated + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); + return item_array->size(); + } + + // When force_count is true, we do not do any verification of the marketplace status and simply compute + // the stock amount based on the descendent hierarchy. This is used specifically when creating a listing. + if (!force_count) + { + // Grab marketplace data for this folder + S32 depth = depth_nesting_in_marketplace(cat_uuid); + LLUUID listing_uuid = nested_parent_id(cat_uuid, depth); + if (!LLMarketplaceData::instance().isListed(listing_uuid)) + { + // If not listed, the notion of stock is meaningless so it won't be computed for any level + return COMPUTE_STOCK_INFINITE; } - LLUUID parent = viitem->getParentUUID(); - - gInventory.changeItemParent( - viitem, - dest_folder_id, - false); - - LLUUID top_level_folder = payload["top_level_folder"].asUUID(); - - if (top_level_folder != LLUUID::null) + LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); + // Handle the case of the first 2 levels : listing and version folders + if (depth == 1) { - LLViewerInventoryCategory* category; - - while (parent.notNull()) + if (version_folder_uuid.notNull()) { - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(parent,cat_array,item_array); - - LLUUID next_parent; - - category = gInventory.getCategory(parent); - - if (!category) break; - - next_parent = category->getParentUUID(); - - if (cat_array->empty() && item_array->empty()) - { - gInventory.removeCategory(parent); - } - - if (parent == top_level_folder) - { - break; - } - - parent = next_parent; + // If there is a version folder, the stock value for the listing is the version folder stock + return compute_stock_count(version_folder_uuid, true); + } + else + { + // If there's no version folder associated, the notion of stock count has no meaning + return COMPUTE_STOCK_INFINITE; } } - - open_outbox(); - } -} - -void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LLUUID& top_level_folder, S32 operation_id) -{ - // Collapse links directly to items/folders - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; - LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); - if (linked_category != NULL) - { - copy_folder_to_outbox(linked_category, dest_folder, top_level_folder, operation_id); - } - else - { - LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); - if (linked_item != NULL) + else if (depth == 2) { - inv_item = (LLInventoryItem *) linked_item; - } - - // Check for copy permissions - if (inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - // when moving item directly into outbox create folder with that name - if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) + if (version_folder_uuid.notNull() && (version_folder_uuid != cat_uuid)) { - dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id); + // If there is a version folder but we're not it, our stock count is meaningless + return COMPUTE_STOCK_INFINITE; } - - copy_inventory_item(gAgent.getID(), - inv_item->getPermissions().getOwner(), - inv_item->getUUID(), - dest_folder, - inv_item->getName(), - LLPointer(NULL)); - - open_outbox(); - } - else - { - LLSD payload; - payload["item_id"] = inv_item->getUUID(); - payload["dest_folder_id"] = dest_folder; - payload["top_level_folder"] = top_level_folder; - payload["operation_id"] = operation_id; - - LLMarketplaceInventoryNotifications::addNoCopyNotification(payload, move_to_outbox_cb_action); } } -} - -void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32 operation_id) -{ - // when moving item directly into outbox create folder with that name - if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) - { - dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id); - } - - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; - - gInventory.changeItemParent( - viewer_inv_item, - dest_folder, - false); -} - -void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, const LLUUID& top_level_folder, S32 operation_id) -{ - LLUUID new_folder_id = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, inv_cat->getName()); - gInventory.notifyObservers(); + // In all other cases, the stock count is the min of stock folders count found in the descendents + // "COMPUTE_STOCK_NOT_EVALUATED" denotes that a stock folder in the hierarchy has a count that cannot be evaluated at this time (folder not up to date) LLInventoryModel::cat_array_t* cat_array; LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(inv_cat->getUUID(),cat_array,item_array); + gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); + + // "COMPUTE_STOCK_INFINITE" denotes a folder that doesn't contain any stock folders in its descendents + S32 curr_count = COMPUTE_STOCK_INFINITE; + + // Note: marketplace listings have a maximum depth nesting of 4 + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLInventoryCategory* category = *iter; + S32 count = compute_stock_count(category->getUUID(), true); + if ((curr_count == COMPUTE_STOCK_INFINITE) || ((count != COMPUTE_STOCK_INFINITE) && (count < curr_count))) + { + curr_count = count; + } + } + + return curr_count; +} + +// local helper +bool can_move_to_marketplace(LLInventoryItem* inv_item, std::string& tooltip_msg, bool resolve_links) +{ + // Collapse links directly to items/folders + LLViewerInventoryItem* viewer_inv_item = (LLViewerInventoryItem *) inv_item; + LLViewerInventoryItem* linked_item = viewer_inv_item->getLinkedItem(); + LLViewerInventoryCategory* linked_category = viewer_inv_item->getLinkedCategory(); + + // Linked items and folders cannot be put for sale + if (linked_category || linked_item) + { + tooltip_msg = LLTrans::getString("TooltipOutboxLinked"); + return false; + } + + // A category is always considered as passing... + if (linked_category != NULL) + { + return true; + } + + // Take the linked item if necessary + if (linked_item != NULL) + { + inv_item = linked_item; + } + + // Check that the agent has transfer permission on the item: this is required as a resident cannot + // put on sale items she cannot transfer. Proceed with move if we have permission. + bool allow_transfer = inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); + if (!allow_transfer) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNoTransfer"); + return false; + } + + // Check worn/not worn status: worn items cannot be put on the marketplace + bool worn = get_is_item_worn(inv_item->getUUID()); + if (worn) + { + tooltip_msg = LLTrans::getString("TooltipOutboxWorn"); + return false; + } + + // Check library status: library items cannot be put on the marketplace + if (!gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.getRootFolderID())) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + return false; + } + + // Check type: for the moment, calling cards cannot be put on the marketplace + bool calling_card = (LLAssetType::AT_CALLINGCARD == inv_item->getType()); + if (calling_card) + { + tooltip_msg = LLTrans::getString("TooltipOutboxCallingCard"); + return false; + } + + return true; +} + +// local helper +// Returns the max tree length (in folder nodes) down from the argument folder +int get_folder_levels(LLInventoryCategory* inv_cat) +{ + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items); + + int max_child_levels = 0; + + for (U32 i = 0; i < cats->size(); ++i) + { + LLInventoryCategory* category = cats->at(i); + max_child_levels = llmax(max_child_levels, get_folder_levels(category)); + } + + return 1 + max_child_levels; +} + +// local helper +// Returns the distance (in folder nodes) between the ancestor and its descendant. Returns -1 if not related. +int get_folder_path_length(const LLUUID& ancestor_id, const LLUUID& descendant_id) +{ + int depth = 0; + + if (ancestor_id == descendant_id) return depth; + + const LLInventoryCategory* category = gInventory.getCategory(descendant_id); + + while (category) + { + LLUUID parent_id = category->getParentUUID(); + + if (parent_id.isNull()) break; + + depth++; + + if (parent_id == ancestor_id) return depth; + + category = gInventory.getCategory(parent_id); + } + + LL_WARNS("SLM") << "get_folder_path_length() couldn't trace a path from the descendant to the ancestor" << LL_ENDL; + return -1; +} + +// local helper +// Returns true if all items within the argument folder are fit for sale, false otherwise +bool has_correct_permissions_for_sale(LLInventoryCategory* cat, std::string& error_msg) +{ + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); - // copy the vector because otherwise the iterator won't be happy if we delete from it LLInventoryModel::item_array_t item_array_copy = *item_array; for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) { LLInventoryItem* item = *iter; - copy_item_to_outbox(item, new_folder_id, top_level_folder, operation_id); + if (!can_move_to_marketplace(item, error_msg, false)) + { + return false; + } } LLInventoryModel::cat_array_t cat_array_copy = *cat_array; for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) { - LLViewerInventoryCategory* category = *iter; - copy_folder_to_outbox(category, new_folder_id, top_level_folder, operation_id); + LLInventoryCategory* category = *iter; + if (!has_correct_permissions_for_sale(category, error_msg)) + { + return false; + } + } + return true; +} + +// Returns true if inv_item can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root +// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). +// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. +bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size, bool from_paste) +{ + // Check stock folder type matches item type in marketplace listings (even if of no use there for the moment) + LLViewerInventoryCategory* view_folder = dynamic_cast(dest_folder); + bool move_in_stock = (view_folder && (view_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)); + bool accept = (view_folder && view_folder->acceptItem(inv_item)); + if (!accept) + { + tooltip_msg = LLTrans::getString("TooltipOutboxMixedStock"); } - open_outbox(); + // Check that the item has the right type and permissions to be sold on the marketplace + if (accept) + { + accept = can_move_to_marketplace(inv_item, tooltip_msg, true); + } + + // Check that the total amount of items won't violate the max limit on the marketplace + if (accept) + { + // If the dest folder is a stock folder, we do not count the incoming items toward the total (stock items are seen as one) + S32 existing_item_count = (move_in_stock ? 0 : bundle_size); + + // If the dest folder is a stock folder, we do assume that the incoming items are also stock items (they should anyway) + S32 existing_stock_count = (move_in_stock ? bundle_size : 0); + + S32 existing_folder_count = 0; + + // Get the version folder: that's where the counts start from + const LLViewerInventoryCategory* version_folder = ((root_folder && (root_folder != dest_folder)) ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); + + if (version_folder) + { + if (!from_paste && gInventory.isObjectDescendentOf(inv_item->getUUID(), version_folder->getUUID())) + { + // Clear those counts or they will be counted twice because we're already inside the version category + existing_item_count = 0; + } + + LLInventoryModel::cat_array_t existing_categories; + LLInventoryModel::item_array_t existing_items; + + gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, FALSE); + + existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); + existing_stock_count += count_stock_items(existing_items); + existing_folder_count += existing_categories.size(); + + // If the incoming item is a nocopy (stock) item, we need to consider that it will create a stock folder + if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && !move_in_stock) + { + // Note : we do not assume that all incoming items are nocopy of different kinds... + existing_folder_count += 1; + } + } + + if (existing_item_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); + accept = false; + } + else if (existing_stock_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxStockItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxStockItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems", args); + accept = false; + } + else if (existing_folder_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders", args); + accept = false; + } + } + + return accept; +} + +// Returns true if inv_cat can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root +// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). +// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. +bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size, bool check_items, bool from_paste) +{ + bool accept = true; + + // Compute the nested folders level we'll add into with that incoming folder + int incoming_folder_depth = get_folder_levels(inv_cat); + // Compute the nested folders level we're inserting ourselves in + // Note: add 1 when inserting under a listing folder as we need to take the root listing folder in the count + int insertion_point_folder_depth = (root_folder ? get_folder_path_length(root_folder->getUUID(), dest_folder->getUUID()) + 1 : 1); + + // Get the version folder: that's where the folders and items counts start from + const LLViewerInventoryCategory* version_folder = (insertion_point_folder_depth >= 2 ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); + + // Compare the whole with the nested folders depth limit + // Note: substract 2 as we leave root and version folder out of the count threshold + if ((incoming_folder_depth + insertion_point_folder_depth - 2) > (S32)(gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"))) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxFolderLevels", args); + accept = false; + } + + if (accept) + { + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + gInventory.collectDescendents(inv_cat->getUUID(), descendent_categories, descendent_items, FALSE); + + S32 dragged_folder_count = descendent_categories.size() + bundle_size; // Note: We assume that we're moving a bunch of folders in. That might be wrong... + S32 dragged_item_count = count_copyable_items(descendent_items) + count_stock_folders(descendent_categories); + S32 dragged_stock_count = count_stock_items(descendent_items); + S32 existing_item_count = 0; + S32 existing_stock_count = 0; + S32 existing_folder_count = 0; + + if (version_folder) + { + if (!from_paste && gInventory.isObjectDescendentOf(inv_cat->getUUID(), version_folder->getUUID())) + { + // Clear those counts or they will be counted twice because we're already inside the version category + dragged_folder_count = 0; + dragged_item_count = 0; + dragged_stock_count = 0; + } + + // Tally the total number of categories and items inside the root folder + LLInventoryModel::cat_array_t existing_categories; + LLInventoryModel::item_array_t existing_items; + gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, FALSE); + + existing_folder_count += existing_categories.size(); + existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); + existing_stock_count += count_stock_items(existing_items); + } + + const S32 total_folder_count = existing_folder_count + dragged_folder_count; + const S32 total_item_count = existing_item_count + dragged_item_count; + const S32 total_stock_count = existing_stock_count + dragged_stock_count; + + if (total_folder_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders", args); + accept = false; + } + else if (total_item_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); + accept = false; + } + else if (total_stock_count > (S32)gSavedSettings.getU32("InventoryOutboxMaxStockItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxStockItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems", args); + accept = false; + } + + // Now check that each item in the folder can be moved in the marketplace + if (accept && check_items) + { + for (U32 i=0; i < descendent_items.size(); ++i) + { + LLInventoryItem* item = descendent_items[i]; + if (!can_move_to_marketplace(item, tooltip_msg, false)) + { + accept = false; + break; + } + } + } + } + + return accept; +} + +bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy) +{ + // Get the marketplace listings depth of the destination folder, exit with error if not under marketplace + S32 depth = depth_nesting_in_marketplace(dest_folder); + if (depth < 0) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Merchant"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + // We will collapse links into items/folders + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; + LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); + + if (linked_category != NULL) + { + // Move the linked folder directly + return move_folder_to_marketplacelistings(linked_category, dest_folder, copy); + } + else + { + // Grab the linked item if any + LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); + viewer_inv_item = (linked_item != NULL ? linked_item : viewer_inv_item); + + // If we want to copy but the item is no copy, fail silently (this is a common case that doesn't warrant notification) + if (copy && !viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + return false; + } + + // Check that the agent has transfer permission on the item: this is required as a resident cannot + // put on sale items she cannot transfer. Proceed with move if we have permission. + std::string error_msg; + if (can_move_to_marketplace(inv_item, error_msg, true)) + { + // When moving an isolated item, we might need to create the folder structure to support it + if (depth == 0) + { + // We need a listing folder + dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); + depth++; + } + if (depth == 1) + { + // We need a version folder + dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); + depth++; + } + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); + if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && + (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) + { + // We need to create a stock folder to move a no copy item + dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName()); + dest_cat = gInventory.getCategory(dest_folder); + depth++; + } + + // Verify we can have this item in that destination category + if (!dest_cat->acceptItem(viewer_inv_item)) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + // Get the parent folder of the moved item : we may have to update it + LLUUID src_folder = viewer_inv_item->getParentUUID(); + + if (copy) + { + // Copy the item + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder)); + copy_inventory_item( + gAgent.getID(), + viewer_inv_item->getPermissions().getOwner(), + viewer_inv_item->getUUID(), + dest_folder, + std::string(), + cb); + } + else + { + // Reparent the item + gInventory.changeItemParent(viewer_inv_item, dest_folder, true); + } + } + else + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + } + + open_marketplace_listings(); + return true; +} + +bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy, bool move_no_copy_items) +{ + // Check that we have adequate permission on all items being moved. Proceed if we do. + std::string error_msg; + if (has_correct_permissions_for_sale(inv_cat, error_msg)) + { + // Get the destination folder + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); + + // Check it's not a stock folder + if (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + // Get the parent folder of the moved item : we may have to update it + LLUUID src_folder = inv_cat->getParentUUID(); + + LLViewerInventoryCategory* viewer_inv_cat = (LLViewerInventoryCategory*) inv_cat; + if (copy) + { + // Copy the folder + copy_inventory_category(&gInventory, viewer_inv_cat, dest_folder, LLUUID::null, move_no_copy_items); + } + else + { + // Reparent the folder + gInventory.changeCategoryParent(viewer_inv_cat, dest_folder, false); + // Check the destination folder recursively for no copy items and promote the including folders if any + validate_marketplacelistings(dest_cat); + } + + // Update the modified folders + update_marketplace_category(src_folder); + update_marketplace_category(dest_folder); + gInventory.notifyObservers(); + } + else + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + open_marketplace_listings(); + return true; +} + +bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCategory* cat2) +{ + return cat1->getName().compare(cat2->getName()) < 0; +} + +void dump_trace(std::string& message, S32 depth, LLError::ELevel log_level) +{ + LL_INFOS() << "validate_marketplacelistings : error = "<< log_level << ", depth = " << depth << ", message = " << message << LL_ENDL; +} + +// Make all relevant business logic checks on the marketplace listings starting with the folder as argument. +// This function does no deletion of listings but a mere audit and raises issues to the user (through the +// optional callback cb). It also returns a boolean, true if things validate, false if issues are raised. +// The only inventory changes that are done is to move and sort folders containing no-copy items to stock folders. +bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_t cb, bool fix_hierarchy, S32 depth) +{ +#if 0 + // Used only for debug + if (!cb) + { + cb = boost::bind(&dump_trace, _1, _2, _3); + } +#endif + // Folder is valid unless issue is raised + bool result = true; + + // Get the type and the depth of the folder + LLViewerInventoryCategory* viewer_cat = (LLViewerInventoryCategory*) (cat); + const LLFolderType::EType folder_type = cat->getPreferredType(); + if (depth < 0) + { + // If the depth argument was not provided, evaluate the depth directly + depth = depth_nesting_in_marketplace(cat->getUUID()); + } + if (depth < 0) + { + // If the folder is not under the marketplace listings root, we run validation as if it was a listing folder and prevent any hierarchy fix + // This allows the function to be used to pre-validate a folder anywhere in the inventory + depth = 1; + fix_hierarchy = false; + } + + // Set the indentation for print output (typically, audit button in marketplace folder floater) + std::string indent; + for (int i = 1; i < depth; i++) + { + indent += " "; + } + + // Check out that version folders are marketplace ready + if (depth == 2) + { + std::string message; + // Note: if we fix the hierarchy, we want to check the items individually, hence the last argument here + if (!can_move_folder_to_marketplace(cat, cat, cat, message, 0, fix_hierarchy)) + { + result = false; + if (cb) + { + message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + message; + cb(message, depth, LLError::LEVEL_ERROR); + } + } + } + + // Check out that stock folders are at the right level + if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth <= 2)) + { + if (fix_hierarchy) + { + if (cb) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); + cb(message, depth, LLError::LEVEL_WARN); + } + // Nest the stock folder one level deeper in a normal folder and restart from there + LLUUID parent_uuid = cat->getParentUUID(); + LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, LLFolderType::FT_NONE, cat->getName()); + LLInventoryCategory* new_cat = gInventory.getCategory(folder_uuid); + gInventory.changeCategoryParent(viewer_cat, folder_uuid, false); + result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy, depth + 1); + return result; + } + else + { + result = false; + if (cb) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); + cb(message, depth, LLError::LEVEL_ERROR); + } + } + } + + // Item sorting and validation : sorting and moving the various stock items is complicated as the set of constraints is high + // We need to: + // * separate non stock items, stock items per types in different folders + // * have stock items nested at depth 2 at least + // * never ever move the non-stock items + + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + + // We use a composite (type,permission) key on that map to store UUIDs of items of same (type,permissions) + std::map > items_vector; + + // Parse the items and create vectors of item UUIDs sorting copyable items and stock items of various types + bool has_bad_items = false; + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; + + // Test but skip items that shouldn't be there to start with, raise an error message for those + std::string error_msg; + if (!can_move_to_marketplace(item, error_msg, false)) + { + has_bad_items = true; + if (cb && fix_hierarchy) + { + std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; + cb(message, depth, LLError::LEVEL_ERROR); + } + continue; + } + // Update the appropriate vector item for that type + LLInventoryType::EType type = LLInventoryType::IT_COUNT; // Default value for non stock items + U32 perms = 0; + if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + // Get the item type for stock items + type = viewer_inv_item->getInventoryType(); + perms = viewer_inv_item->getPermissions().getMaskNextOwner(); + } + U32 key = (((U32)(type) & 0xFF) << 24) | (perms & 0xFFFFFF); + items_vector[key].push_back(viewer_inv_item->getUUID()); + } + + // How many types of items? Which type is it if only one? + S32 count = items_vector.size(); + U32 default_key = (U32)(LLInventoryType::IT_COUNT) << 24; // This is the key for any normal copyable item + U32 unique_key = (count == 1 ? items_vector.begin()->first : default_key); // The key in the case of one item type only + + // If we have no items in there (only folders or empty), analyze a bit further + if ((count == 0) && !has_bad_items) + { + if (cat_array->size() == 0) + { + // So we have no item and no folder. That's at least a warning. + if (depth == 2) + { + // If this is an empty version folder, warn only (listing won't be delivered by AIS, but only AIS should unlist) + if (cb) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Version"); + cb(message, depth, LLError::LEVEL_WARN); + } + } + else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2)) + { + // If this is a legit but empty stock folder, warn only (listing must stay searchable when out of stock) + if (cb) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Stock"); + cb(message, depth, LLError::LEVEL_WARN); + } + } + else if (cb) + { + // We warn if there's nothing in a regular folder (maybe it's an under construction listing) + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Empty"); + cb(message, depth, LLError::LEVEL_WARN); + } + } + else + { + // Done with that folder : Print out the folder name unless we already found an error here + if (cb && result && (depth >= 1)) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); + cb(message, depth, LLError::LEVEL_INFO); + } + } + } + // If we have a single type of items of the right type in the right place, we're done + else if ((count == 1) && !has_bad_items && (((unique_key == default_key) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2) && (cat_array->size() == 0)))) + { + // Done with that folder : Print out the folder name unless we already found an error here + if (cb && result && (depth >= 1)) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); + cb(message, depth, LLError::LEVEL_INFO); + } + } + else + { + if (fix_hierarchy && !has_bad_items) + { + // Alert the user when an existing stock folder has to be split + if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && ((count >= 2) || (cat_array->size() > 0))) + { + LLNotificationsUtil::add("AlertMerchantStockFolderSplit"); + } + // If we have more than 1 type of items or we are at the listing level or we have stock/no stock type mismatch, wrap the items in subfolders + if ((count > 1) || (depth == 1) || + ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (unique_key == default_key)) || + ((folder_type != LLFolderType::FT_MARKETPLACE_STOCK) && (unique_key != default_key))) + { + // Create one folder per vector at the right depth and of the right type + std::map >::iterator items_vector_it = items_vector.begin(); + while (items_vector_it != items_vector.end()) + { + // Create a new folder + LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); + LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); + std::string folder_name = (depth >= 1 ? viewer_cat->getName() : viewer_inv_item->getName()); + LLFolderType::EType new_folder_type = (items_vector_it->first == default_key ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); + if (cb) + { + std::string message = ""; + if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK) + { + message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Stock"); + } + else + { + message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Version"); + } + cb(message, depth, LLError::LEVEL_WARN); + } + LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, new_folder_type, folder_name); + + // Move each item to the new folder + while (!items_vector_it->second.empty()) + { + LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); + if (cb) + { + std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); + cb(message, depth, LLError::LEVEL_WARN); + } + gInventory.changeItemParent(viewer_inv_item, folder_uuid, true); + items_vector_it->second.pop_back(); + } + + // Next type + update_marketplace_category(parent_uuid); + update_marketplace_category(folder_uuid); + gInventory.notifyObservers(); + items_vector_it++; + } + } + // Stock folder should have no sub folder so reparent those up + if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK) + { + LLUUID parent_uuid = cat->getParentUUID(); + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLViewerInventoryCategory* viewer_cat = (LLViewerInventoryCategory*) (*iter); + gInventory.changeCategoryParent(viewer_cat, parent_uuid, false); + result &= validate_marketplacelistings(viewer_cat, cb, fix_hierarchy, depth); + } + } + } + else if (cb) + { + // We are not fixing the hierarchy but reporting problems, report everything we can find + // Print the folder name + if (result && (depth >= 1)) + { + if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (count >= 2)) + { + // Report if a stock folder contains a mix of items + result = false; + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Mixed Stock"); + cb(message, depth, LLError::LEVEL_ERROR); + } + else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (cat_array->size() != 0)) + { + // Report if a stock folder contains subfolders + result = false; + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Subfolder In Stock"); + cb(message, depth, LLError::LEVEL_ERROR); + } + else + { + // Simply print the folder name + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); + cb(message, depth, LLError::LEVEL_INFO); + + } + } + // Scan each item and report if there's a problem + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + LLViewerInventoryItem* viewer_inv_item = (LLViewerInventoryItem*) item; + std::string error_msg; + if (!can_move_to_marketplace(item, error_msg, false)) + { + // Report items that shouldn't be there to start with + result = false; + std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; + cb(message, depth, LLError::LEVEL_ERROR); + } + else if ((!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) && (folder_type != LLFolderType::FT_MARKETPLACE_STOCK)) + { + // Report stock items that are misplaced + result = false; + std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error Stock Item"); + cb(message, depth, LLError::LEVEL_ERROR); + } + else if (depth == 1) + { + // Report items not wrapped in version folder + result = false; + std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Unwrapped Item"); + cb(message, depth, LLError::LEVEL_ERROR); + } + } + } + + // Clean up + if (viewer_cat->getDescendentCount() == 0) + { + // Remove the current folder if it ends up empty + if (cb) + { + std::string message = indent + viewer_cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); + cb(message, depth, LLError::LEVEL_WARN); + } + gInventory.removeCategory(cat->getUUID()); + gInventory.notifyObservers(); + return result && !has_bad_items; + } + } + + // Recursion : Perform the same validation on each nested folder + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + // Sort the folders in alphabetical order first + std::sort(cat_array_copy.begin(), cat_array_copy.end(), sort_alpha); + + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLInventoryCategory* category = *iter; + result &= validate_marketplacelistings(category, cb, fix_hierarchy, depth + 1); + } + + update_marketplace_category(cat->getUUID()); + gInventory.notifyObservers(); + return result && !has_bad_items; } ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index e986eabda..f4c374108 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -32,6 +32,10 @@ #include "llinventory.h" #include "llwearabletype.h" +// compute_stock_count() return error code +const S32 COMPUTE_STOCK_INFINITE = -1; +const S32 COMPUTE_STOCK_NOT_EVALUATED = -2; + /******************************************************************************** ** ** ** MISCELLANEOUS GLOBAL FUNCTIONS @@ -41,7 +45,6 @@ BOOL get_is_parent_to_worn_item(const LLUUID& id); // Is this item or its baseitem is worn, attached, etc... -BOOL get_is_item_worn(const LLInventoryItem *item); BOOL get_is_item_worn(const LLUUID& id); // Could this item be worn (correct type + not already being worn) @@ -55,20 +58,28 @@ BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) void show_item_profile(const LLUUID& item_uuid); +// Nudge the listing categories in the inventory to signal that their marketplace status changed +void update_marketplace_category(const LLUUID& cat_id, bool perform_consistency_enforcement = true); +// Nudge all listing categories to signal that their marketplace status changed +void update_all_marketplace_count(); + void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); -void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null); +void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null, bool move_no_copy_items = false); // Generates a string containing the path to the item specified by item_id. void append_path(const LLUUID& id, std::string& path); -// Same as append_path but omits the root prefix "/My Inventory/". -void append_path_short(const LLUUID& id, std::string& path); +typedef boost::function validation_callback_t; -void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LLUUID& top_level_folder, S32 operation_id); -void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32 operation_id); - -void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, const LLUUID& top_level_folder, S32 operation_id); +bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size = 1, bool from_paste = false); +bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size = 1, bool check_items = true, bool from_paste = false); +bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy = false); +bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false, bool move_no_copy_items = false); +bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true, S32 depth = -1); +S32 depth_nesting_in_marketplace(LLUUID cur_uuid); +LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth); +S32 compute_stock_count(LLUUID cat_uuid, bool force_count = false); /** Miscellaneous global functions ** ** diff --git a/indra/newview/llinventoryicon.cpp b/indra/newview/llinventoryicon.cpp index f93dc8dd7..f2739df69 100644 --- a/indra/newview/llinventoryicon.cpp +++ b/indra/newview/llinventoryicon.cpp @@ -88,8 +88,14 @@ LLIconDictionary::LLIconDictionary() addEntry(LLInventoryType::ICONNAME_LINKFOLDER, new IconEntry("inv_link_folder.tga")); addEntry(LLInventoryType::ICONNAME_MESH, new IconEntry("inv_item_mesh.tga")); + addEntry(LLInventoryType::ICONNAME_SETTINGS_SKY, new IconEntry("Inv_SettingsSky.png")); + addEntry(LLInventoryType::ICONNAME_SETTINGS_WATER, new IconEntry("Inv_SettingsWater.png")); + addEntry(LLInventoryType::ICONNAME_SETTINGS_DAY, new IconEntry("Inv_SettingsDay.png")); + addEntry(LLInventoryType::ICONNAME_SETTINGS, new IconEntry("Inv_Settings.png")); + addEntry(LLInventoryType::ICONNAME_CLOTHING_UNKNOWN, new IconEntry("inv_item_unknown.tga")); addEntry(LLInventoryType::ICONNAME_INVALID, new IconEntry("inv_invalid.png")); + addEntry(LLInventoryType::ICONNAME_UNKNOWN, new IconEntry("Inv_UnknownObject.png")); addEntry(LLInventoryType::ICONNAME_NONE, new IconEntry("NONE")); } @@ -163,6 +169,14 @@ const std::string& LLInventoryIcon::getIconName(LLAssetType::EType asset_type, break; case LLAssetType::AT_MESH: idx = LLInventoryType::ICONNAME_MESH; + break; + case LLAssetType::AT_SETTINGS: + // TODO: distinguish between Sky and Water settings. + idx = assignSettingsIcon(misc_flag); + break; + case LLAssetType::AT_UNKNOWN: + idx = LLInventoryType::ICONNAME_UNKNOWN; + break; default: break; } @@ -182,3 +196,14 @@ LLInventoryType::EIconName LLInventoryIcon::assignWearableIcon(U32 misc_flag) const LLWearableType::EType wearable_type = LLWearableType::EType(LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK & misc_flag); return LLWearableType::getIconName(wearable_type); } + +LLInventoryType::EIconName LLInventoryIcon::assignSettingsIcon(U32 misc_flag) +{ + switch (misc_flag & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK) + { + case 0: return LLInventoryType::ICONNAME_SETTINGS_SKY; + case 1: return LLInventoryType::ICONNAME_SETTINGS_WATER; + case 2: return LLInventoryType::ICONNAME_SETTINGS_DAY; + } + return LLInventoryType::ICONNAME_SETTINGS; +} diff --git a/indra/newview/llinventoryicon.h b/indra/newview/llinventoryicon.h index 659448143..58e1ffdb6 100644 --- a/indra/newview/llinventoryicon.h +++ b/indra/newview/llinventoryicon.h @@ -49,6 +49,7 @@ public: protected: static LLInventoryType::EIconName assignWearableIcon(U32 misc_flag); + static LLInventoryType::EIconName assignSettingsIcon(U32 misc_flag); }; #endif // LL_LLINVENTORYICON_H diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 25bd0321e..30a3ca092 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -25,6 +25,9 @@ */ #include "llviewerprecompiledheaders.h" + +#include + #include "llinventorymodel.h" #include "llaisapi.h" @@ -38,6 +41,7 @@ #include "llinventoryobserver.h" #include "llinventorypanel.h" #include "llnotificationsutil.h" +#include "llmarketplacefunctions.h" #include "llwindow.h" #include "llviewercontrol.h" #include "llpreview.h" @@ -50,7 +54,6 @@ #include "llvoavatarself.h" #include "llgesturemgr.h" #include "llsdutil.h" -#include #include "statemachine/aievent.h" // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a) @@ -151,6 +154,7 @@ LLInventoryModel::LLInventoryModel() mItemLock() {} + // Destroys the object LLInventoryModel::~LLInventoryModel() { @@ -561,12 +565,13 @@ public: { } - /*virtual*/ void httpFailure(void) +protected: + virtual void httpFailure() { - LL_WARNS(LOG_INV) << "CreateInventoryCategory failed. status = " << mStatus << ", reason = \"" << mReason << "\"" << LL_ENDL; + LL_WARNS(LOG_INV) << dumpResponse() << LL_ENDL; } - /*virtual*/ void httpSuccess(void) + virtual void httpSuccess() { //Server has created folder. const LLSD& content = getContent(); @@ -595,7 +600,6 @@ public: { mCallback.get()(category_id); } - } /*virtual*/ char const* getName(void) const { return "LLCreateInventoryCategoryResponder"; } @@ -624,7 +628,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup()) { - LL_DEBUGS(LOG_INV) << "Attempt to create undefined category. (" << preferred_type << ")" << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL; return id; } @@ -1132,7 +1136,8 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 LLPointer old_cat = getCategory(cat->getUUID()); if(old_cat) { - // We already have an old category, modify it's values + // We already have an old category, modify its values + U32 mask = LLInventoryObserver::NONE; LLUUID old_parent_id = old_cat->getParentUUID(); LLUUID new_parent_id = cat->getParentUUID(); if(old_parent_id != new_parent_id) @@ -1156,6 +1161,12 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 { mask |= LLInventoryObserver::LABEL; } + // Under marketplace, category labels are quite complex and need extra upate + const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplace_id.notNull() && isObjectDescendentOf(cat->getUUID(), marketplace_id)) + { + mask |= LLInventoryObserver::LABEL; + } old_cat->copyViewerCategory(cat); addChangedMask(mask, cat->getUUID()); } @@ -1292,7 +1303,8 @@ void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat, void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLSD& update) { LLTimer timer; - if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) + static LLCachedControl debug_ava_appr_msg(gSavedSettings, "DebugAvatarAppearanceMessage"); + if (debug_ava_appr_msg) { dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update); } @@ -1509,6 +1521,11 @@ void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, boo LLPointer cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); vector_replace_with_last(*cat_list, cat); } + + // Note : We need to tell the inventory observers that those things are going to be deleted *before* the tree is cleared or they won't know what to delete (in views and view models) + addChangedMask(LLInventoryObserver::REMOVE, id); + gInventory.notifyObservers(); + item_list = getUnlockedItemArray(id); if(item_list) { @@ -1540,11 +1557,11 @@ void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, boo // Can't have links to links, so there's no need for this update // if the item removed is a link. Can also skip if source of the // update is getting broken link info separately. - obj = NULL; // delete obj if (fix_broken_links && !is_link_type) { updateLinkedObjectsFromPurge(id); } + obj = NULL; // delete obj if (do_notify_observers) { notifyObservers(); @@ -1654,9 +1671,10 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) } mModifyMask |= mask; - if (referent.notNull()) + if (referent.notNull() && (mChangedItemIDs.find(referent) == mChangedItemIDs.end())) { mChangedItemIDs.insert(referent); + update_marketplace_category(referent, false); if (mask & LLInventoryObserver::ADD) { @@ -1840,7 +1858,7 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) // Empty the entire contents void LLInventoryModel::empty() { -// LL_INFOS() << "LLInventoryModel::empty()" << LL_ENDL; +// LL_INFOS(LOG_INV) << "LLInventoryModel::empty()" << LL_ENDL; std::for_each( mParentChildCategoryTree.begin(), mParentChildCategoryTree.end(), @@ -2080,13 +2098,18 @@ bool LLInventoryModel::loadSkeleton( { continue; } - if (cat->getVersion() != tcat->getVersion()) + else if (cat->getVersion() != tcat->getVersion()) { // if the cached version does not match the server version, // throw away the version we have so we can fetch the // correct contents the next time the viewer opens the folder. tcat->setVersion(NO_VERSION); } + else if (tcat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + // Do not trust stock folders being updated + tcat->setVersion(NO_VERSION); + } else { cached_ids.insert(tcat->getUUID()); @@ -2162,7 +2185,7 @@ bool LLInventoryModel::loadSkeleton( { bad_link_count++; invalid_categories.insert(cit->second); - //LL_INFOS() << "link still broken: " << item->getName() << " in folder " << cat->getName() << LL_ENDL; + //LL_INFOS(LOG_INV) << "link still broken: " << item->getName() << " in folder " << cat->getName() << LL_ENDL; } else { @@ -2344,22 +2367,24 @@ void LLInventoryModel::buildParentChildMap() // plop it into the lost & found. LLFolderType::EType pref = cat->getPreferredType(); if(LLFolderType::FT_NONE == pref) - { - cat->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); - } - else if(LLFolderType::FT_ROOT_INVENTORY == pref) - { - // it's the root - cat->setParent(LLUUID::null); - } - else - { - // it's a protected folder. - cat->setParent(gInventory.getRootFolderID()); - } - cat->updateServer(TRUE); - catsp = getUnlockedCatArray(cat->getParentUUID()); - if(catsp) + { + cat->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); + } + else if(LLFolderType::FT_ROOT_INVENTORY == pref) + { + // it's the root + cat->setParent(LLUUID::null); + } + else + { + // it's a protected folder. + cat->setParent(gInventory.getRootFolderID()); + } + // FIXME note that updateServer() fails with protected + // types, so this will not work as intended in that case. + cat->updateServer(TRUE); + catsp = getUnlockedCatArray(cat->getParentUUID()); + if(catsp) { catsp->push_back(cat); } @@ -2491,6 +2516,7 @@ void LLInventoryModel::buildParentChildMap() // root of the agent's inv found. // The inv tree is built. mIsAgentInvUsable = true; + AIEvent::trigger(AIEvent::LLInventoryModel_mIsAgentInvUsable_true); // notifyObservers() has been moved to // llstartup/idle_startup() after this func completes. @@ -3077,7 +3103,18 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) LL_DEBUGS("Inventory") << "unpacked folder '" << tfolder->getName() << "' (" << tfolder->getUUID() << ") in " << tfolder->getParentUUID() << LL_ENDL; - if(tfolder->getUUID().notNull()) + + // If the folder is a listing or a version folder, all we need to do is update the SLM data + int depth_folder = depth_nesting_in_marketplace(tfolder->getUUID()); + if ((depth_folder == 1) || (depth_folder == 2)) + { + // Trigger an SLM listing update + LLUUID listing_uuid = (depth_folder == 1 ? tfolder->getUUID() : tfolder->getParentUUID()); + S32 listing_id = LLMarketplaceData::instance().getListingID(listing_uuid); + LLMarketplaceData::instance().getListing(listing_id); + // In that case, there is no item to update so no callback -> we skip the rest of the update + } + else if(tfolder->getUUID().notNull()) { folders.push_back(tfolder); LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID()); @@ -3580,25 +3617,25 @@ void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& item } */ // See also LLInventorySort where landmarks in the Favorites folder are sorted. -/*class LLViewerInventoryItemSort +class LLViewerInventoryItemSort { public: - bool operator()(const LLPointer& a, const LLPointer& b) + bool operator()(const LLPointer& a, const LLPointer& b) const { return a->getSortField() < b->getSortField(); } -};*/ +}; /** * Sorts passed items by LLViewerInventoryItem sort field. * * @param[in, out] items - array of items, not sorted. */ -/*static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) -{ - static LLViewerInventoryItemSort sort_functor; - std::sort(items.begin(), items.end(), sort_functor); -}*/ +//static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) +//{ +// static LLViewerInventoryItemSort sort_functor; +// std::sort(items.begin(), items.end(), sort_functor); +//} // * @param source_item_id - LLUUID of the source item to be moved into new position // * @param target_item_id - LLUUID of the target item before which source item should be placed. @@ -4080,7 +4117,8 @@ void LLInventoryModel::FetchItemHttpHandler::httpSuccess() //If we get back an error (not found, etc...), handle it here void LLInventoryModel::FetchItemHttpHandler::httpFailure() { - LL_INFOS() << "FetchItemHttpHandler::error " + LL_WARNS(LOG_INV) << "FetchItemHttpHandler::error " << mStatus << ": " << mReason << LL_ENDL; gInventory.notifyObservers(); } + diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index b9187ef6f..915dbc296 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -27,7 +27,7 @@ #ifndef LL_LLINVENTORYMODEL_H #define LL_LLINVENTORYMODEL_H -#include +#include #include #include #include @@ -154,13 +154,13 @@ private: // the inventory using several different identifiers. // mInventory member data is the 'master' list of inventory, and // mCategoryMap and mItemMap store uuid->object mappings. - typedef std::map > cat_map_t; - typedef std::map > item_map_t; + typedef std::unordered_map > cat_map_t; + typedef std::unordered_map > item_map_t; cat_map_t mCategoryMap; item_map_t mItemMap; // This last set of indices is used to map parents to children. - typedef std::map parent_cat_map_t; - typedef std::map parent_item_map_t; + typedef std::unordered_map parent_cat_map_t; + typedef std::unordered_map parent_item_map_t; parent_cat_map_t mParentChildCategoryTree; parent_item_map_t mParentChildItemTree; @@ -418,15 +418,6 @@ public: // Returns end() of the vector if not found. static LLInventoryModel::item_array_t::iterator findItemIterByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id); - // Saves current order of the passed items using inventory item sort field. - // Resets 'items' sort fields and saves them on server. - // Is used to save order for Favorites folder. - //void saveItemsOrder(const LLInventoryModel::item_array_t& items); - - // Rearranges Landmarks inside Favorites folder. - // Moves source landmark before target one. - void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); - //-------------------------------------------------------------------- // Creation //-------------------------------------------------------------------- @@ -476,7 +467,7 @@ public: LLInitializedS32& operator++() { ++mValue; return *this; } LLInitializedS32& operator--() { --mValue; return *this; } }; - typedef std::map update_map_t; + typedef std::unordered_map update_map_t; // Call when there are category updates. Call them *before* the // actual update so the method can do descendent accounting correctly. @@ -601,8 +592,8 @@ protected: cat_array_t* getUnlockedCatArray(const LLUUID& id); item_array_t* getUnlockedItemArray(const LLUUID& id); private: - std::map mCategoryLock; - std::map mItemLock; + std::unordered_map mCategoryLock; + std::unordered_map mItemLock; //-------------------------------------------------------------------- // Debugging diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 9e3e0af73..82fc9d391 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -37,6 +37,7 @@ #include "llfolderview.h" #include "llimview.h" #include "llinventorybridge.h" +#include "llinventoryfilter.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" #include "llviewerfoldertype.h" @@ -44,7 +45,6 @@ #include "llscrollcontainer.h" #include "llviewerassettype.h" #include "llpanelmaininventory.h" -#include "llpanelmarketplaceoutboxinventory.h" #include "llsdserialize.h" @@ -72,7 +72,8 @@ class LLInventoryPanelObserver : public LLInventoryObserver public: LLInventoryPanelObserver(LLInventoryPanel* ip) : mIP(ip) {} virtual ~LLInventoryPanelObserver() {} - virtual void changed(U32 mask) + + void changed(U32 mask) override { mIP->modelChanged(mask); } @@ -80,12 +81,6 @@ protected: LLInventoryPanel* mIP; }; - - - - - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInvPanelComplObserver // @@ -102,7 +97,7 @@ protected: class LLInvPanelComplObserver : public LLInventoryCompletionObserver { public: - typedef boost::function callback_t; + typedef std::function callback_t; LLInvPanelComplObserver(callback_t cb) : mCallback(cb) @@ -112,7 +107,7 @@ public: void reset(); private: - /*virtual*/ void done(); + /*virtual*/ void done() override; /// Called when all the items are complete. callback_t mCallback; @@ -129,9 +124,13 @@ void LLInvPanelComplObserver::done() mCallback(); } +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryPanel +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + LLInventoryPanel::LLInventoryPanel(const std::string& name, const std::string& sort_order_setting, - const std::string& start_folder, + const LLSD& start_folder, const LLRect& rect, LLInventoryModel* inventory, BOOL allow_multi_select, @@ -142,6 +141,12 @@ LLInventoryPanel::LLInventoryPanel(const std::string& name, mScroller(NULL), mSortOrderSetting(sort_order_setting), mStartFolder(start_folder), + mShowRootFolder(false), + mShowEmptyMessage(true), + //mShowItemLinkOverlays(false), + mAllowDropOnRoot(true), + mAllowWear(true), + mUseMarketplaceFolders(false), mInventory(inventory), mAllowMultiSelect(allow_multi_select), mViewsInitialized(false), @@ -159,33 +164,12 @@ void LLInventoryPanel::buildFolderView() { // Determine the root folder in case specified, and // build the views starting with that folder. - - //std::string start_folder_name(params.start_folder()); - - const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(mStartFolder); - - LLUUID root_id; - - if ("LIBRARY" == mStartFolder) - { - root_id = gInventory.getLibraryRootFolderID(); - } - else - { - root_id = (preferred_type != LLFolderType::FT_NONE) - ? gInventory.findCategoryUUIDForType(preferred_type, false) - : gInventory.getCategory(static_cast(mStartFolder)) ? static_cast(mStartFolder) // Singu Note: if start folder is an id of a folder, use it - : LLUUID::null; - } - - if ((root_id == LLUUID::null) && !mStartFolder.empty()) - { - LL_WARNS() << "No category found that matches start_folder: " << mStartFolder << LL_ENDL; - root_id = LLUUID::generateNewID(); - } + LLUUID root_id = getRootFolderID(); + if (!mStartFolder.has("id")) + mStartFolder["id"] = root_id; // Cache this, so we don't waste time on future getRootFolderID calls LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, + (mUseMarketplaceFolders/*mParams.use_marketplace_folders*/ ? LLAssetType::AT_MARKETPLACE_FOLDER : LLAssetType::AT_CATEGORY), LLInventoryType::IT_CATEGORY, this, NULL, @@ -193,6 +177,8 @@ void LLInventoryPanel::buildFolderView() LLFolderView* folder_view = createFolderView(new_listener, true/*params.use_label_suffix()*/); mFolderRoot = folder_view->getHandle(); + + addItemID(root_id, mFolderRoot.get()); } BOOL LLInventoryPanel::postBuild() { @@ -225,6 +211,7 @@ BOOL LLInventoryPanel::postBuild() { initializeViews(); } + gIdleCallbacks.addFunction(onIdle, (void*)this); if (mSortOrderSetting != INHERIT_SORT_ORDER) @@ -235,7 +222,25 @@ BOOL LLInventoryPanel::postBuild() { setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER)); } - getFilter()->setFilterEmptySystemFolders(); + + // hide inbox + if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible")) + { + //getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); // Singu Nope! + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_OUTBOX)); + } + // hide marketplace listing box, unless we are a marketplace panel + if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mUseMarketplaceFolders/*mParams.use_marketplace_folders*/) + { + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS)); + } + + // set the filter for the empty folder if the debug setting is on + if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders")) + { + getFilter().setFilterEmptySystemFolders(); + } + return LLPanel::postBuild(); } @@ -296,7 +301,7 @@ class LLInventoryRecentItemsPanel : public LLInventoryPanel public: LLInventoryRecentItemsPanel(const std::string& name, const std::string& sort_order_setting, - const std::string& start_folder, + const LLSD& start_folder, const LLRect& rect, LLInventoryModel* inventory, BOOL allow_multi_select, @@ -306,6 +311,14 @@ public: LLView* LLInventoryPanel::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { + std::string filename; + if (node->getAttributeString("filename", filename) && !filename.empty()) + { + LLXMLNodePtr node; + if (LLUICtrlFactory::getLayeredXMLNode(filename, node)) + return factory->createCtrlWidget(static_cast(parent), node); + } + LLInventoryPanel* panel; std::string name("inventory_panel"); @@ -320,22 +333,32 @@ LLView* LLInventoryPanel::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac std::string sort_order(INHERIT_SORT_ORDER); node->getAttributeString("sort_order", sort_order); - std::string start_folder; - node->getAttributeString("start_folder", start_folder); + LLSD start_folder; + std::string start; + if (node->getAttributeString("start_folder.name", start)) + start_folder["name"] = start; + if (node->getAttributeString("start_folder.id", start)) + start_folder["id"] = LLUUID(start); + if (node->getAttributeString("start_folder.type", start)) + start_folder["type"] = start; - if(name == "Recent Items") + if (name == "Recent Items") panel = new LLInventoryRecentItemsPanel(name, sort_order, start_folder, rect, &gInventory, allow_multi_select, parent); - else if(name == "panel_outbox_inventory") - panel = new LLOutboxInventoryPanel(name, sort_order, start_folder, - rect, &gInventory, - allow_multi_select, parent); else panel = new LLInventoryPanel(name, sort_order, start_folder, rect, &gInventory, allow_multi_select, parent); + // Singu TODO: Turn these into mParams like upstream. + node->getAttribute_bool("show_root_folder", panel->mShowRootFolder); + node->getAttribute_bool("show_empty_message", panel->mShowEmptyMessage); + //node->getAttribute_bool("show_item_link_overlays", panel->mShowItemLinkOverlays); + node->getAttribute_bool("allow_drop_on_root", panel->mAllowDropOnRoot); + node->getAttribute_bool("allow_wear", panel->mAllowWear); + node->getAttribute_bool("use_marketplace_folders", panel->mUseMarketplaceFolders); + panel->initFromXML(node, parent); panel->postBuild(); @@ -346,60 +369,55 @@ LLView* LLInventoryPanel::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac void LLInventoryPanel::draw() { // Select the desired item (in case it wasn't loaded when the selection was requested) - mFolderRoot.get()->updateSelection(); + updateSelection(); + LLPanel::draw(); } -LLInventoryFilter* LLInventoryPanel::getFilter() +LLInventoryFilter& LLInventoryPanel::getFilter() { - if (mFolderRoot.get()) - { - return mFolderRoot.get()->getFilter(); - } - return NULL; + return mFolderRoot.get()->getFilter(); } -const LLInventoryFilter* LLInventoryPanel::getFilter() const +const LLInventoryFilter& LLInventoryPanel::getFilter() const { - if (mFolderRoot.get()) - { - return mFolderRoot.get()->getFilter(); - } - return NULL; + return mFolderRoot.get()->getFilter(); } void LLInventoryPanel::setFilterTypes(U64 types, LLInventoryFilter::EFilterType filter_type) { if (filter_type == LLInventoryFilter::FILTERTYPE_OBJECT) - getFilter()->setFilterObjectTypes(types); + { + getFilter().setFilterObjectTypes(types); + } if (filter_type == LLInventoryFilter::FILTERTYPE_CATEGORY) - getFilter()->setFilterCategoryTypes(types); + getFilter().setFilterCategoryTypes(types); } U32 LLInventoryPanel::getFilterObjectTypes() const { - return mFolderRoot.get()->getFilterObjectTypes(); + return getFilter().getFilterObjectTypes(); } U32 LLInventoryPanel::getFilterPermMask() const { - return mFolderRoot.get()->getFilterPermissions(); + return getFilter().getFilterPermissions(); } void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask) { - getFilter()->setFilterPermissions(filter_perm_mask); + getFilter().setFilterPermissions(filter_perm_mask); } void LLInventoryPanel::setFilterWearableTypes(U64 types) { - getFilter()->setFilterWearableTypes(types); + getFilter().setFilterWearableTypes(types); } -void LLInventoryPanel::setFilterWorn(bool worn) +void LLInventoryPanel::setFilterWornItems() { - getFilter()->setFilterWorn(worn); + getFilter().setFilterWornItems(); } void LLInventoryPanel::setFilterSubString(const std::string& string) @@ -420,7 +438,7 @@ void LLInventoryPanel::setFilterSubString(const std::string& string) if (string.empty()) { // Unlike v3, do not clear other filters. Text is independent. - getFilter()->setFilterSubString(LLStringUtil::null); + getFilter().setFilterSubString(LLStringUtil::null); getRootFolder()->restoreFolderState(); LLOpenFoldersWithSelection opener; getRootFolder()->applyFunctorRecursively(opener); @@ -434,19 +452,19 @@ void LLInventoryPanel::setFilterSubString(const std::string& string) getRootFolder()->saveFolderState(); } // set new filter string - getFilter()->setFilterSubString(string); + getFilter().setFilterSubString(string); } } const std::string LLInventoryPanel::getFilterSubString() { - return mFolderRoot.get()->getFilterSubString(); + return getFilter().getFilterSubString(); } void LLInventoryPanel::setSortOrder(U32 order) { - getFilter()->setSortOrder(order); - if (getFilter()->isModified()) + getFilter().setSortOrder(order); + if (getFilter().isModified()) { mFolderRoot.get()->setSortOrder(order); // try to keep selection onscreen, even if it wasn't to start with @@ -466,29 +484,35 @@ void LLInventoryPanel::requestSort() void LLInventoryPanel::setSinceLogoff(BOOL sl) { - getFilter()->setDateRangeLastLogoff(sl); + getFilter().setDateRangeLastLogoff(sl); } void LLInventoryPanel::setHoursAgo(U32 hours) { - getFilter()->setHoursAgo(hours); + getFilter().setHoursAgo(hours); } -void LLInventoryPanel::setFilterLinks(U64 filter_links) +void LLInventoryPanel::setDateSearchDirection(U32 direction) { - getFilter()->setFilterLinks(filter_links); + getFilter().setDateSearchDirection(direction); +} + +void LLInventoryPanel::setFilterLinks(LLInventoryFilter::EFilterLink filter_links) +{ + getFilter().setFilterLinks(filter_links); } void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show) { - getFilter()->setShowFolderState(show); + getFilter().setShowFolderState(show); } LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState() { - return getFilter()->getShowFolderState(); + return getFilter().getShowFolderState(); } +// Called when something changed in the global model (new item, item coming through the wire, rename, move, etc...) (CHUI-849) void LLInventoryPanel::modelChanged(U32 mask) { static LLTrace::BlockTimerStatHandle FTM_REFRESH("Inventory Refresh"); @@ -508,11 +532,18 @@ void LLInventoryPanel::modelChanged(U32 mask) { const LLUUID& item_id = (*items_iter); const LLInventoryObject* model_item = model->getObject(item_id); - LLFolderViewItem* view_item = mFolderRoot.get()->getItemByID(item_id); + LLFolderViewItem* view_item = getItemByID(item_id); // LLFolderViewFolder is derived from LLFolderViewItem so dynamic_cast from item // to folder is the fast way to get a folder without searching through folders tree. - LLFolderViewFolder* view_folder = dynamic_cast(view_item); + LLFolderViewFolder* view_folder = NULL; + + // Check requires as this item might have already been deleted + // as a child of its deleted parent. + if (model_item && view_item) + { + view_folder = dynamic_cast(view_item); + } ////////////////////////////// // LABEL Operation @@ -552,7 +583,9 @@ void LLInventoryPanel::modelChanged(U32 mask) { if (model_item && view_item) { + const LLUUID& idp = view_item->getListener()->getUUID(); view_item->destroyView(); + removeItemID(idp); } view_item = buildNewViews(item_id); view_folder = dynamic_cast(view_item); @@ -608,18 +641,20 @@ void LLInventoryPanel::modelChanged(U32 mask) // This item already exists in both memory and UI. It was probably reparented. else if (model_item && view_item) { + LLFolderViewFolder* old_parent = view_item->getParentFolder(); // Don't process the item if it is the root if (view_item->getRoot() != view_item) { - LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolderRoot.get()->getItemByID(model_item->getParentUUID()); + LLFolderViewFolder* new_parent = (LLFolderViewFolder*)getItemByID(model_item->getParentUUID()); // Item has been moved. - if (view_item->getParentFolder() != new_parent) + if (old_parent != new_parent) { if (new_parent != NULL) { // Item is to be moved and we found its new parent in the panel's directory, so move the item's UI. view_item->getParentFolder()->extractItem(view_item); view_item->addToFolder(new_parent, mFolderRoot.get()); + addItemID(view_item->getListener()->getUUID(), view_item); if (mInventory) { const LLUUID trash_id = mInventory->findCategoryUUIDForType(LLFolderType::FT_TRASH); @@ -631,6 +666,10 @@ void LLInventoryPanel::modelChanged(U32 mask) } else { + // Remove the item ID before destroying the view because the view-model-item gets + // destroyed when the view is destroyed + removeItemID(view_item->getListener()->getUUID()); + // Item is to be moved outside the panel's directory (e.g. moved to trash for a panel that // doesn't include trash). Just remove the item's UI. view_item->destroyView(); @@ -645,12 +684,54 @@ void LLInventoryPanel::modelChanged(U32 mask) else if (!model_item && view_item) { // Remove the item's UI. + //LLFolderViewFolder* parent = view_item->getParentFolder(); + removeItemID(view_item->getListener()->getUUID()); view_item->destroyView(); } } } } +const LLUUID LLInventoryPanel::getRootFolderID() const +{ + LLUUID root_id; + if (mFolderRoot.get() && mFolderRoot.get()->getListener()) + { + root_id = mFolderRoot.get()->getListener()->getUUID(); + } + else + { + if (mStartFolder.has("id")) + { + root_id = mStartFolder["id"]; + } + else + { + LLStringExplicit label(mStartFolder["name"]); + const LLFolderType::EType preferred_type = mStartFolder.has("type") + ? LLFolderType::lookup(mStartFolder["type"]) + : LLViewerFolderType::lookupTypeFromNewCategoryName(label); + + if ("LIBRARY" == label) + { + root_id = gInventory.getLibraryRootFolderID(); + } + else if (preferred_type != LLFolderType::FT_NONE) + { + //setLabel(label); + + root_id = gInventory.findCategoryUUIDForType(preferred_type, false); + if (root_id.isNull()) + { + LL_WARNS() << "Could not find folder for " << mStartFolder << LL_ENDL; + root_id.generateNewID(); + } + } + } + } + return root_id; +} + // static void LLInventoryPanel::onIdle(void *userdata) { @@ -669,10 +750,7 @@ void LLInventoryPanel::onIdle(void *userdata) } } -const LLUUID& LLInventoryPanel::getRootFolderID() const -{ - return mFolderRoot.get()->getListener()->getUUID(); -} + void LLInventoryPanel::initializeViews() { @@ -688,14 +766,14 @@ void LLInventoryPanel::initializeViews() if (gAgent.isFirstLogin()) { // Auto open the user's library - LLFolderViewFolder* lib_folder = mFolderRoot.get()->getFolderByID(gInventory.getLibraryRootFolderID()); + LLFolderViewFolder* lib_folder = getFolderByID(gInventory.getLibraryRootFolderID()); if (lib_folder) { lib_folder->setOpen(TRUE); } // Auto close the user's my inventory folder - LLFolderViewFolder* my_inv_folder = mFolderRoot.get()->getFolderByID(gInventory.getRootFolderID()); + LLFolderViewFolder* my_inv_folder = getFolderByID(gInventory.getRootFolderID()); if (my_inv_folder) { my_inv_folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); @@ -729,10 +807,13 @@ LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool u bridge, mGroupedItemBridge); ret->setAllowMultiSelect(mAllowMultiSelect); + ret->setShowEmptyMessage(mShowEmptyMessage); + /*ret->setShowItemLinkOverlays(mShowItemLinkOverlays); + ret->setAllowDropOnRoot(mAllowDropOnRoot);*/ return ret; } -LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) +LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge* bridge) { return new LLFolderViewFolder( bridge->getDisplayName(), @@ -757,80 +838,106 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) { - LLInventoryObject const* objectp = gInventory.getObject(id); - LLUUID root_id = mFolderRoot.get()->getListener()->getUUID(); - LLFolderViewFolder* parent_folder = NULL; - LLFolderViewItem* itemp = NULL; - - if (id == root_id) - { - parent_folder = mFolderRoot.get(); - } - else if (objectp) - { - const LLUUID &parent_id = objectp->getParentUUID(); - parent_folder = (LLFolderViewFolder*)mFolderRoot.get()->getItemByID(parent_id); - - if (parent_folder) - { - if (objectp->getType() <= LLAssetType::AT_NONE || - objectp->getType() >= LLAssetType::AT_COUNT) - { - LL_WARNS() << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " - << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() - << LL_ENDL; - return NULL; - } - - if ((objectp->getType() == LLAssetType::AT_CATEGORY) && - (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) - { - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), - objectp->getType(), - LLInventoryType::IT_CATEGORY, - this, - mFolderRoot.get(), - objectp->getUUID()); - if (new_listener) - { - LLFolderViewFolder* folderp = createFolderViewFolder(new_listener); - if (folderp) - { - folderp->setItemSortOrder(mFolderRoot.get()->getSortOrder()); - } - itemp = folderp; - } - } - else - { - // Build new view for item. - LLInventoryItem* item = (LLInventoryItem*)objectp; - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), - item->getActualType(), - item->getInventoryType(), - this, - mFolderRoot.get(), - item->getUUID(), - item->getFlags()); - - if (new_listener) - { - itemp = createFolderViewItem(new_listener); - } - } - - if (itemp) - { - itemp->addToFolder(parent_folder, mFolderRoot.get()); - } + LLInventoryObject const* objectp = gInventory.getObject(id); + LLFolderViewFolder* parent_folder = mFolderRoot.get(); + LLUUID root_id = parent_folder->getListener()->getUUID(); + LLFolderViewItem* folder_view_item = nullptr; + + if (id != root_id && !objectp) + { + return NULL; + } + + if (objectp) + { + folder_view_item = getItemByID(id); + + const LLUUID& parent_id = objectp->getParentUUID(); + parent_folder = (LLFolderViewFolder*)getItemByID(parent_id); + + // Force the creation of an extra root level folder item if required by the inventory panel (default is "false") + bool allow_drop = true; + bool create_root = false; + if (mShowRootFolder) + { + LLUUID root_id = getRootFolderID(); + if (root_id == id) + { + // We insert an extra level that's seen by the UI but has no influence on the model + parent_folder = dynamic_cast(folder_view_item); + folder_view_item = NULL; + allow_drop = mAllowDropOnRoot; + create_root = true; } } + if (!folder_view_item && parent_folder) + { + if (objectp->getType() <= LLAssetType::AT_NONE || + objectp->getType() >= LLAssetType::AT_COUNT) + { + LL_WARNS() << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " + << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() + << LL_ENDL; + return NULL; + } + + if ((objectp->getType() == LLAssetType::AT_CATEGORY) && + (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) + { + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, + (mUseMarketplaceFolders ? LLAssetType::AT_MARKETPLACE_FOLDER : LLAssetType::AT_CATEGORY), + LLInventoryType::IT_CATEGORY, + this, + mFolderRoot.get(), + objectp->getUUID()); + if (new_listener) + { + folder_view_item = createFolderViewFolder(new_listener); + if (folder_view_item) + { + static_cast(folder_view_item)->setItemSortOrder(mFolderRoot.get()->getSortOrder()); + folder_view_item->setAllowDrop(allow_drop); + folder_view_item->setAllowWear(mAllowWear); + } + } + } + else + { + // Build new view for item. + LLInventoryItem* item = (LLInventoryItem*)objectp; + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), + item->getActualType(), + item->getInventoryType(), + this, + mFolderRoot.get(), + item->getUUID(), + item->getFlags()); + + if (new_listener) + { + folder_view_item = createFolderViewItem(new_listener); + } + } + + if (folder_view_item) + { + llassert(parent_folder != NULL); + folder_view_item->addToFolder(parent_folder, mFolderRoot.get()); + addItemID(id, folder_view_item); + // In the case of the root folder been shown, open that folder by default once the widget is created + if (create_root) + { + folder_view_item->setOpen(TRUE); + } + } + } + } + // If this is a folder, add the children of the folder and recursively add any // child folders. if (id.isNull() - || (objectp - && objectp->getType() == LLAssetType::AT_CATEGORY)) + || (folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY)) { LLViewerInventoryCategory::cat_array_t* categories; LLViewerInventoryItem::item_array_t* items; @@ -847,7 +954,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) } } - if(items && parent_folder) + if(items) { for (LLViewerInventoryItem::item_array_t::const_iterator item_iter = items->begin(); item_iter != items->end(); @@ -860,7 +967,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) mInventory->unlockDirectDescendentArrays(id); } - return itemp; + return folder_view_item; } // bit of a hack to make sure the inventory is open. @@ -880,24 +987,6 @@ void LLInventoryPanel::openStartFolderOrMyInventory() } } -struct LLConfirmPurgeData -{ - LLUUID mID; - LLInventoryModel* mModel; -}; - -class LLIsNotWorn : public LLInventoryCollectFunctor -{ -public: - LLIsNotWorn() {} - virtual ~LLIsNotWorn() {} - virtual bool operator()(LLInventoryCategory* cat, - LLInventoryItem* item) - { - return !gAgentWearables.isWearingItem(item->getUUID()); - } -}; - class LLOpenFolderByID : public LLFolderViewFunctor { public: @@ -937,7 +1026,8 @@ BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask) BOOL handled = LLView::handleHover(x, y, mask); if(handled) { - if (LLInventoryModelBackgroundFetch::instance().folderFetchActive()) + ECursorType cursor = getWindow()->getCursor(); + if (LLInventoryModelBackgroundFetch::instance().folderFetchActive() && cursor == UI_CURSOR_ARROW) { // replace arrow cursor with arrow and hourglass cursor getWindow()->setCursor(UI_CURSOR_WORKING); @@ -965,7 +1055,7 @@ BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // If folder view is empty the (x, y) point won't be in its rect // so the handler must be called explicitly. // but only if was not handled before. See EXT-6746. - if (!handled && !mFolderRoot.get()->hasVisibleChildren()) + if (!handled && mAllowDropOnRoot && !mFolderRoot.get()->hasVisibleChildren()) { handled = mFolderRoot.get()->handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); } @@ -1025,7 +1115,7 @@ void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_foc { return; } - mFolderRoot.get()->setSelectionByID(obj_id, take_keyboard_focus); + setSelectionByID(obj_id, take_keyboard_focus); } void LLInventoryPanel::setSelectCallback(const boost::function& items, BOOL user_action)>& cb) @@ -1039,6 +1129,7 @@ void LLInventoryPanel::setSelectCallback(const boost::functionclearSelection(); + mSelectThisID.setNull(); } void LLInventoryPanel::onSelectionChange(const std::deque& items, BOOL user_action) @@ -1056,7 +1147,7 @@ void LLInventoryPanel::onSelectionChange(const std::deque& it } } - LLFolderView* fv = getRootFolder(); + LLFolderView* fv = mFolderRoot.get(); if (fv->needsAutoRename()) // auto-selecting a new user-created asset and preparing to rename { fv->setNeedsAutoRename(FALSE); @@ -1097,7 +1188,7 @@ void LLInventoryPanel::createNewItem(const std::string& name, BOOL LLInventoryPanel::getSinceLogoff() { - return getFilter()->isSinceLogoff(); + return getFilter().isSinceLogoff(); } // DEBUG ONLY @@ -1132,18 +1223,171 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open) res = floater_inventory->getActivePanel(); } + return res; } void LLInventoryPanel::addHideFolderType(LLFolderType::EType folder_type) { - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << folder_type)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << folder_type)); } BOOL LLInventoryPanel::getIsHiddenFolderType(LLFolderType::EType folder_type) const { - return !(getFilter()->getFilterCategoryTypes() & (1ULL << folder_type)); + return !(getFilter().getFilterCategoryTypes() & (1ULL << folder_type)); } +void LLInventoryPanel::addItemID( const LLUUID& id, LLFolderViewItem* itemp ) +{ + mItemMap[id] = itemp; +} + +void LLInventoryPanel::removeItemID(const LLUUID& id) +{ + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(id, categories, items, TRUE); + + mItemMap.erase(id); + + for (LLInventoryModel::cat_array_t::iterator it = categories.begin(), end_it = categories.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); +} + + for (LLInventoryModel::item_array_t::iterator it = items.begin(), end_it = items.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); + } +} + +//static LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID"); +LLFolderViewItem* LLInventoryPanel::getItemByID(const LLUUID& id) +{ + return mFolderRoot.get()->getItemByID(id); + /* + LLFastTimer mew(FTM_GET_ITEM_BY_ID); + + std::map::iterator map_it; + map_it = mItemMap.find(id); + if (map_it != mItemMap.end()) + { + return map_it->second; + } + + return NULL;*/ +} + +LLFolderViewFolder* LLInventoryPanel::getFolderByID(const LLUUID& id) +{ + return mFolderRoot.get()->getFolderByID(id); +/* + LLFolderViewItem* item = getItemByID(id); + return dynamic_cast(item); +*/ +} + + +void LLInventoryPanel::setSelectionByID( const LLUUID& obj_id, BOOL take_keyboard_focus ) +{ + mFolderRoot.get()->setSelectionByID(obj_id, take_keyboard_focus); + /* + LLFolderViewItem* itemp = getItemByID(obj_id); + if(itemp && itemp->getListener()) + { + itemp->arrangeAndSet(TRUE, take_keyboard_focus); + mSelectThisID.setNull(); + return; + } + else + { + // save the desired item to be selected later (if/when ready) + mSelectThisID = obj_id; + }*/ +} + +void LLInventoryPanel::updateSelection() +{ + mFolderRoot.get()->updateSelection(); +/* + if (mSelectThisID.notNull()) + { + setSelectionByID(mSelectThisID, false); + } +*/ +} + +namespace LLInventoryAction +{ + bool doToSelected(LLFolderView* root, std::string action, BOOL user_confirm = TRUE); +} + +void LLInventoryPanel::doToSelected(const LLSD& userdata) +{ + LLInventoryAction::doToSelected(mFolderRoot.get(), userdata.asString()); + + return; +} + +BOOL LLInventoryPanel::handleKeyHere( KEY key, MASK mask ) +{ + /* + BOOL handled = FALSE; + switch (key) + { + case KEY_RETURN: + // Open selected items if enter key hit on the inventory panel + if (mask == MASK_NONE) + { + LLInventoryAction::doToSelected(mFolderRoot.get(), "open"); + handled = TRUE; + } + break; + case KEY_DELETE: + case KEY_BACKSPACE: + // Delete selected items if delete or backspace key hit on the inventory panel + // Note: on Mac laptop keyboards, backspace and delete are one and the same + if (isSelectionRemovable() && (mask == MASK_NONE)) + { + LLInventoryAction::doToSelected(mFolderRoot.get(), "delete"); + handled = TRUE; + } + break; + }*/ + return LLPanel::handleKeyHere(key, mask); +} + +bool LLInventoryPanel::isSelectionRemovable() +{ + bool can_delete = false; + if (mFolderRoot.get()) + { + std::set selection_set = mFolderRoot.get()->getSelectionList(); + if (!selection_set.empty()) + { + can_delete = true; + for (std::set::iterator iter = selection_set.begin(); + iter != selection_set.end(); + ++iter) + { + LLFolderViewItem *item = getItemByID(*iter); + const LLFolderViewEventListener* listener =item->getListener(); + if (!listener) + { + can_delete = false; + } + else + { + can_delete &= listener->isItemRemovable() && !listener->isItemInTrash(); + } + } + } + } + return can_delete; +} /************************************************************************/ /* Recent Inventory Panel related class */ @@ -1154,13 +1398,14 @@ static const LLRecentInventoryBridgeBuilder RECENT_ITEMS_BUILDER; LLInventoryRecentItemsPanel:: LLInventoryRecentItemsPanel(const std::string& name, const std::string& sort_order_setting, - const std::string& start_folder, + const LLSD& start_folder, const LLRect& rect, LLInventoryModel* inventory, BOOL allow_multi_select, LLView *parent_view) : - LLInventoryPanel(name, sort_order_setting, start_folder,rect,inventory,allow_multi_select,parent_view) + LLInventoryPanel(name, sort_order_setting,start_folder,rect,inventory,allow_multi_select,parent_view) { + // replace bridge builder to have necessary View bridges. mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER; } diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index b0819d867..a29b0ea99 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -1,6 +1,6 @@ /** - * @file llpanelmaininventory.h - * @brief LLPanelMainInventory, LLInventoryFolder, and LLInventoryItem + * @file llinventorypanel.h + * @brief LLInventoryPanel * class definition * * $LicenseInfo:firstyear=2001&license=viewergpl$ @@ -65,11 +65,37 @@ class LLFolderViewGroupedItemBridge; class LLInventoryPanel : public LLPanel { + //-------------------------------------------------------------------- + // Data + //-------------------------------------------------------------------- +public: + struct Filter : public LLInitParam::Block + { + Optional sort_order; + Optional types; + Optional search_string; + + Filter() + : sort_order("sort_order"), + types("types", 0xffffffff), + search_string("search_string") + {} + }; + + struct InventoryState : public LLInitParam::Block + { + Mandatory filter; + //Mandatory sort; + }; + + //-------------------------------------------------------------------- + // Initialization + //-------------------------------------------------------------------- protected: friend class LFFloaterInvPanel; LLInventoryPanel(const std::string& name, const std::string& sort_order_setting, - const std::string& start_folder, + const LLSD& start_folder, const LLRect& rect, LLInventoryModel* inventory, BOOL allow_multi_select, @@ -87,6 +113,7 @@ public: // LLView methods void draw(); + /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); BOOL handleHover(S32 x, S32 y, MASK mask); BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -104,8 +131,9 @@ public: void setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus); void setSelectCallback(const boost::function& items, BOOL user_action)>& cb); void clearSelection(); - LLInventoryFilter* getFilter(); - const LLInventoryFilter* getFilter() const; + bool isSelectionRemovable(); + LLInventoryFilter& getFilter(); + const LLInventoryFilter& getFilter() const; void setFilterTypes(U64 filter, LLInventoryFilter::EFilterType = LLInventoryFilter::FILTERTYPE_OBJECT); U32 getFilterObjectTypes() const; void setFilterPermMask(PermissionMask filter_perm_mask); @@ -113,13 +141,13 @@ public: void setFilterWearableTypes(U64 filter); void setFilterSubString(const std::string& string); const std::string getFilterSubString(); - void setFilterWorn(bool worn); - bool getFilterWorn() const { return mFolderRoot.get()->getFilterWorn(); } - + void setFilterWornItems(); + void setSinceLogoff(BOOL sl); void setHoursAgo(U32 hours); + void setDateSearchDirection(U32 direction); BOOL getSinceLogoff(); - void setFilterLinks(U64 filter_links); + void setFilterLinks(LLInventoryFilter::EFilterLink filter_links); void setShowFolderState(LLInventoryFilter::EFolderShow show); LLInventoryFilter::EFolderShow getShowFolderState(); @@ -128,10 +156,14 @@ public: void modelChanged(U32 mask); LLFolderView* getRootFolder() { return mFolderRoot.get(); } LLScrollContainer* getScrollableContainer() { return mScroller; } + bool getAllowDropOnRoot() const { return mAllowDropOnRoot; } void onSelectionChange(const std::deque &items, BOOL user_action); LLHandle getInventoryPanelHandle() const { return getDerivedHandle(); } + + // Callbacks + void doToSelected(const LLSD& userdata); // DEBUG ONLY: static void dumpSelectionInformation(void* user_data); @@ -155,6 +187,13 @@ public: LLInventoryType::EType inv_type, U32 next_owner_perm = 0); + void addItemID(const LLUUID& id, LLFolderViewItem* itemp); + void removeItemID(const LLUUID& id); + LLFolderViewItem* getItemByID(const LLUUID& id); + LLFolderViewFolder* getFolderByID(const LLUUID& id); + void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); + void updateSelection(); + // Clean up stuff when the folder root gets deleted void clearFolderRoot(); @@ -162,15 +201,17 @@ protected: void openStartFolderOrMyInventory(); // open the first level of inventory void onItemsCompletion(); // called when selected items are complete + LLUUID mSelectThisID; LLInventoryModel* mInventory; LLInventoryObserver* mInventoryObserver; LLInvPanelComplObserver* mCompletionObserver; - - BOOL mAllowMultiSelect; + bool mAllowMultiSelect; + LLHandle mFolderRoot; LLScrollContainer* mScroller; LLPointer mGroupedItemBridge; + std::map mItemMap; /** * Pointer to LLInventoryFolderViewModelBuilder. * @@ -195,7 +236,13 @@ public: void requestSort(); private: - const std::string mStartFolder; + LLSD mStartFolder; + bool mShowRootFolder; + bool mShowEmptyMessage; + //bool mShowItemLinkOverlays; + bool mAllowDropOnRoot; + bool mAllowWear; + bool mUseMarketplaceFolders; const std::string mSortOrderSetting; //-------------------------------------------------------------------- @@ -206,7 +253,7 @@ public: public: BOOL getIsViewsInitialized() const { return mViewsInitialized; } - const LLUUID& getRootFolderID() const; + const LLUUID getRootFolderID() const; protected: // Builds the UI. Call this once the inventory is usable. void initializeViews(); @@ -224,9 +271,6 @@ protected: class LLPanelMainInventory; - - - ///---------------------------------------------------------------------------- /// Function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- @@ -249,6 +293,3 @@ const BOOL TAKE_FOCUS_YES = TRUE; const BOOL TAKE_FOCUS_NO = FALSE; #endif // LL_LLINVENTORYPANEL_H - - - diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp new file mode 100644 index 000000000..ba1ba95b5 --- /dev/null +++ b/indra/newview/lllandmarkactions.cpp @@ -0,0 +1,421 @@ +// 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 lllandmarkactions.cpp +* @brief LLLandmarkActions class implementation +* +* $LicenseInfo:firstyear=2001&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2010, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" +#include "lllandmarkactions.h" + +#include "roles_constants.h" + +#include "llinventory.h" +#include "llinventoryfunctions.h" +#include "lllandmark.h" +#include "llparcel.h" +#include "llregionhandle.h" + +#include "llnotificationsutil.h" + +#include "llagent.h" +#include "llagentui.h" +#include "llinventorymodel.h" +#include "lllandmarklist.h" +#include "llslurl.h" +#include "llstring.h" +#include "llviewerinventory.h" +#include "llviewerparcelmgr.h" +#include "llworldmapmessage.h" +#include "llviewerwindow.h" +#include "llwindow.h" +#include "llworldmap.h" + +void copy_slurl_to_clipboard_callback(const std::string& slurl); + +class LLFetchlLandmarkByPos : public LLInventoryCollectFunctor +{ +private: + LLVector3d mPos; +public: + LLFetchlLandmarkByPos(const LLVector3d& pos) : + mPos(pos) + {} + + bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) override + { + if (!item || item->getType() != LLAssetType::AT_LANDMARK) + return false; + + LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID()); + if (!landmark) // the landmark not been loaded yet + return false; + + LLVector3d landmark_global_pos; + if (!landmark->getGlobalPos(landmark_global_pos)) + return false; + //we have to round off each coordinates to compare positions properly + return ll_round(mPos.mdV[VX]) == ll_round(landmark_global_pos.mdV[VX]) + && ll_round(mPos.mdV[VY]) == ll_round(landmark_global_pos.mdV[VY]) + && ll_round(mPos.mdV[VZ]) == ll_round(landmark_global_pos.mdV[VZ]); + } +}; + +class LLFetchLandmarksByName : public LLInventoryCollectFunctor +{ +private: + std::string name; + BOOL use_substring; + //this member will be contain copy of founded items to keep the result unique + std::set check_duplicate; + +public: +LLFetchLandmarksByName(std::string &landmark_name, BOOL if_use_substring) +:name(landmark_name), +use_substring(if_use_substring) + { + LLStringUtil::toLower(name); + } + +public: + bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) override + { + if (!item || item->getType() != LLAssetType::AT_LANDMARK) + return false; + + LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID()); + if (!landmark) // the landmark not been loaded yet + return false; + + bool acceptable = false; + std::string landmark_name = item->getName(); + LLStringUtil::toLower(landmark_name); + if(use_substring) + { + acceptable = landmark_name.find( name ) != std::string::npos; + } + else + { + acceptable = landmark_name == name; + } + if(acceptable){ + if(check_duplicate.find(landmark_name) != check_duplicate.end()){ + // we have duplicated items in landmarks + acceptable = false; + }else{ + check_duplicate.insert(landmark_name); + } + } + + return acceptable; + } +}; + +// Returns true if the given inventory item is a landmark pointing to the current parcel. +// Used to find out if there is at least one landmark from current parcel. +class LLFirstAgentParcelLandmark : public LLInventoryCollectFunctor +{ +private: + bool mFounded;// to avoid unnecessary check + +public: + LLFirstAgentParcelLandmark(): mFounded(false){} + + bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) override + { + if (mFounded || !item || item->getType() != LLAssetType::AT_LANDMARK) + return false; + + LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID()); + if (!landmark) // the landmark not been loaded yet + return false; + + LLVector3d landmark_global_pos; + if (!landmark->getGlobalPos(landmark_global_pos)) + return false; + mFounded = LLViewerParcelMgr::getInstance()->inAgentParcel(landmark_global_pos); + return mFounded; + } +}; + +static void fetch_landmarks(LLInventoryModel::cat_array_t& cats, + LLInventoryModel::item_array_t& items, + LLInventoryCollectFunctor& add) +{ + // Look in "My Favorites" + const LLUUID favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + gInventory.collectDescendentsIf(favorites_folder_id, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + add); + + // Look in "Landmarks" + const LLUUID landmarks_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + gInventory.collectDescendentsIf(landmarks_folder_id, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + add); +} + +LLInventoryModel::item_array_t LLLandmarkActions::fetchLandmarksByName(std::string& name, BOOL use_substring) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFetchLandmarksByName by_name(name, use_substring); + fetch_landmarks(cats, items, by_name); + + return items; +} + +bool LLLandmarkActions::landmarkAlreadyExists() +{ + // Determine whether there are landmarks pointing to the current global agent position. + return findLandmarkForAgentPos() != nullptr; +} + +//static +bool LLLandmarkActions::hasParcelLandmark() +{ + LLFirstAgentParcelLandmark get_first_agent_landmark; + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + fetch_landmarks(cats, items, get_first_agent_landmark); + return !items.empty(); + +} + +// *TODO: This could be made more efficient by only fetching the FIRST +// landmark that meets the criteria +LLViewerInventoryItem* LLLandmarkActions::findLandmarkForGlobalPos(const LLVector3d &pos) +{ + // Determine whether there are landmarks pointing to the current parcel. + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFetchlLandmarkByPos is_current_pos_landmark(pos); + fetch_landmarks(cats, items, is_current_pos_landmark); + + return (items.empty()) ? nullptr : items[0]; +} + +LLViewerInventoryItem* LLLandmarkActions::findLandmarkForAgentPos() +{ + return findLandmarkForGlobalPos(gAgent.getPositionGlobal()); +} + +bool LLLandmarkActions::canCreateLandmarkHere() +{ + LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if(!agent_parcel) + { + LL_WARNS() << "No agent region" << LL_ENDL; + return false; + } + if (agent_parcel->getAllowLandmark() + || LLViewerParcelMgr::isParcelOwnedByAgent(agent_parcel, GP_LAND_ALLOW_LANDMARK)) + { + return true; + } + + return false; +} + +void LLLandmarkActions::createLandmarkHere( + const std::string& name, + const std::string& desc, + const LLUUID& folder_id) +{ + if(!gAgent.getRegion()) + { + LL_WARNS() << "No agent region" << LL_ENDL; + return; + } + LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (!agent_parcel) + { + LL_WARNS() << "No agent parcel" << LL_ENDL; + return; + } + if (!canCreateLandmarkHere()) + { + LLNotificationsUtil::add("CannotCreateLandmarkNotOwner"); + return; + } + + create_inventory_item(gAgent.getID(), gAgent.getSessionID(), + folder_id, LLTransactionID::tnull, + name, desc, + LLAssetType::AT_LANDMARK, + LLInventoryType::IT_LANDMARK, + NOT_WEARABLE, PERM_ALL, + NULL); +} + +void LLLandmarkActions::createLandmarkHere() +{ + std::string landmark_name, landmark_desc; + + LLAgentUI::buildLocationString(landmark_name, LLAgentUI::LOCATION_FORMAT_LANDMARK); + LLAgentUI::buildLocationString(landmark_desc, LLAgentUI::LOCATION_FORMAT_FULL); + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + + createLandmarkHere(landmark_name, landmark_desc, folder_id); +} + +LLVector3d getRegionPosFromGlobalPos(const LLVector3d& global_pos, const LLSimInfo* siminfo) +{ + LLVector3d local_pos; + local_pos[0] = fmod(global_pos[0], siminfo ? siminfo->getSizeX() : 256); + local_pos[1] = fmod(global_pos[1], siminfo ? siminfo->getSizeY() : 256); + return local_pos; +} + +void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slurl_callback_t cb, bool escaped /* = true */) +{ + const LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromPosGlobal(global_pos); + if (siminfo) + { + std::string slurl = LLSLURL(siminfo->getName(), getRegionPosFromGlobalPos(global_pos, siminfo)).getSLURLString(); + cb(slurl); + } + else + { + U64 new_region_handle = to_region_handle(global_pos); + + LLWorldMapMessage::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseSLURL, + cb, + global_pos, + escaped, + _2); + + LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, LLStringExplicit("unused"), false); + } +} + +void LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(const LLVector3d& global_pos, region_name_and_coords_callback_t cb) +{ + LLSimInfo* sim_infop = LLWorldMap::getInstance()->simInfoFromPosGlobal(global_pos); + if (sim_infop) + { + LLVector3 pos = sim_infop->getLocalPos(global_pos); + std::string name = sim_infop->getName() ; + cb(name, ll_round(pos.mV[VX]), ll_round(pos.mV[VY]),ll_round(pos.mV[VZ])); + } + else + { + U64 new_region_handle = to_region_handle(global_pos); + + LLWorldMapMessage::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseNameAndCoords, + cb, + global_pos, + _1); + + LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false); + } +} + +void LLLandmarkActions::onRegionResponseSLURL(slurl_callback_t cb, + const LLVector3d& global_pos, + bool escaped, + const std::string& url) +{ + std::string sim_name; + std::string slurl; + const LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromPosGlobal(global_pos); + if (siminfo) + { + slurl = LLSLURL(siminfo->getName(), getRegionPosFromGlobalPos(global_pos, siminfo)).getSLURLString(); + } + else + { + slurl.clear(); + } + + cb(slurl); +} + +void LLLandmarkActions::onRegionResponseNameAndCoords(region_name_and_coords_callback_t cb, + const LLVector3d& global_pos, + U64 region_handle) +{ + LLSimInfo* sim_infop = LLWorldMap::getInstance()->simInfoFromHandle(region_handle); + if (sim_infop) + { + LLVector3 local_pos = sim_infop->getLocalPos(global_pos); + std::string name = sim_infop->getName() ; + cb(name, ll_round(local_pos.mV[VX]), ll_round(local_pos.mV[VY]), ll_round(local_pos.mV[VZ])); + } +} + +bool LLLandmarkActions::getLandmarkGlobalPos(const LLUUID& landmarkInventoryItemID, LLVector3d& posGlobal) +{ + LLViewerInventoryItem* item = gInventory.getItem(landmarkInventoryItemID); + if (NULL == item) + return false; + + const LLUUID& asset_id = item->getAssetUUID(); + + LLLandmark* landmark = gLandmarkList.getAsset(asset_id); + if (NULL == landmark) + return false; + + return landmark->getGlobalPos(posGlobal); +} + +LLLandmark* LLLandmarkActions::getLandmark(const LLUUID& landmarkInventoryItemID, LLLandmarkList::loaded_callback_t cb) +{ + LLViewerInventoryItem* item = gInventory.getItem(landmarkInventoryItemID); + if (NULL == item) + return NULL; + + const LLUUID& asset_id = item->getAssetUUID(); + + LLLandmark* landmark = gLandmarkList.getAsset(asset_id, cb); + if (landmark) + { + return landmark; + } + + return NULL; +} + +void LLLandmarkActions::copySLURLtoClipboard(const LLUUID& landmarkInventoryItemID) +{ + LLLandmark* landmark = LLLandmarkActions::getLandmark(landmarkInventoryItemID); + if(landmark) + { + LLVector3d global_pos; + landmark->getGlobalPos(global_pos); + LLLandmarkActions::getSLURLfromPosGlobal(global_pos,©_slurl_to_clipboard_callback,true); + } +} + +void copy_slurl_to_clipboard_callback(const std::string& slurl) +{ + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl)); + LLSD args; + args["SLURL"] = slurl; + LLNotificationsUtil::add("CopySLURL", args); +} diff --git a/indra/newview/lllandmarkactions.h b/indra/newview/lllandmarkactions.h new file mode 100644 index 000000000..8ac61d6af --- /dev/null +++ b/indra/newview/lllandmarkactions.h @@ -0,0 +1,138 @@ +/** + * @file lllandmarkactions.h + * @brief LLLandmark class declaration + * + * $LicenseInfo:firstyear=2000&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_LLLANDMARKACTIONS_H +#define LL_LLLANDMARKACTIONS_H + +#include "llinventorymodel.h" + +#include "lllandmarklist.h" + +class LLLandmark; + +/** + * @brief Provides helper functions to manage landmarks + */ +class LLLandmarkActions +{ +public: + typedef std::function slurl_callback_t; + typedef std::function region_name_and_coords_callback_t; + + /** + * @brief Fetches landmark LLViewerInventoryItems for the given landmark name. + */ + static LLInventoryModel::item_array_t fetchLandmarksByName(std::string& name, BOOL if_use_substring); + /** + * @brief Checks whether landmark exists for current agent position. + */ + static bool landmarkAlreadyExists(); + + /** + * @brief Checks whether landmark exists for current agent parcel. + */ + static bool hasParcelLandmark(); + + /** + * @brief Searches landmark for global position. + * @return Returns landmark or NULL. + * + * *TODO: dzaporozhan: There can be many landmarks for single parcel. + */ + static LLViewerInventoryItem* findLandmarkForGlobalPos(const LLVector3d &pos); + + /** + * @brief Searches landmark for agent global position. + * @return Returns landmark or NULL. + * + * *TODO: dzaporozhan: There can be many landmarks for single parcel. + */ + static LLViewerInventoryItem* findLandmarkForAgentPos(); + + + /** + * @brief Checks whether agent has rights to create landmark for current parcel. + */ + static bool canCreateLandmarkHere(); + + /** + * @brief Creates landmark for current parcel. + */ + static void createLandmarkHere(); + + /** + * @brief Creates landmark for current parcel. + */ + static void createLandmarkHere( + const std::string& name, + const std::string& desc, + const LLUUID& folder_id); + /** + * @brief Creates SLURL for given global position. + */ + static void getSLURLfromPosGlobal(const LLVector3d& global_pos, slurl_callback_t cb, bool escaped = true); + + static void getRegionNameAndCoordsFromPosGlobal(const LLVector3d& global_pos, region_name_and_coords_callback_t cb); + + /** + * @brief Gets landmark global position specified by inventory LLUUID. + * Found position is placed into "posGlobal" variable. + *. + * @return - true if specified item exists in Inventory and an appropriate landmark found. + * and its position is known, false otherwise. + */ + // *TODO: mantipov: profide callback for cases, when Landmark is not loaded yet. + static bool getLandmarkGlobalPos(const LLUUID& landmarkInventoryItemID, LLVector3d& posGlobal); + + /** + * @brief Retrieve a landmark from gLandmarkList by inventory item's id + * If a landmark is not currently in the gLandmarkList a callback "cb" is called when it is loaded. + * + * @return pointer to loaded landmark from gLandmarkList or NULL if landmark does not exist or wasn't loaded. + */ + static LLLandmark* getLandmark(const LLUUID& landmarkInventoryItemID, LLLandmarkList::loaded_callback_t cb = nullptr); + + /** + * @brief Performs standard action of copying of SLURL from landmark to user's clipboard. + * This action requires additional server request. The user will be notified by info message, + * when URL is copied . + */ + static void copySLURLtoClipboard(const LLUUID& landmarkInventoryItemID); + +private: + LLLandmarkActions() = delete; + LLLandmarkActions(const LLLandmarkActions&) = delete; + + static void onRegionResponseSLURL(slurl_callback_t cb, + const LLVector3d& global_pos, + bool escaped, + const std::string& url); + static void onRegionResponseNameAndCoords(region_name_and_coords_callback_t cb, + const LLVector3d& global_pos, + U64 region_handle); +}; + +#endif //LL_LLLANDMARKACTIONS_H diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp index 1d2eab102..02cfd326a 100644 --- a/indra/newview/lllandmarklist.cpp +++ b/indra/newview/lllandmarklist.cpp @@ -1,3 +1,5 @@ +// 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 lllandmarklist.cpp * @brief Landmark asset list class @@ -35,7 +37,6 @@ #include "llagent.h" #include "llvfile.h" #include "llviewerstats.h" -#include "llnotificationsutil.h" // Globals LLLandmarkList gLandmarkList; @@ -47,6 +48,7 @@ LLLandmarkList gLandmarkList; LLLandmarkList::~LLLandmarkList() { std::for_each(mList.begin(), mList.end(), DeletePairedPointer()); + mList.clear(); } LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_uuid, loaded_callback_t cb) @@ -67,7 +69,7 @@ LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_uuid, loaded_callback_t { if ( mBadList.find(asset_uuid) != mBadList.end() ) { - return NULL; + return nullptr; } landmark_requested_list_t::iterator iter = mRequestedList.find(asset_uuid); @@ -76,7 +78,7 @@ LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_uuid, loaded_callback_t const F32 rerequest_time = 30.f; // 30 seconds between requests if (gFrameTimeSeconds - iter->second < rerequest_time) { - return NULL; + return nullptr; } } @@ -89,10 +91,10 @@ LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_uuid, loaded_callback_t gAssetStorage->getAssetData(asset_uuid, LLAssetType::AT_LANDMARK, LLLandmarkList::processGetAssetReply, - NULL); + nullptr); mRequestedList[asset_uuid] = gFrameTimeSeconds; } - return NULL; + return nullptr; } // static @@ -143,14 +145,16 @@ void LLLandmarkList::processGetAssetReply( else { LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); - + // SJB: No use case for a notification here. Use LL_DEBUGS() instead if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ) { - LLNotificationsUtil::add("LandmarkMissing"); + LL_WARNS("Landmarks") << "Missing Landmark" << LL_ENDL; + //LLNotificationsUtil::add("LandmarkMissing"); } else { - LLNotificationsUtil::add("UnableToLoadLandmark"); + LL_WARNS("Landmarks") << "Unable to load Landmark" << LL_ENDL; + //LLNotificationsUtil::add("UnableToLoadLandmark"); } gLandmarkList.mBadList.insert(uuid); diff --git a/indra/newview/lllandmarklist.h b/indra/newview/lllandmarklist.h index ab51f7135..4983af562 100644 --- a/indra/newview/lllandmarklist.h +++ b/indra/newview/lllandmarklist.h @@ -27,11 +27,6 @@ #ifndef LL_LLLANDMARKLIST_H #define LL_LLLANDMARKLIST_H -#ifndef BOOST_FUNCTION_HPP_INCLUDED -#include -#define BOOST_FUNCTION_HPP_INCLUDED -#endif -#include #include "lllandmark.h" #include "lluuid.h" #include "llassetstorage.h" @@ -43,7 +38,7 @@ class LLInventoryItem; class LLLandmarkList { public: - typedef boost::function loaded_callback_t; + typedef std::function loaded_callback_t; LLLandmarkList() {} ~LLLandmarkList(); @@ -53,7 +48,7 @@ public: //const LLLandmark* getNext() { return mList.getNextData(); } BOOL assetExists(const LLUUID& asset_uuid); - LLLandmark* getAsset(const LLUUID& asset_uuid, loaded_callback_t cb = NULL); + LLLandmark* getAsset(const LLUUID& asset_uuid, loaded_callback_t cb = nullptr); static void processGetAssetReply( LLVFS *vfs, const LLUUID& uuid, diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp index 5d8c30fb1..c5ffcdae6 100644 --- a/indra/newview/llmaniptranslate.cpp +++ b/indra/newview/llmaniptranslate.cpp @@ -542,7 +542,7 @@ BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask) { F32 max_drag_distance = gSavedSettings.getF32("MaxDragDistance"); - if (gHippoGridManager->getConnectedGrid()->isAurora()) + if (gHippoGridManager->getConnectedGrid()->isWhiteCore()) max_drag_distance = llmin(10000.f, max_drag_distance); if (relative_move.magVecSquared() > max_drag_distance * max_drag_distance) diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index d8c921b9b..10efa114c 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -29,14 +29,23 @@ #include "llmarketplacefunctions.h" #include "llagent.h" +#include "llbufferstream.h" #include "llhttpclient.h" +#include "llinventoryfunctions.h" +#include "llinventoryobserver.h" +#include "llnotificationsutil.h" +#include "llsdjson.h" #include "lltimer.h" #include "lltrans.h" #include "llviewercontrol.h" +#include "llviewerinventory.h" #include "llviewermedia.h" #include "llviewernetwork.h" +#include "llviewerregion.h" #include "hippogridmanager.h" +#include "llsdutil.h" + // // Helpers // @@ -84,7 +93,7 @@ static std::string getMarketplaceDomain() else { // TODO: Find out if OpenSim, and Avination adopted any outbox stuffs, if so code HippoGridManager for this - // Aurora grid has not. + // WhiteCore has not. // For now, set domain on other grids to the loginUri domain, so we don't harass LL web services. domain = getLoginUriDomain(); //gHippoGridManager->getCurrentGrid()->getMarketPlaceDomain(); } @@ -101,7 +110,7 @@ static std::string getMarketplaceURL(const std::string& urlStringName) return marketplace_url; } -LLSD getMarketplaceStringSubstitutions() +LLSD LLMarketplaceData::getMarketplaceStringSubstitutions() { std::string marketplace_url = getMarketplaceURL("MarketplaceURL"); std::string marketplace_url_create = getMarketplaceURL("MarketplaceURL_CreateStore"); @@ -120,16 +129,545 @@ LLSD getMarketplaceStringSubstitutions() return marketplace_sub_map; } +// Get the version folder: if there is only one subfolder, we will use it as a version folder +LLUUID getVersionFolderIfUnique(const LLUUID& folder_id) +{ + LLUUID version_id = LLUUID::null; + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(folder_id, categories, items); + if (categories->size() == 1) + { + version_id = categories->begin()->get()->getUUID(); + } + else + { + LLNotificationsUtil::add("AlertMerchantListingActivateRequired"); + } + return version_id; +} + +/////////////////////////////////////////////////////////////////////////////// + // SLM Reporters +void log_SLM_warning(const std::string& request, U32 status, const std::string& reason, const std::string& code, const LLSD& content) +{ + LL_WARNS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", reason : " << reason << ", code : " << code << ", description : " << ll_pretty_print_sd(content) << LL_ENDL; + if ((status == 422) && + content.isArray() && + content.size() > 4) + { + // Unprocessable Entity : Special case that error as it is a frequent answer when trying to list an incomplete listing + LLNotificationsUtil::add("MerchantUnprocessableEntity"); + } + else + { + // Prompt the user with the warning (so they know why things are failing) + LLSD subs; + // We do show long descriptions in the alert (unlikely to be readable). The description string will be in the log though. + std::string description; + { + if (content.isArray()) + { + for (auto it = content.beginArray(); it != content.endArray(); ++it) + { + if (!description.empty()) + description += '\n'; + description += (*it).asString(); + } + } + else + { + description = content.asString(); + } + } + std::string reason_lc = reason; + LLStringUtil::toLower(reason_lc); + if (!description.empty() && reason_lc.find("unknown") != std::string::npos) + { + subs["[ERROR_REASON]"] = LLStringUtil::null; + } + else + { + subs["[ERROR_REASON]"] = '\'' + reason +"'\n"; + } + subs["[ERROR_DESCRIPTION]"] = description; + LLNotificationsUtil::add("MerchantTransactionFailed", subs); + } +} + +void log_SLM_infos(const std::string& request, U32 status, const std::string& body) +{ + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + LL_INFOS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", body or description : " << body << LL_ENDL; + } +} + +void log_SLM_infos(const std::string& request, const std::string& url, const std::string& body) +{ + if (gSavedSettings.getBOOL("MarketplaceListingsLogging")) + { + LL_INFOS("SLM") << "SLM API : Sending " << request << ". url : " << url << ", body : " << body << LL_ENDL; + } +} + +class LLSLMGetMerchantResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMGetMerchantResponder); +public: + + LLSLMGetMerchantResponder() {} + +protected: + void completedRaw(const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) override + { + S32 httpCode = getStatus(); + + if (!isGoodStatus(mStatus)) + { + if (httpCode == HTTP_NOT_FOUND) + { + log_SLM_infos("Get /merchant", httpCode, std::string("User is not a merchant")); + LLMarketplaceData::instance(). + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT); + } + else if (httpCode == HTTP_SERVICE_UNAVAILABLE) + { + log_SLM_infos("Get /merchant", httpCode, std::string("Merchant is not migrated")); + LLMarketplaceData::instance(). + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT); + } + else if (httpCode == 499) + { + const auto& content(getContent()); + // 499 includes timeout and ssl error - marketplace is down or having issues, we do not show it in this request according to MAINT-5938 + LL_WARNS("SLM") << "SLM Merchant Request failed with status: " << httpCode + << ", reason : " << getReason() + << ", code : " << content.get("error_code") + << ", description : " << content.get("error_description") << LL_ENDL; + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE); + } + else + { + log_SLM_warning("Get /merchant", httpCode, getReason(), LLStringUtil::null, getContent()); + LLMarketplaceData::instance(). + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE); + } + return; + } + + log_SLM_infos("Get /merchant", getStatus(), "User is a merchant"); + LLMarketplaceData::instance(). + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT); + } + virtual char const* getName() const { return "getMerchantStatus"; } +}; + +class LLSLMGetListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMGetListingsResponder); +public: + LLSLMGetListingsResponder(const LLUUID& folder_id) + { + folderId = folder_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance(). + setUpdating(folderId, false); + + std::string body; + decode_raw_body(channels, buffer, body); + + auto json = nlohmann::json::parse(body); + LLSD result = LlsdFromJson(json);; + + if (!isGoodStatus(mStatus)) + { + log_SLM_warning("Get /listings", getStatus(), getReason(), LLStringUtil::null, result); + LLMarketplaceData::instance(). + setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_FAILED); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Get /listings", getStatus(), body); + + // Extract the info from the results + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int listingId = listing["id"].asInteger(); + bool isListed = listing["is_listed"].asBoolean(); + std::string editUrl = listing["edit_url"].asString(); + LLUUID folderUuid = listing["inventory_info"]["listing_folder_id"].asUUID(); + LLUUID versionUuid = listing["inventory_info"]["version_folder_id"].asUUID(); + int count = listing["inventory_info"]["count_on_hand"].asInteger(); + + if (folderUuid.notNull()) + { + LLMarketplaceData::instance(). + addListing(folderUuid, listingId, versionUuid, isListed, editUrl, count); + } + } + + // Update all folders under the root + LLMarketplaceData::instance(). + setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_DONE); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + } + virtual char const* getName() const { return "getSLMListings"; } +private: + LLUUID folderId; +}; + +class LLSLMCreateListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMCreateListingsResponder); +public: + + LLSLMCreateListingsResponder(const LLUUID& folder_id) + { + folderId = folder_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance().setUpdating(folderId, false); + + std::string body; + decode_raw_body(channels, buffer, body); + + auto json = nlohmann::json::parse(body); + LLSD result = LlsdFromJson(json);; + + if (!isGoodStatus(mStatus)) + { + log_SLM_warning("Post /listings", getStatus(), getReason(), LLStringUtil::null, result); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Post /listings", getStatus(), body); + + // Extract the info from the Json string + auto it = result["listings"].beginArray(); + + while (it != result["listings"].endArray()) + { + auto listing = *it; + + int listing_id = listing["id"].asInteger(); + bool is_listed = listing["is_listed"].asBoolean(); + std::string edit_url = listing["edit_url"].asString(); + std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString(); + std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString(); + int count = listing["inventory_info"]["count_on_hand"].asInteger(); + + LLUUID folder_id(folder_uuid_string); + LLUUID version_id(version_uuid_string); + LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed,edit_url,count); + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + it++; + } + } + virtual char const* getName() const { return "LLSLMCreateListingsResponder"; } +private: + LLUUID folderId; +}; + +class LLSLMGetListingResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMGetListingResponder); +public: + + LLSLMGetListingResponder(const LLUUID& folder_id) + { + folderId = folder_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance(). + setUpdating(folderId, false); + + std::string body; + decode_raw_body(channels, buffer, body); + + auto json = nlohmann::json::parse(body); + LLSD result = LlsdFromJson(json); + + if (!isGoodStatus(mStatus)) + { + if (getStatus() == HTTP_NOT_FOUND) + { + // That listing does not exist -> delete its record from the local SLM data store + LLMarketplaceData::instance(). + deleteListing(folderId, false); + } + else + { + log_SLM_warning("Get /listing", getStatus(), getReason(), LLStringUtil::null, result); + } + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Get /listing", getStatus(), body); + + // Extract the info from the results + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int resListingId = listing["id"].asInteger(); + bool isListed = listing["is_listed"].asBoolean(); + std::string editUrl = listing["edit_url"].asString(); + LLUUID folderUuid = listing["inventory_info"]["listing_folder_id"].asUUID(); + LLUUID versionUuid = listing["inventory_info"]["version_folder_id"].asUUID(); + int count = listing["inventory_info"]["count_on_hand"].asInteger(); + + // Update that listing + LLMarketplaceData::instance(). + setListingID(folderUuid, resListingId, false); + LLMarketplaceData::instance(). + setVersionFolderID(folderUuid, versionUuid, false); + LLMarketplaceData::instance(). + setActivationState(folderUuid, isListed, false); + LLMarketplaceData::instance(). + setListingURL(folderUuid, editUrl, false); + LLMarketplaceData::instance(). + setCountOnHand(folderUuid, count, false); + update_marketplace_category(folderUuid, false); + gInventory.notifyObservers(); + } + } + virtual char const* getName() const { return "getSingleListingCoro"; } +private: + LLUUID folderId; +}; + +class LLSLMUpdateListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMUpdateListingsResponder); +public: + + LLSLMUpdateListingsResponder(const LLUUID& folder_id, bool expected_listed_state, const LLUUID& expected_version_id) + { + folderId = folder_id; + isListed = expected_listed_state; + versionId = expected_version_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance(). + setUpdating(folderId, false); + + std::string body; + decode_raw_body(channels, buffer, body); + + auto json = nlohmann::json::parse(body); + LLSD result = LlsdFromJson(json); + + if (!isGoodStatus(mStatus)) + { + log_SLM_warning("Put /listing", getStatus(), getReason(), LLStringUtil::null, result); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Put /listing", getStatus(), body); + + // Extract the info from the Json string + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int listing_id = listing["id"].asInteger(); + bool is_listed = listing["is_listed"].asBoolean(); + std::string edit_url = listing["edit_url"].asString(); + LLUUID folderUuid = listing["inventory_info"]["listing_folder_id"].asUUID(); + LLUUID versionUuid = listing["inventory_info"]["version_folder_id"].asUUID(); + int onHand = listing["inventory_info"]["count_on_hand"].asInteger(); + + // Update that listing + LLMarketplaceData::instance(). + setListingID(folderUuid, listing_id, false); + LLMarketplaceData::instance(). + setVersionFolderID(folderUuid, versionUuid, false); + LLMarketplaceData::instance(). + setActivationState(folderUuid, is_listed, false); + LLMarketplaceData::instance(). + setListingURL(folderUuid, edit_url, false); + LLMarketplaceData::instance(). + setCountOnHand(folderUuid, onHand, false); + update_marketplace_category(folderUuid, false); + gInventory.notifyObservers(); + + // Show a notification alert if what we got is not what we expected + // (this actually doesn't result in an error status from the SLM API protocol) + if ((isListed != is_listed) || (versionId != versionUuid)) + { + LLSD subs; + subs["[URL]"] = edit_url; + LLNotificationsUtil::add("AlertMerchantListingNotUpdated", subs); + } + } + } + virtual char const* getName() const { return "updateSLMListingCoro"; } +private: + LLUUID folderId; + bool isListed; + LLUUID versionId; +}; + +class LLSLMAssociateListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMAssociateListingsResponder); +public: + + LLSLMAssociateListingsResponder(const LLUUID& folder_id, const LLUUID& source_folder_id) + { + folderId = folder_id; + sourceFolderId = source_folder_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance().setUpdating(folderId, false); + LLMarketplaceData::instance().setUpdating(sourceFolderId, false); + + std::string body; + decode_raw_body(channels, buffer, body); + + auto json = nlohmann::json::parse(body); + LLSD result = LlsdFromJson(json); + + if (!isGoodStatus(mStatus)) + { + log_SLM_warning("Put /associate_inventory", getStatus(), getReason(), LLStringUtil::null, result); + update_marketplace_category(folderId, false); + update_marketplace_category(sourceFolderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Put /associate_inventory", getStatus(), body); + + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int listing_id = listing["id"].asInteger(); + bool is_listed = listing["is_listed"].asBoolean(); + std::string edit_url = listing["edit_url"].asString(); + LLUUID folder_uuid = listing["inventory_info"]["listing_folder_id"].asUUID(); + LLUUID version_uuid = listing["inventory_info"]["version_folder_id"].asUUID(); + int count = listing["inventory_info"]["count_on_hand"].asInteger(); + + // Check that the listing ID is not already associated to some other record + LLUUID old_listing = LLMarketplaceData::instance().getListingFolder(listing_id); + if (old_listing.notNull()) + { + // If it is already used, unlist the old record (we can't have 2 listings with the same listing ID) + LLMarketplaceData::instance().deleteListing(old_listing); + } + + // Add the new association + LLMarketplaceData::instance(). + addListing(folder_uuid, listing_id, version_uuid, is_listed, edit_url, count); + update_marketplace_category(folder_uuid, false); + gInventory.notifyObservers(); + + // The stock count needs to be updated with the new local count now + LLMarketplaceData::instance(). + updateCountOnHand(folder_uuid, 1); + } + + // Always update the source folder so its widget updates + update_marketplace_category(sourceFolderId, false); + } + + virtual char const* getName() const { return "associateSLMListingCoro"; } +private: + LLUUID folderId; // This is the folder now associated with the id. + LLUUID sourceFolderId; // This is the folder initially associated with the id. Can be LLUUID::null +}; + +class LLSLMDeleteListingsResponder : public LLHTTPClient::ResponderWithCompleted +{ + LOG_CLASS(LLSLMDeleteListingsResponder); +public: + + LLSLMDeleteListingsResponder(const LLUUID& folder_id) + { + folderId = folder_id; + } + + virtual void completedRaw(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + LLMarketplaceData::instance().setUpdating(folderId, false); + + std::string body; + decode_raw_body(channels, buffer, body); + auto json = nlohmann::json::parse(body); + LLSD result = LlsdFromJson(json); + + if (!isGoodStatus(mStatus)) + { + log_SLM_warning("Delete /listing", getStatus(), getReason(), LLStringUtil::null, result); + update_marketplace_category(folderId, false); + gInventory.notifyObservers(); + return; + } + + log_SLM_infos("Delete /listing", getStatus(), body); + + for (LLSD::array_iterator it = result["listings"].beginArray(); + it != result["listings"].endArray(); ++it) + { + LLSD listing = *it; + + int listing_id = listing["id"].asInteger(); + LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id); + LLMarketplaceData::instance().deleteListing(folder_id); + } + } + virtual char const* getName() const { return "deleteSLMListingCoro"; } +private: + LLUUID folderId; +}; + +// SLM Responders End +/////////////////////////////////////////////////////////////////////////////// namespace LLMarketplaceImport { // Basic interface for this namespace - bool hasSessionCookie(); bool inProgress(); bool resultPending(); - U32 getResultStatus(); + S32 getResultStatus(); const LLSD& getResults(); bool establishMarketplaceSessionCookie(); @@ -138,12 +676,12 @@ namespace LLMarketplaceImport // Internal state variables - static std::string sMarketplaceCookie = ""; + static std::string sMarketplaceCookie; static LLSD sImportId = LLSD::emptyMap(); static bool sImportInProgress = false; static bool sImportPostPending = false; static bool sImportGetPending = false; - static U32 sImportResultStatus = 0; + static S32 sImportResultStatus = 0; static LLSD sImportResults = LLSD::emptyMap(); static LLTimer slmGetTimer; @@ -153,30 +691,32 @@ namespace LLMarketplaceImport class LLImportPostResponder : public LLHTTPClient::ResponderWithCompleted { + LOG_CLASS(LLImportPostResponder); public: - /*virtual*/ void httpCompleted(void) + void httpCompleted() override { slmPostTimer.stop(); if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { - LL_INFOS() << " SLM POST status: " << mStatus << LL_ENDL; - LL_INFOS() << " SLM POST reason: " << mReason << LL_ENDL; - LL_INFOS() << " SLM POST content: " << mContent.asString() << LL_ENDL; - LL_INFOS() << " SLM POST timer: " << slmPostTimer.getElapsedTimeF32() << LL_ENDL; + LL_INFOS() << " SLM [timer:" << slmPostTimer.getElapsedTimeF32() << "] " + << dumpResponse() << LL_ENDL; } - // MAINT-2301 : we determined we can safely ignore that error in that context - if (mStatus == MarketplaceErrorCodes::IMPORT_JOB_TIMEOUT) + S32 httpCode = getStatus(); + if ((httpCode == MarketplaceErrorCodes::IMPORT_REDIRECT) || + (httpCode == MarketplaceErrorCodes::IMPORT_AUTHENTICATION_ERROR) || + // MAINT-2301 : we determined we can safely ignore that error in that context + (httpCode == MarketplaceErrorCodes::IMPORT_JOB_TIMEOUT)) { if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { LL_INFOS() << " SLM POST : Ignoring time out status and treating it as success" << LL_ENDL; } - mStatus = MarketplaceErrorCodes::IMPORT_DONE; + httpCode = MarketplaceErrorCodes::IMPORT_DONE; } - if (mStatus >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) + if (httpCode >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) { if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { @@ -185,17 +725,19 @@ namespace LLMarketplaceImport sMarketplaceCookie.clear(); } - sImportInProgress = (mStatus == MarketplaceErrorCodes::IMPORT_DONE); + sImportInProgress = (httpCode == MarketplaceErrorCodes::IMPORT_DONE); sImportPostPending = false; - sImportResultStatus = mStatus; - sImportId = mContent; + sImportResultStatus = httpCode; + + sImportId = getContent(); } - /*virtual*/ char const* getName(void) const { return "LLImportPostResponder"; } + char const* getName() const override { return "LLImportPostResponder"; } }; class LLImportGetResponder : public LLHTTPClient::ResponderWithCompleted { + LOG_CLASS(LLImportGetResponder); public: /*virtual*/ bool needsHeaders(void) const { return true; } @@ -215,42 +757,42 @@ namespace LLMarketplaceImport } } - /*virtual*/ void httpCompleted(void) + /*virtual*/ void httpCompleted() { slmGetTimer.stop(); if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { - LL_INFOS() << " SLM GET status: " << mStatus << LL_ENDL; - LL_INFOS() << " SLM GET reason: " << mReason << LL_ENDL; - LL_INFOS() << " SLM GET content: " << mContent.asString() << LL_ENDL; - LL_INFOS() << " SLM GET timer: " << slmGetTimer.getElapsedTimeF32() << LL_ENDL; + LL_INFOS() << " SLM [timer:" << slmGetTimer.getElapsedTimeF32() << "] " + << dumpResponse() << LL_ENDL; } // MAINT-2452 : Do not clear the cookie on IMPORT_DONE_WITH_ERRORS : Happens when trying to import objects with wrong permissions // ACME-1221 : Do not clear the cookie on IMPORT_NOT_FOUND : Happens for newly created Merchant accounts that are initially empty - if ((mStatus >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) && - (mStatus != MarketplaceErrorCodes::IMPORT_DONE_WITH_ERRORS) && - (mStatus != MarketplaceErrorCodes::IMPORT_NOT_FOUND)) + S32 httpCode = getStatus(); + if ((httpCode >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST) && + (httpCode != MarketplaceErrorCodes::IMPORT_DONE_WITH_ERRORS) && + (httpCode != MarketplaceErrorCodes::IMPORT_NOT_FOUND)) { if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { - LL_INFOS() << " SLM GET clearing marketplace cookie due to client or server error (" << mStatus << " / " << mReason << ")." << LL_ENDL; + LL_INFOS() << " SLM GET clearing marketplace cookie due to client or server error" << LL_ENDL; } sMarketplaceCookie.clear(); } - else if (gSavedSettings.getBOOL("InventoryOutboxLogging") && (mStatus >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST)) + else if (gSavedSettings.getBOOL("InventoryOutboxLogging") && (httpCode >= MarketplaceErrorCodes::IMPORT_BAD_REQUEST)) { - LL_INFOS() << " SLM GET : Got error status = " << mStatus << ", but marketplace cookie not cleared." << LL_ENDL; + LL_INFOS() << " SLM GET : Got error status = " << httpCode << ", but marketplace cookie not cleared." << LL_ENDL; } - sImportInProgress = (mStatus == MarketplaceErrorCodes::IMPORT_PROCESSING); + sImportInProgress = (httpCode == MarketplaceErrorCodes::IMPORT_PROCESSING); sImportGetPending = false; - sImportResultStatus = mStatus; - sImportResults = mContent; + sImportResultStatus = httpCode; + + sImportResults = getContent(); } - /*virtual*/ char const* getName(void) const { return "LLImportGetResponder"; } + char const* getName() const override { return "LLImportGetResponder"; } }; // Basic API @@ -270,7 +812,7 @@ namespace LLMarketplaceImport return (sImportPostPending || sImportGetPending); } - U32 getResultStatus() + S32 getResultStatus() { return sImportResultStatus; } @@ -307,8 +849,10 @@ namespace LLMarketplaceImport if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { LL_INFOS() << " SLM GET: establishMarketplaceSessionCookie, LLHTTPClient::get, url = " << url << LL_ENDL; + std::stringstream str; + str << headers; //LLSDSerialize::toPrettyXML(headers, str); LL_INFOS() << " SLM GET: headers " << LL_ENDL; - LL_INFOS() << headers << LL_ENDL; + LL_INFOS() << str.str() << LL_ENDL; } slmGetTimer.start(); @@ -334,14 +878,17 @@ namespace LLMarketplaceImport AIHTTPHeaders headers; headers.addHeader("Accept", "*/*"); headers.addHeader("Cookie", sMarketplaceCookie); + // *TODO: Why are we setting Content-Type for a GET request? headers.addHeader("Content-Type", "application/llsd+xml"); headers.addHeader("User-Agent", LLViewerMedia::getCurrentUserAgent()); if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { LL_INFOS() << " SLM GET: pollStatus, LLHTTPClient::get, url = " << url << LL_ENDL; + std::stringstream str; + str << headers; //LLSDSerialize::toPrettyXML(headers, str); LL_INFOS() << " SLM GET: headers " << LL_ENDL; - LL_INFOS() << headers << LL_ENDL; + LL_INFOS() << str.str() << LL_ENDL; } slmGetTimer.start(); @@ -376,8 +923,10 @@ namespace LLMarketplaceImport if (gSavedSettings.getBOOL("InventoryOutboxLogging")) { LL_INFOS() << " SLM POST: triggerImport, LLHTTPClient::post, url = " << url << LL_ENDL; + std::stringstream str; + str << headers; //LLSDSerialize::toPrettyXML(headers, str); LL_INFOS() << " SLM POST: headers " << LL_ENDL; - LL_INFOS() << headers << LL_ENDL; + LL_INFOS() << str.str() << LL_ENDL; } slmPostTimer.start(); @@ -403,7 +952,6 @@ void LLMarketplaceInventoryImporter::update() if (update_timer.hasExpired()) { LLMarketplaceInventoryImporter::instance().updateImport(); - //static LLCachedControl MARKET_IMPORTER_UPDATE_FREQUENCY("MarketImporterUpdateFreq", 1.0f); update_timer.setTimerExpirySec(MARKET_IMPORTER_UPDATE_FREQUENCY); } } @@ -509,54 +1057,967 @@ void LLMarketplaceInventoryImporter::updateImport() // If we are no longer in progress if (!mImportInProgress) { + // Look for results success + mInitialized = LLMarketplaceImport::hasSessionCookie(); + + // Report results + if (mStatusReportSignal) + { + (*mStatusReportSignal)(LLMarketplaceImport::getResultStatus(), LLMarketplaceImport::getResults()); + } + if (mInitialized) { - // Report results - if (mStatusReportSignal) + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_MERCHANT; + // Follow up with auto trigger of import + if (mAutoTriggerImport) { - (*mStatusReportSignal)(LLMarketplaceImport::getResultStatus(), LLMarketplaceImport::getResults()); + mAutoTriggerImport = false; + mImportInProgress = triggerImport(); } } else { - // Look for results success - mInitialized = LLMarketplaceImport::hasSessionCookie(); - - if (mInitialized) + U32 status = LLMarketplaceImport::getResultStatus(); + if ((status == MarketplaceErrorCodes::IMPORT_FORBIDDEN) || + (status == MarketplaceErrorCodes::IMPORT_AUTHENTICATION_ERROR)) { - mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_MERCHANT; - // Follow up with auto trigger of import - if (mAutoTriggerImport) + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT; + } + else if (status == MarketplaceErrorCodes::IMPORT_SERVER_API_DISABLED) + { + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT; + } + else + { + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE; + } + if (mErrorInitSignal && (mMarketPlaceStatus == MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE)) + { + (*mErrorInitSignal)(LLMarketplaceImport::getResultStatus(), LLMarketplaceImport::getResults()); + } + } + } + } + + // Make sure we trigger the status change with the final state (in case of auto trigger after initialize) + if (mStatusChangedSignal) + { + (*mStatusChangedSignal)(mImportInProgress); + } +} + +// +// Direct Delivery : Marketplace tuples and data +// +class LLMarketplaceInventoryObserver : public LLInventoryObserver +{ +public: + LLMarketplaceInventoryObserver() {} + virtual ~LLMarketplaceInventoryObserver() {} + virtual void changed(U32 mask); +}; + +void LLMarketplaceInventoryObserver::changed(U32 mask) +{ + // When things are added to the marketplace, we might need to re-validate and fix the containing listings + if (mask & LLInventoryObserver::ADD) + { + const std::set& changed_items = gInventory.getChangedIDs(); + + std::set::const_iterator id_it = changed_items.begin(); + std::set::const_iterator id_end = changed_items.end(); + // First, count the number of items in this list... + S32 count = 0; + for (;id_it != id_end; ++id_it) + { + LLInventoryObject* obj = gInventory.getObject(*id_it); + if (obj && (LLAssetType::AT_CATEGORY != obj->getType())) + { + count++; + } + } + // Then, decrement the folders of that amount + // Note that of all of those, only one folder will be a listing folder (if at all). + // The other will be ignored by the decrement method. + id_it = changed_items.begin(); + for (;id_it != id_end; ++id_it) + { + LLInventoryObject* obj = gInventory.getObject(*id_it); + if (obj && (LLAssetType::AT_CATEGORY == obj->getType())) + { + LLMarketplaceData::instance().decrementValidationWaiting(obj->getUUID(),count); + } + } + } + + // When things are changed in the inventory, this can trigger a host of changes in the marketplace listings folder: + // * stock counts changing : no copy items coming in and out will change the stock count on folders + // * version and listing folders : moving those might invalidate the marketplace data itself + // Since we should cannot raise inventory change while the observer is called (the list will be cleared + // once observers are called) we need to raise a flag in the inventory to signal that things have been dirtied. + + if (mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE)) + { + const std::set& changed_items = gInventory.getChangedIDs(); + + std::set::const_iterator id_it = changed_items.begin(); + std::set::const_iterator id_end = changed_items.end(); + for (;id_it != id_end; ++id_it) + { + LLInventoryObject* obj = gInventory.getObject(*id_it); + if (obj) + { + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + // If it's a folder known to the marketplace, let's check it's in proper shape + if (LLMarketplaceData::instance().isListed(*id_it) || LLMarketplaceData::instance().isVersionFolder(*id_it)) { - mAutoTriggerImport = false; - mImportInProgress = triggerImport(); + LLInventoryCategory* cat = (LLInventoryCategory*)(obj); + validate_marketplacelistings(cat); } } else { - U32 status = LLMarketplaceImport::getResultStatus(); - if ((status == MarketplaceErrorCodes::IMPORT_FORBIDDEN) || - (status == MarketplaceErrorCodes::IMPORT_AUTHENTICATION_ERROR)) + // If it's not a category, it's an item... + LLInventoryItem* item = (LLInventoryItem*)(obj); + // If it's a no copy item, we may need to update the label count of marketplace listings + if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) { - mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT; - } - else - { - mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE; - } - if (mErrorInitSignal && (mMarketPlaceStatus == MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE)) - { - (*mErrorInitSignal)(LLMarketplaceImport::getResultStatus(), LLMarketplaceImport::getResults()); + LLMarketplaceData::instance().setDirtyCount(); } } } } + } +} - // Make sure we trigger the status change with the final state (in case of auto trigger after initialize) - if (mStatusChangedSignal) +// Tuple == Item +LLMarketplaceTuple::LLMarketplaceTuple() : + mListingFolderId(), + mListingId(0), + mVersionFolderId(), + mIsActive(false), + mEditURL("") +{ +} + +LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id) : + mListingFolderId(folder_id), + mListingId(0), + mVersionFolderId(), + mIsActive(false), + mEditURL("") +{ +} + +LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed) : + mListingFolderId(folder_id), + mListingId(listing_id), + mVersionFolderId(version_id), + mIsActive(is_listed), + mEditURL("") +{ +} + + +// Data map +LLMarketplaceData::LLMarketplaceData() : + mMarketPlaceStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED), + mMarketPlaceDataFetched(MarketplaceFetchCodes::MARKET_FETCH_NOT_DONE), + mStatusUpdatedSignal(NULL), + mDataFetchedSignal(NULL), + mDirtyCount(false) +{ + mInventoryObserver = new LLMarketplaceInventoryObserver; + gInventory.addObserver(mInventoryObserver); +} + +LLMarketplaceData::~LLMarketplaceData() +{ + gInventory.removeObserver(mInventoryObserver); +} + +void LLMarketplaceData::initializeSLM(const status_updated_signal_t::slot_type& cb) +{ + if (mStatusUpdatedSignal == NULL) + { + mStatusUpdatedSignal = new status_updated_signal_t(); + } + mStatusUpdatedSignal->connect(cb); + + if (mMarketPlaceStatus != MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED) + { + // If already initialized, just confirm the status so the callback gets called + setSLMStatus(mMarketPlaceStatus); + } + else + { + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING; + } + + std::string url = getSLMConnectURL("/merchant"); + if (url.empty()) + { + LL_WARNS("Marketplace") << "No marketplace capability on Sim" << LL_ENDL; + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE); + return; + } + + log_SLM_infos("LLHTTPClient::get", url, LLStringUtil::null); + LLHTTPClient::get(url, LLSD(), new LLSLMGetMerchantResponder); +} + +void LLMarketplaceData::setDataFetchedSignal(const status_updated_signal_t::slot_type& cb) +{ + if (mDataFetchedSignal == NULL) + { + mDataFetchedSignal = new status_updated_signal_t(); + } + mDataFetchedSignal->connect(cb); +} + +// Get/Post/Put requests to the SLM Server using the SLM API +void LLMarketplaceData::getSLMListings() +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + // Send request + std::string url = getSLMConnectURL("/listings"); + log_SLM_infos("LLHTTPClient::get", url, ""); + const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + setUpdating(marketplacelistings_id, true); + LLHTTPClient::get(url, new LLSLMGetListingsResponder(marketplacelistings_id), headers); +} + +void LLMarketplaceData::getSLMListing(S32 listingId) +{ + LLUUID folderId = getListingFolder(listingId); + setUpdating(folderId, true); + + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + // Send request + std::string url = getSLMConnectURL("/listing/") + llformat("%d", listingId); + log_SLM_infos("LLHTTPClient::get", url, LLStringUtil::null); + LLHTTPClient::get(url, new LLSLMGetListingResponder(folderId), headers); +} + +void LLMarketplaceData::createSLMListing(const LLUUID& folderId, const LLUUID& versionId, S32 count) +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + LLViewerInventoryCategory* category = gInventory.getCategory(folderId); + LLSD invInfo; + invInfo["listing_folder_id"] = folderId; + invInfo["version_folder_id"] = versionId; + invInfo["count_on_hand"] = count; + LLSD listing; + listing["name"] = category->getName(); + listing["inventory_info"] = invInfo; + LLSD postData; + postData["listing"] = listing; + + auto json = LlsdToJson(postData); + auto json_str = json.dump(); + + // postRaw() takes ownership of the buffer and releases it later. + size_t size = json_str.length(); + U8* data = new U8[size]; + memcpy(data, (U8*)(json_str.c_str()), size); + + // Send request + std::string url = getSLMConnectURL("/listings"); + log_SLM_infos("LLHTTPClient::postRaw", url, json_str); + setUpdating(folderId, true); + LLHTTPClient::postRaw(url, data, size, new LLSLMCreateListingsResponder(folderId), headers); +} + +void LLMarketplaceData::updateSLMListing(const LLUUID& folderId, S32 listingId, const LLUUID& versionId, bool isListed, S32 count) +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + // Note : auto unlist if the count is 0 (out of stock) + if (isListed && (count == 0)) + { + isListed = false; + LLNotificationsUtil::add("AlertMerchantStockFolderEmpty"); + } + + LLSD invInfo; + invInfo["listing_folder_id"] = folderId; + invInfo["version_folder_id"] = versionId; + invInfo["count_on_hand"] = count; + LLSD listing; + listing["inventory_info"] = invInfo; + listing["id"] = listingId; + listing["is_listed"] = isListed; + LLSD postData; + postData["listing"] = listing; + + auto json = LlsdToJson(postData); + auto json_str = json.dump(); + + // postRaw() takes ownership of the buffer and releases it later. + size_t size = json_str.size(); + U8* data = new U8[size]; + memcpy(data, (U8*)(json_str.c_str()), size); + + std::string url = getSLMConnectURL("/listing/") + llformat("%d", listingId); + log_SLM_infos("LLHTTPClient::putRaw", url, json_str); + setUpdating(folderId, true); + LLHTTPClient::putRaw(url, data, size, new LLSLMUpdateListingsResponder(folderId, isListed, versionId), headers); +} + +void LLMarketplaceData::associateSLMListing(const LLUUID& folderId, S32 listingId, const LLUUID& versionId, const LLUUID& sourceFolderId) +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + LLSD invInfo; + invInfo["listing_folder_id"] = folderId; + invInfo["version_folder_id"] = versionId; + LLSD listing; + listing["id"] = listingId; + listing["inventory_info"] = invInfo; + LLSD postData; + postData["listing"] = listing; + + auto json = LlsdToJson(postData); + auto json_str = json.dump(); + + // postRaw() takes ownership of the buffer and releases it later. + size_t size = json_str.size(); + U8* data = new U8[size]; + memcpy(data, (U8*)(json_str.c_str()), size); + + // Send request + std::string url = getSLMConnectURL("/associate_inventory/") + llformat("%d", listingId); + log_SLM_infos("LLHTTPClient::putRaw", url, json_str); + setUpdating(folderId, true); + setUpdating(sourceFolderId, true); + + LLHTTPClient::putRaw(url, data, size, new LLSLMAssociateListingsResponder(folderId, sourceFolderId), headers); +} + +void LLMarketplaceData::deleteSLMListing(S32 listingId) +{ + AIHTTPHeaders headers; + headers.addHeader("Accept", "application/json"); + headers.addHeader("Content-Type", "application/json"); + + // Send request + std::string url = getSLMConnectURL("/listing/") + llformat("%d", listingId); + log_SLM_infos("LLHTTPClient::del", url, ""); + LLUUID folderId = getListingFolder(listingId); + + setUpdating(folderId, true); + LLHTTPClient::del(url, new LLSLMDeleteListingsResponder(folderId), headers); +} + +std::string LLMarketplaceData::getSLMConnectURL(const std::string& route) +{ + std::string url; + LLViewerRegion* regionp = gAgent.getRegion(); + if (regionp) + { + // Get DirectDelivery cap + url = regionp->getCapability("DirectDelivery"); + if (!url.empty()) { - (*mStatusChangedSignal)(mImportInProgress); + url += route; + } + } + return url; +} + +void LLMarketplaceData::setSLMStatus(U32 status) +{ + mMarketPlaceStatus = status; + if (mStatusUpdatedSignal) + { + (*mStatusUpdatedSignal)(); + } +} + +void LLMarketplaceData::setSLMDataFetched(U32 status) +{ + mMarketPlaceDataFetched = status; + if (mDataFetchedSignal) + { + (*mDataFetchedSignal)(); + } +} + +// Creation / Deletion / Update +// Methods publicly called +bool LLMarketplaceData::createListing(const LLUUID& folder_id) +{ + if (isListed(folder_id)) + { + // Listing already exists -> exit with error + return false; + } + + // Get the version folder: if there is only one subfolder, we will set it as a version folder immediately + S32 count = -1; + LLUUID version_id = getVersionFolderIfUnique(folder_id); + if (version_id.notNull()) + { + count = compute_stock_count(version_id, true); + } + + // Validate the count on hand + if (count == COMPUTE_STOCK_NOT_EVALUATED) + { + // If the count on hand cannot be evaluated, we will consider it empty (out of stock) at creation time + // It will get reevaluated and updated once the items are fetched + count = 0; + } + + // Post the listing creation request to SLM + createSLMListing(folder_id, version_id, count); + + return true; +} + +bool LLMarketplaceData::clearListing(const LLUUID& folder_id, S32 depth) +{ + if (folder_id.isNull()) + { + // Folder doesn't exist -> exit with error + return false; + } + + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } + // Folder id can be the root of the listing or not so we need to retrieve the root first + LLUUID listing_uuid = (isListed(folder_id) ? folder_id : nested_parent_id(folder_id, depth)); + S32 listing_id = getListingID(listing_uuid); + + if (listing_id == 0) + { + // Listing doesn't exist -> exit with error + return false; + } + + // Update the SLM Server so that this listing is deleted (actually, archived...) + deleteSLMListing(listing_id); + + return true; +} + +bool LLMarketplaceData::getListing(const LLUUID& folder_id, S32 depth) +{ + if (folder_id.isNull()) + { + // Folder doesn't exist -> exit with error + return false; + } + + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } + // Folder id can be the root of the listing or not so we need to retrieve the root first + LLUUID listing_uuid = (isListed(folder_id) ? folder_id : nested_parent_id(folder_id, depth)); + S32 listing_id = getListingID(listing_uuid); + + if (listing_id == 0) + { + // Listing doesn't exist -> exit with error + return false; + } + + // Get listing data from SLM + getSLMListing(listing_id); + + return true; +} + +bool LLMarketplaceData::getListing(S32 listing_id) +{ + if (listing_id == 0) + { + return false; + } + + // Get listing data from SLM + getSLMListing(listing_id); + return true; +} + +bool LLMarketplaceData::activateListing(const LLUUID& folder_id, bool activate, S32 depth) +{ + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } + // Folder id can be the root of the listing or not so we need to retrieve the root first + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + S32 listing_id = getListingID(listing_uuid); + if (listing_id == 0) + { + // Listing doesn't exist -> exit with error + return false; + } + + if (getActivationState(listing_uuid) == activate) + { + // If activation state is unchanged, no point spamming SLM with an update + return true; + } + + LLUUID version_uuid = getVersionFolder(listing_uuid); + + // Also update the count on hand + S32 count = compute_stock_count(folder_id); + if (count == COMPUTE_STOCK_NOT_EVALUATED) + { + // If the count on hand cannot be evaluated locally, we should not change that SLM value + // We are assuming that this issue is local and should not modify server side values + count = getCountOnHand(listing_uuid); + } + + // Post the listing update request to SLM + updateSLMListing(listing_uuid, listing_id, version_uuid, activate, count); + + return true; +} + +bool LLMarketplaceData::setVersionFolder(const LLUUID& folder_id, const LLUUID& version_id, S32 depth) +{ + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } + // Folder id can be the root of the listing or not so we need to retrieve the root first + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + S32 listing_id = getListingID(listing_uuid); + if (listing_id == 0) + { + // Listing doesn't exist -> exit with error + return false; + } + + if (getVersionFolder(listing_uuid) == version_id) + { + // If version folder is unchanged, no point spamming SLM with an update + return true; + } + + // Note: if the version_id is cleared, we need to unlist the listing, otherwise, state unchanged + bool is_listed = (version_id.isNull() ? false : getActivationState(listing_uuid)); + + // Also update the count on hand + S32 count = compute_stock_count(version_id); + if (count == COMPUTE_STOCK_NOT_EVALUATED) + { + // If the count on hand cannot be evaluated, we will consider it empty (out of stock) when resetting the version folder + // It will get reevaluated and updated once the items are fetched + count = 0; + } + + // Post the listing update request to SLM + updateSLMListing(listing_uuid, listing_id, version_id, is_listed, count); + + return true; +} + +bool LLMarketplaceData::updateCountOnHand(const LLUUID& folder_id, S32 depth) +{ + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } + // Folder id can be the root of the listing or not so we need to retrieve the root first + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + S32 listing_id = getListingID(listing_uuid); + if (listing_id == 0) + { + // Listing doesn't exist -> exit with error + return false; + } + + // Compute the new count on hand + S32 count = compute_stock_count(folder_id); + + if (count == getCountOnHand(listing_uuid)) + { + // If count on hand is unchanged, no point spamming SLM with an update + return true; + } + else if (count == COMPUTE_STOCK_NOT_EVALUATED) + { + // If local count on hand is not known at that point, do *not* force an update to SLM + return false; + } + + // Get the unchanged values + bool is_listed = getActivationState(listing_uuid); + LLUUID version_uuid = getVersionFolder(listing_uuid); + + + // Post the listing update request to SLM + updateSLMListing(listing_uuid, listing_id, version_uuid, is_listed, count); + + // Force the local value as it prevents spamming (count update may occur in burst when restocking) + // Note that if SLM has a good reason to return a different value, it'll be updated by the responder + setCountOnHand(listing_uuid, count, false); + + return true; +} + +bool LLMarketplaceData::associateListing(const LLUUID& folder_id, const LLUUID& source_folder_id, S32 listing_id) +{ + if (isListed(folder_id)) + { + // Listing already exists -> exit with error + return false; + } + + // Get the version folder: if there is only one subfolder, we will set it as a version folder immediately + LLUUID version_id = getVersionFolderIfUnique(folder_id); + + // Post the listing associate request to SLM + associateSLMListing(folder_id, listing_id, version_id, source_folder_id); + + return true; +} + +// Methods privately called or called by SLM responders to perform changes +bool LLMarketplaceData::addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, const std::string& edit_url, S32 count) +{ + mMarketplaceItems[folder_id] = LLMarketplaceTuple(folder_id, listing_id, version_id, is_listed); + mMarketplaceItems[folder_id].mEditURL = edit_url; + mMarketplaceItems[folder_id].mCountOnHand = count; + if (version_id.notNull()) + { + mVersionFolders[version_id] = folder_id; + } + return true; +} + +bool LLMarketplaceData::deleteListing(const LLUUID& folder_id, bool update) +{ + LLUUID version_folder = getVersionFolder(folder_id); + + if (mMarketplaceItems.erase(folder_id) != 1) + { + return false; + } + mVersionFolders.erase(version_folder); + + if (update) + { + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + } + return true; +} + +bool LLMarketplaceData::deleteListing(S32 listing_id, bool update) +{ + if (listing_id == 0) + { + return false; + } + + LLUUID folder_id = getListingFolder(listing_id); + return deleteListing(folder_id, update); +} + +// Accessors +bool LLMarketplaceData::getActivationState(const LLUUID& folder_id) +{ + // Listing folder case + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it != mMarketplaceItems.end()) + { + return (it->second).mIsActive; + } + // Version folder case + version_folders_list_t::iterator it_version = mVersionFolders.find(folder_id); + if (it_version != mVersionFolders.end()) + { + marketplace_items_list_t::iterator it = mMarketplaceItems.find(it_version->second); + if (it != mMarketplaceItems.end()) + { + return (it->second).mIsActive; + } + } + return false; +} + +S32 LLMarketplaceData::getListingID(const LLUUID& folder_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it == mMarketplaceItems.end() ? 0 : (it->second).mListingId); +} + +S32 LLMarketplaceData::getCountOnHand(const LLUUID& folder_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it == mMarketplaceItems.end() ? -1 : (it->second).mCountOnHand); +} + +LLUUID LLMarketplaceData::getVersionFolder(const LLUUID& folder_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it == mMarketplaceItems.end() ? LLUUID::null : (it->second).mVersionFolderId); +} + +// Reverse lookup : find the listing folder id from the listing id +LLUUID LLMarketplaceData::getListingFolder(S32 listing_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.begin(); + while (it != mMarketplaceItems.end()) + { + if ((it->second).mListingId == listing_id) + { + return (it->second).mListingFolderId; + } + it++; + } + return LLUUID::null; +} + +std::string LLMarketplaceData::getListingURL(const LLUUID& folder_id, S32 depth) +{ + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + + } + + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + + marketplace_items_list_t::iterator it = mMarketplaceItems.find(listing_uuid); + return (it == mMarketplaceItems.end() ? "" : (it->second).mEditURL); +} + +bool LLMarketplaceData::isListed(const LLUUID& folder_id) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + return (it != mMarketplaceItems.end()); +} + +bool LLMarketplaceData::isListedAndActive(const LLUUID& folder_id) +{ + return (isListed(folder_id) && getActivationState(folder_id)); +} + +bool LLMarketplaceData::isVersionFolder(const LLUUID& folder_id) +{ + version_folders_list_t::iterator it = mVersionFolders.find(folder_id); + return (it != mVersionFolders.end()); +} + +bool LLMarketplaceData::isInActiveFolder(const LLUUID& obj_id, S32 depth) +{ + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(obj_id); + + } + + LLUUID listing_uuid = nested_parent_id(obj_id, depth); + bool active = getActivationState(listing_uuid); + LLUUID version_uuid = getVersionFolder(listing_uuid); + return (active && ((obj_id == version_uuid) || gInventory.isObjectDescendentOf(obj_id, version_uuid))); +} + +LLUUID LLMarketplaceData::getActiveFolder(const LLUUID& obj_id, S32 depth) +{ + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(obj_id); + + } + + LLUUID listing_uuid = nested_parent_id(obj_id, depth); + return (getActivationState(listing_uuid) ? getVersionFolder(listing_uuid) : LLUUID::null); +} + +bool LLMarketplaceData::isUpdating(const LLUUID& folder_id, S32 depth) +{ + // Evaluate the depth if it wasn't passed as a parameter + if (depth < 0) + { + depth = depth_nesting_in_marketplace(folder_id); + } + if ((depth <= 0) || (depth > 2)) + { + // Only listing and version folders though are concerned by that status + return false; + } + else + { + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + std::set::iterator it = mPendingUpdateSet.find(marketplace_listings_uuid); + if (it != mPendingUpdateSet.end()) + { + // If we're waiting for data for the marketplace listings root, we are in the updating process for all + return true; + } + else + { + // Check if the listing folder is waiting for data + LLUUID listing_uuid = nested_parent_id(folder_id, depth); + it = mPendingUpdateSet.find(listing_uuid); + return (it != mPendingUpdateSet.end()); } } } +void LLMarketplaceData::setUpdating(const LLUUID& folder_id, bool isUpdating) +{ + std::set::iterator it = mPendingUpdateSet.find(folder_id); + if (it != mPendingUpdateSet.end()) + { + mPendingUpdateSet.erase(it); + } + if (isUpdating) + { + mPendingUpdateSet.insert(folder_id); + } +} + +void LLMarketplaceData::setValidationWaiting(const LLUUID& folder_id, S32 count) +{ + mValidationWaitingList[folder_id] = count; +} + +void LLMarketplaceData::decrementValidationWaiting(const LLUUID& folder_id, S32 count) +{ + waiting_list_t::iterator found = mValidationWaitingList.find(folder_id); + if (found != mValidationWaitingList.end()) + { + found->second -= count; + if (found->second <= 0) + { + mValidationWaitingList.erase(found); + LLInventoryCategory *cat = gInventory.getCategory(folder_id); + validate_marketplacelistings(cat); + update_marketplace_category(folder_id); + gInventory.notifyObservers(); + } + } +} + +// Private Modifiers +bool LLMarketplaceData::setListingID(const LLUUID& folder_id, S32 listing_id, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + (it->second).mListingId = listing_id; + + if (update) + { + update_marketplace_category(folder_id, false); + gInventory.notifyObservers(); + } + return true; +} + +bool LLMarketplaceData::setCountOnHand(const LLUUID& folder_id, S32 count, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + (it->second).mCountOnHand = count; + + return true; +} + +bool LLMarketplaceData::setVersionFolderID(const LLUUID& folder_id, const LLUUID& version_id, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + LLUUID old_version_id = (it->second).mVersionFolderId; + if (old_version_id == version_id) + { + return false; + } + + (it->second).mVersionFolderId = version_id; + mVersionFolders.erase(old_version_id); + if (version_id.notNull()) + { + mVersionFolders[version_id] = folder_id; + } + + if (update) + { + update_marketplace_category(old_version_id, false); + update_marketplace_category(version_id, false); + gInventory.notifyObservers(); + } + return true; +} + +bool LLMarketplaceData::setActivationState(const LLUUID& folder_id, bool activate, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + (it->second).mIsActive = activate; + + if (update) + { + update_marketplace_category((it->second).mListingFolderId, false); + gInventory.notifyObservers(); + } + return true; +} + +bool LLMarketplaceData::setListingURL(const LLUUID& folder_id, const std::string& edit_url, bool update) +{ + marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id); + if (it == mMarketplaceItems.end()) + { + return false; + } + + (it->second).mEditURL = edit_url; + return true; +} + diff --git a/indra/newview/llmarketplacefunctions.h b/indra/newview/llmarketplacefunctions.h index d658775b9..8aa4dfad8 100644 --- a/indra/newview/llmarketplacefunctions.h +++ b/indra/newview/llmarketplacefunctions.h @@ -30,10 +30,7 @@ #include -#ifndef BOOST_FUNCTION_HPP_INCLUDED #include -#define BOOST_FUNCTION_HPP_INCLUDED -#endif #include #include "llsingleton.h" @@ -41,26 +38,24 @@ #include "llhttpstatuscodes.h" -LLSD getMarketplaceStringSubstitutions(); - namespace MarketplaceErrorCodes { enum eCode { - IMPORT_DONE = HTTP_OK, - IMPORT_PROCESSING = HTTP_ACCEPTED, - IMPORT_REDIRECT = HTTP_FOUND, - IMPORT_BAD_REQUEST = HTTP_BAD_REQUEST, - IMPORT_AUTHENTICATION_ERROR = HTTP_UNAUTHORIZED, - IMPORT_FORBIDDEN = HTTP_FORBIDDEN, - IMPORT_NOT_FOUND = HTTP_NOT_FOUND, - IMPORT_DONE_WITH_ERRORS = HTTP_CONFLICT, - IMPORT_JOB_FAILED = HTTP_GONE, + IMPORT_DONE = 200, + IMPORT_PROCESSING = 202, + IMPORT_REDIRECT = 302, + IMPORT_BAD_REQUEST = 400, + IMPORT_AUTHENTICATION_ERROR = 401, + IMPORT_FORBIDDEN = 403, + IMPORT_NOT_FOUND = 404, + IMPORT_DONE_WITH_ERRORS = 409, + IMPORT_JOB_FAILED = 410, IMPORT_JOB_LOW_SPEED = HTTP_INTERNAL_ERROR_LOW_SPEED, - IMPORT_JOB_TIMEOUT = HTTP_INTERNAL_ERROR_CURL_TIMEOUT, - IMPORT_SERVER_SITE_DOWN = HTTP_INTERNAL_SERVER_ERROR, - IMPORT_SERVER_API_DISABLED = HTTP_SERVICE_UNAVAILABLE, + IMPORT_JOB_TIMEOUT = 499, + IMPORT_SERVER_SITE_DOWN = 500, + IMPORT_SERVER_API_DISABLED = 503, }; } @@ -71,8 +66,21 @@ namespace MarketplaceStatusCodes MARKET_PLACE_NOT_INITIALIZED = 0, MARKET_PLACE_INITIALIZING = 1, MARKET_PLACE_CONNECTION_FAILURE = 2, - MARKET_PLACE_MERCHANT = 3, - MARKET_PLACE_NOT_MERCHANT = 4, + MARKET_PLACE_NOT_MERCHANT = 3, + MARKET_PLACE_MERCHANT = 4, + MARKET_PLACE_NOT_MIGRATED_MERCHANT = 5, + MARKET_PLACE_MIGRATED_MERCHANT = 6 + }; +} + +namespace MarketplaceFetchCodes +{ + enum sCode + { + MARKET_FETCH_NOT_DONE = 0, + MARKET_FETCH_LOADING = 1, + MARKET_FETCH_FAILED = 2, + MARKET_FETCH_DONE = 3 }; } @@ -114,6 +122,167 @@ private: }; +// Classes handling the data coming from and going to the Marketplace SLM Server DB: +// * implement the Marketplace API +// * cache the current Marketplace data (tuples) +// * provide methods to get Marketplace data on any inventory item +// * set Marketplace data +// * signal Marketplace updates to inventory +namespace SLMErrorCodes +{ + enum eCode + { + SLM_SUCCESS = 200, + SLM_RECORD_CREATED = 201, + SLM_MALFORMED_PAYLOAD = 400, + SLM_NOT_FOUND = 404, + }; +} + +class LLMarketplaceData; +class LLInventoryObserver; + +// A Marketplace item is known by its tuple +class LLMarketplaceTuple +{ +public: + friend class LLMarketplaceData; + + LLMarketplaceTuple(); + LLMarketplaceTuple(const LLUUID& folder_id); + LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed = false); + +private: + // Representation of a marketplace item in the Marketplace DB (well, what we know of it...) + LLUUID mListingFolderId; + S32 mListingId; + LLUUID mVersionFolderId; + bool mIsActive; + S32 mCountOnHand; + std::string mEditURL; +}; +// Notes: +// * The mListingFolderId is used as a key to this map. It could therefore be taken off the LLMarketplaceTuple objects themselves. +// * The SLM DB however uses mListingId as its primary key and it shows in its API. In the viewer though, the mListingFolderId is what we use to grab an inventory record. +typedef std::map marketplace_items_list_t; +typedef std::map version_folders_list_t; + +// Session cache of all Marketplace tuples +// Notes: +// * There's one and only one possible set of Marketplace dataset per agent and per session thus making it an LLSingleton +// * Some of those records might correspond to folders that do not exist in the inventory anymore. We do not clear them out though. They just won't show up in the UI. + +class LLSLMGetMerchantResponder; +class LLSLMGetListingsResponder; +class LLSLMCreateListingsResponder; +class LLSLMGetListingResponder; +class LLSLMUpdateListingsResponder; +class LLSLMAssociateListingsResponder; +class LLSLMDeleteListingsResponder; + +class LLMarketplaceData + : public LLSingleton +{ +public: + friend class LLSLMGetMerchantResponder; + friend class LLSLMGetListingsResponder; + friend class LLSLMCreateListingsResponder; + friend class LLSLMGetListingResponder; + friend class LLSLMUpdateListingsResponder; + friend class LLSLMAssociateListingsResponder; + friend class LLSLMDeleteListingsResponder; + + static LLSD getMarketplaceStringSubstitutions(); + LLMarketplaceData(); + virtual ~LLMarketplaceData(); + + // Public SLM API : Initialization and status + typedef boost::signals2::signal status_updated_signal_t; + void initializeSLM(const status_updated_signal_t::slot_type& cb); + U32 getSLMStatus() const { return mMarketPlaceStatus; } + void setSLMStatus(U32 status); + void getSLMListings(); + bool isEmpty() const { return (mMarketplaceItems.size() == 0); } + void setDataFetchedSignal(const status_updated_signal_t::slot_type& cb); + void setSLMDataFetched(U32 status); + U32 getSLMDataFetched() { return mMarketPlaceDataFetched; } + + // High level create/delete/set Marketplace data: each method returns true if the function succeeds, false if error + bool createListing(const LLUUID& folder_id); + bool activateListing(const LLUUID& folder_id, bool activate, S32 depth = -1); + bool clearListing(const LLUUID& folder_id, S32 depth = -1); + bool setVersionFolder(const LLUUID& folder_id, const LLUUID& version_id, S32 depth = -1); + bool associateListing(const LLUUID& folder_id, const LLUUID& source_folder_id, S32 listing_id); + bool updateCountOnHand(const LLUUID& folder_id, S32 depth = -1); + bool getListing(const LLUUID& folder_id, S32 depth = -1); + bool getListing(S32 listing_id); + bool deleteListing(S32 listing_id, bool update = true); + + // Probe the Marketplace data set to identify folders + bool isListed(const LLUUID& folder_id); // returns true if folder_id is a Listing folder + bool isListedAndActive(const LLUUID& folder_id); // returns true if folder_id is an active (listed) Listing folder + bool isVersionFolder(const LLUUID& folder_id); // returns true if folder_id is a Version folder + bool isInActiveFolder(const LLUUID& obj_id, S32 depth = -1); // returns true if the obj_id is buried in an active version folder + LLUUID getActiveFolder(const LLUUID& obj_id, S32 depth = -1); // returns the UUID of the active version folder obj_id is in + bool isUpdating(const LLUUID& folder_id, S32 depth = -1); // returns true if we're waiting from SLM incoming data for folder_id + + // Access Marketplace data set : each method returns a default value if the argument can't be found + bool getActivationState(const LLUUID& folder_id); + S32 getListingID(const LLUUID& folder_id); + LLUUID getVersionFolder(const LLUUID& folder_id); + std::string getListingURL(const LLUUID& folder_id, S32 depth = -1); + LLUUID getListingFolder(S32 listing_id); + S32 getCountOnHand(const LLUUID& folder_id); + + // Used to flag if stock count values for Marketplace have to be updated + bool checkDirtyCount() { if (mDirtyCount) { mDirtyCount = false; return true; } else { return false; } } + void setDirtyCount() { mDirtyCount = true; } + void setUpdating(const LLUUID& folder_id, bool isUpdating); + + // Used to decide when to run a validation on listing folders + void setValidationWaiting(const LLUUID& folder_id, S32 count); + void decrementValidationWaiting(const LLUUID& folder_id, S32 count = 1); + +private: + // Modify Marketplace data set : each method returns true if the function succeeds, false if error + // Used internally only by SLM Responders when data are received from the SLM Server + bool addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, const std::string& edit_url, S32 count); + bool deleteListing(const LLUUID& folder_id, bool update = true); + bool setListingID(const LLUUID& folder_id, S32 listing_id, bool update = true); + bool setVersionFolderID(const LLUUID& folder_id, const LLUUID& version_id, bool update = true); + bool setActivationState(const LLUUID& folder_id, bool activate, bool update = true); + bool setListingURL(const LLUUID& folder_id, const std::string& edit_url, bool update = true); + bool setCountOnHand(const LLUUID& folder_id, S32 count, bool update = true); + + // Private SLM API : package data and get/post/put requests to the SLM Server through the SLM API + void createSLMListing(const LLUUID& folder_id, const LLUUID& version_id, S32 count); + void getSLMListing(S32 listing_id); + void updateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed, S32 count); + void associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, const LLUUID& source_folder_id); + void deleteSLMListing(S32 listing_id); + std::string getSLMConnectURL(const std::string& route); + + // Handling Marketplace connection and inventory connection + U32 mMarketPlaceStatus; + status_updated_signal_t* mStatusUpdatedSignal; + LLInventoryObserver* mInventoryObserver; + bool mDirtyCount; // If true, stock count value need to be updated at the next check + + // Update data + U32 mMarketPlaceDataFetched; + status_updated_signal_t* mDataFetchedSignal; + std::set mPendingUpdateSet; + + // Listing folders waiting for validation + typedef std::map waiting_list_t; + waiting_list_t mValidationWaitingList; + + // The cache of SLM data (at last...) + marketplace_items_list_t mMarketplaceItems; + // We need a list (version folder -> listing folder) because such reverse lookups are frequent + version_folders_list_t mVersionFolders; +}; + #endif // LL_LLMARKETPLACEFUNCTIONS_H diff --git a/indra/newview/llmenucommands.cpp b/indra/newview/llmenucommands.cpp index 28519fa0b..2246deaff 100644 --- a/indra/newview/llmenucommands.cpp +++ b/indra/newview/llmenucommands.cpp @@ -76,12 +76,12 @@ #include "llfloaterland.h" #include "llfloaterlandholdings.h" #include "llfloatermap.h" +#include "llfloatermarketplacelistings.h" #include "llfloatermediafilter.h" #include "llfloatermemleak.h" #include "llfloatermessagelog.h" #include "llfloatermute.h" #include "llfloaternotificationsconsole.h" -#include "llfloateroutbox.h" #include "llfloaterpathfindingcharacters.h" #include "llfloaterpathfindinglinksets.h" #include "llfloaterperms.h" @@ -165,7 +165,6 @@ struct MenuFloaterDict : public LLSingleton registerFloater("about", boost::bind(&LLFloaterAbout::show,(void*)NULL)); registerFloater("always run", boost::bind(toggle_always_run), boost::bind(&LLAgent::getAlwaysRun, &gAgent)); registerFloater("anims_explorer", boost::bind(LLFloaterExploreAnimations::show)); - registerFloater("ao", boost::bind(LLFloaterAO::show, (void*)NULL)); registerFloater("appearance", boost::bind(LLFloaterCustomize::show)); registerFloater("asset_blacklist", boost::bind(LLFloaterBlacklist::toggle), boost::bind(LLFloaterBlacklist::visible)); registerFloater("build", boost::bind(toggle_build)); @@ -194,7 +193,6 @@ struct MenuFloaterDict : public LLSingleton registerFloater("mouselook", boost::bind(toggle_mouselook)); registerFloater("my land", boost::bind(LLFloaterLandHoldings::show, (void*)NULL)); registerFloater("outfit", boost::bind(show_outfit_dialog)); - registerFloater("PostProcess", boost::bind(LLFloaterPostProcess::show)); registerFloater("preferences", boost::bind(LLFloaterPreference::show, (void*)NULL)); registerFloater("quit", boost::bind(&LLAppViewer::userQuit, LLAppViewer::instance())); registerFloater("RegionDebugConsole", boost::bind(handle_singleton_toggle, (void*)NULL), boost::bind(LLFloaterRegionDebugConsole::instanceExists)); @@ -213,6 +211,7 @@ struct MenuFloaterDict : public LLSingleton registerFloater ("about land"); registerFloater ("about region"); registerFloater ("active speakers"); + registerFloater ("ao"); registerFloater ("areasearch"); registerFloater ("autoreplace"); registerFloater ("avatar"); @@ -230,13 +229,14 @@ struct MenuFloaterDict : public LLSingleton registerFloater ("joystick"); registerFloater ("media filter"); registerFloater ("mini map"); + registerFloater ("marketplace_listings"); registerFloater ("movement controls"); registerFloater ("mute list"); registerFloater ("notifications console"); - registerFloater ("outbox"); registerFloater ("pathfinding_characters"); registerFloater ("pathfinding_linksets"); registerFloater ("perm prefs"); + registerFloater ("PostProcess"); registerFloater ("radar"); registerFloater ("region_tracker"); registerFloater ("script info"); diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index ca015e283..28303be2c 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -2591,7 +2591,8 @@ void LLMeshRepository::notifyLoadedMeshes() if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) { region_name = gAgent.getRegion()->getName(); - mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh2"); + const std::string mesh_cap(gAgent.getRegion()->getViewerAssetUrl()); + mGetMeshCapability = !mesh_cap.empty() ? mesh_cap : gAgent.getRegion()->getCapability("GetMesh2"); if (mGetMeshCapability.empty()) { mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); diff --git a/indra/newview/llnotify.cpp b/indra/newview/llnotify.cpp index d60d53275..a223030b0 100644 --- a/indra/newview/llnotify.cpp +++ b/indra/newview/llnotify.cpp @@ -191,29 +191,12 @@ LLNotifyBox::LLNotifyBox(LLNotificationPtr notification) setBackgroundVisible(FALSE); setBackgroundOpaque(TRUE); - LLIconCtrl* icon; - LLTextEditor* text; - const S32 TOP = getRect().getHeight() - (mIsTip ? (S32)sFont->getLineHeight() : 32); const S32 BOTTOM = (S32)sFont->getLineHeight(); S32 x = HPAD + HPAD; S32 y = TOP; - if (mIsTip) - { - // use the tip notification icon - icon = new LLIconCtrl(std::string("icon"), LLRect(x, y, x+32, TOP-32), std::string("notify_tip_icon.tga")); - } - else if (mIsCaution) - { - // use the caution notification icon - icon = new LLIconCtrl(std::string("icon"), LLRect(x, y, x+32, TOP-32), std::string("notify_caution_icon.tga")); - } - else - { - // use the default notification icon - icon = new LLIconCtrl(std::string("icon"), LLRect(x, y, x+32, TOP-32), std::string("notify_box_icon.tga")); - } + auto icon = new LLIconCtrl(std::string("icon"), LLRect(x, y, x+32, TOP-32), mIsTip ? "notify_tip_icon.tga" : mIsCaution ? "notify_caution_icon.tga" : "notify_box_icon.tga"); icon->setMouseOpaque(FALSE); addChild(icon); @@ -221,11 +204,10 @@ LLNotifyBox::LLNotifyBox(LLNotificationPtr notification) x += HPAD + HPAD + 32; // add a caution textbox at the top of a caution notification - LLTextBox* caution_box = NULL; if (mIsCaution && !mIsTip) { S32 caution_height = ((S32)sFont->getLineHeight() * 2) + VPAD; - caution_box = new LLTextBox( + auto caution_box = new LLTextBox( std::string("caution_box"), LLRect(x, y, getRect().getWidth() - 2, caution_height), LLStringUtil::null, @@ -244,58 +226,30 @@ LLNotifyBox::LLNotifyBox(LLNotificationPtr notification) // it appears below the caution textbox y = y - caution_height; } - else if (mIsCaution && mIsTip) + else { - const S32 BTN_TOP = BOTTOM_PAD + (((mNumOptions-1+2)/3)) * (BTN_HEIGHT+VPAD); // Tokenization on \n is handled by LLTextBox const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; // For script dialogs: add space for title. - text = new LLTextEditor(std::string("box"), LLRect(x, y, getRect().getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16), MAX_LENGTH, message, sFont, FALSE); + auto text = new LLTextEditor(std::string("box"), LLRect(x, y, getRect().getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16), MAX_LENGTH, LLStringUtil::null, sFont, FALSE, true); text->setWordWrap(TRUE); - text->setTabStop(FALSE); - text->setMouseOpaque(FALSE); + text->setMouseOpaque(TRUE); text->setBorderVisible(FALSE); - text->setTakesNonScrollClicks(FALSE); + text->setTakesNonScrollClicks(TRUE); text->setHideScrollbarForShortDocs(TRUE); text->setReadOnlyBgColor ( LLColor4::transparent ); // the background color of the box is manually // rendered under the text box, therefore we want // the actual text box to be transparent - text->setReadOnlyFgColor ( gColors.getColor("NotifyCautionWarnColor") ); //sets caution text color for tip notifications - text->setEnabled(FALSE); // makes it read-only - text->setTabStop(FALSE); // can't tab to it (may be a problem for scrolling via keyboard) - addChild(text); - } - else - { - const S32 BTN_TOP = BOTTOM_PAD + (((mNumOptions-1+2)/3)) * (BTN_HEIGHT+VPAD); - // Tokenization on \n is handled by LLTextBox - const S32 MAX_LENGTH = 512 + 20 + - DB_FIRST_NAME_BUF_SIZE + - DB_LAST_NAME_BUF_SIZE + - DB_INV_ITEM_NAME_BUF_SIZE; // For script dialogs: add space for title. - - text = new LLTextEditor(std::string("box"), - LLRect(x, y, getRect().getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16), - MAX_LENGTH, - message, - sFont, - FALSE); - text->setWordWrap(TRUE); - text->setTabStop(FALSE); - text->setMouseOpaque(FALSE); - text->setBorderVisible(FALSE); - text->setTakesNonScrollClicks(FALSE); - text->setHideScrollbarForShortDocs(TRUE); - text->setReadOnlyBgColor ( LLColor4::transparent ); // the background color of the box is manually - // rendered under the text box, therefore we want - // the actual text box to be transparent - text->setReadOnlyFgColor ( gColors.getColor("NotifyTextColor") ); - text->setEnabled(FALSE); // makes it read-only + auto text_color = gColors.getColor(mIsCaution && mIsTip ? "NotifyCautionWarnColor" : "NotifyTextColor"); + text->setReadOnlyFgColor(text_color); //sets caution text color for tip notifications + if (!mIsCaution) // We could do some extra color math here to determine if bg's too close to link color, but let's just cross with the link color instead + text->setLinkColor(new LLColor4(lerp(text_color, gSavedSettings.getColor4("HTMLLinkColor"), 0.4f))); text->setTabStop(FALSE); // can't tab to it (may be a problem for scrolling via keyboard) + text->appendText(message,false,false,nullptr,!layout_script_dialog); // Now we can set the text, since colors have been set. addChild(text); } @@ -486,13 +440,9 @@ BOOL LLNotifyBox::handleMouseUp(S32 x, S32 y, MASK mask) // virtual BOOL LLNotifyBox::handleRightMouseDown(S32 x, S32 y, MASK mask) { - if (!mIsTip) - { - moveToBack(true); - return TRUE; - } - - return LLPanel::handleRightMouseDown(x, y, mask); + bool done = LLPanel::handleRightMouseDown(x, y, mask); + if (!done && !mIsTip) moveToBack(true); + return done || !mIsTip; } diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index a689b07af..16b224bd9 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -70,6 +70,9 @@ #include #include + +#include "hippogridmanager.h" // Include Gridmanager for OpenSim Support in profiles, provides ability for the helpbutton to redirect to GRID Websites Help page + // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] @@ -299,11 +302,15 @@ void LLPanelAvatarSecondLife::onDoubleClickGroup() LLGroupActions::show(item->getUUID()); } -// static +// static - Not anymore :P bool LLPanelAvatarSecondLife::onClickPartnerHelpLoadURL(const LLSD& notification, const LLSD& response) { if (!LLNotification::getSelectedOption(notification, response)) - LLWeb::loadURL("http://secondlife.com/partner"); + { + const auto& grid = *gHippoGridManager->getConnectedGrid(); + const std::string url = grid.isSecondLife() ? "http://secondlife.com/partner" : grid.getPartnerUrl(); + if (!url.empty()) LLWeb::loadURL(url); + } return false; } @@ -380,21 +387,32 @@ BOOL LLPanelAvatarSecondLife::postBuild() LLTextureCtrl* ctrl = getChild("img"); ctrl->setFallbackImageName("default_profile_picture.j2c"); + auto show_pic = [&] + { + show_picture(getChild("img")->getImageAssetID(), profile_picture_title(getChildView("dnname")->getValue())); + }; + auto show_pic_if_not_self = [=] { if (!ctrl->canChange()) show_pic(); }; - getChild("bigimg")->setCommitCallback(boost::bind(boost::bind(show_picture, boost::bind(&LLTextureCtrl::getImageAssetID, ctrl), boost::bind(profile_picture_title, boost::bind(&LLView::getValue, getChild("dnname")))))); + ctrl->setMouseUpCallback(std::bind(show_pic_if_not_self)); + getChild("bigimg")->setCommitCallback(std::bind(show_pic)); return TRUE; } BOOL LLPanelAvatarFirstLife::postBuild() { - BOOL own_avatar = (getPanelAvatar()->getAvatarID() == gAgent.getID() ); - enableControls(own_avatar); + enableControls(getPanelAvatar()->getAvatarID() == gAgentID); LLTextureCtrl* ctrl = getChild("img"); ctrl->setFallbackImageName("default_profile_picture.j2c"); + auto show_pic = [&] + { + show_picture(getChild("img")->getImageAssetID(), "First Life Picture"); + }; + auto show_pic_if_not_self = [=] { if (!ctrl->canChange()) show_pic(); }; - getChild("flbigimg")->setCommitCallback(boost::bind(boost::bind(boost::bind(show_picture, boost::bind(&LLTextureCtrl::getImageAssetID, ctrl), "First Life Picture")))); + ctrl->setMouseUpCallback(std::bind(show_pic_if_not_self)); + getChild("flbigimg")->setCommitCallback(std::bind(show_pic)); return TRUE; } @@ -1445,7 +1463,9 @@ void LLPanelAvatar::sendAvatarNotesUpdate() notes == mLastNotes) // Avatar notes unchanged return; - LLAvatarPropertiesProcessor::instance().sendNotes(mAvatarID, notes); + auto& inst(LLAvatarPropertiesProcessor::instance()); + inst.sendNotes(mAvatarID, notes); + inst.sendAvatarNotesRequest(mAvatarID); // Rerequest notes to update anyone that might be listening, also to be sure we match the server. } // virtual @@ -1481,8 +1501,9 @@ void LLPanelAvatar::processProperties(void* data, EAvatarProcessorType type) const LLAvatarNotes* pAvatarNotes = static_cast( data ); if (pAvatarNotes && (mAvatarID == pAvatarNotes->target_id) && (pAvatarNotes->target_id != LLUUID::null)) { - childSetValue("notes edit", pAvatarNotes->notes); - childSetEnabled("notes edit", true); + auto notes = getChildView("notes edit"); + notes->setEnabled(true); + notes->setValue(pAvatarNotes->notes); mHaveNotes = true; mLastNotes = pAvatarNotes->notes; } diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index ad132c6a9..e68b9ae26 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -511,12 +511,8 @@ void for_each_picker_ctrl_entry(LLPanel* panel, LLWearableType::EType type, func return; } const texture_vec_t& indexes = get_pickers_indexes(wearable_entry); - for (texture_vec_t::const_iterator - iter = indexes.begin(), - iter_end = indexes.end(); - iter != iter_end; ++iter) + for (const auto& te : indexes) { - const ETextureIndex te = *iter; const LLEditWearableDictionary::PickerControlEntry* entry = get_picker_entry(te); if (!entry) { @@ -684,6 +680,16 @@ LLPanelEditWearable::~LLPanelEditWearable() } } +static std::string getTabName(U32 i) { return llformat("%i", i); } +void LLPanelEditWearable::addLayerTabs(U32 index, U32 last) +{ + for(U32 i = index; i <= last; ++i) + { + const auto number = getTabName(i); + mTab->addTabPanel(new LLPanel(number), "Layer " + number); + } +} + BOOL LLPanelEditWearable::postBuild() { std::string icon_name = LLInventoryIcon::getIconName(LLWearableType::getAssetType( mType ),LLInventoryType::IT_WEARABLE,mType,FALSE); @@ -691,15 +697,22 @@ BOOL LLPanelEditWearable::postBuild() getChildView("icon")->setValue(icon_name); mCreateNew = getChild("Create New"); - mCreateNew->setCommitCallback(boost::bind(&LLPanelEditWearable::onBtnCreateNew, this) ); + mCreateNew->setCommitCallback(boost::bind(&LLPanelEditWearable::onBtnCreateNew, this)); + + mCreateNewLayer = getChild("New Layer"); + mCreateNewLayer->setCommitCallback(boost::bind(&LLPanelEditWearable::onBtnCreateNew, this)); mTakeOff = getChild("Take Off"); // If PG, can't take off underclothing or shirt - mCanTakeOff = - LLWearableType::getAssetType( mType ) == LLAssetType::AT_CLOTHING && - !( gAgent.isTeen() && (mType == LLWearableType::WT_UNDERSHIRT || mType == LLWearableType::WT_UNDERPANTS) ); + mCanTakeOff = !(gAgent.isTeen() && (mType == LLWearableType::WT_UNDERSHIRT || mType == LLWearableType::WT_UNDERPANTS) ); mTakeOff->setVisible(mCanTakeOff); - mTakeOff->setCommitCallback(boost::bind(&LLPanelEditWearable::onBtnTakeOff, this) ); + mTakeOff->setCommitCallback(boost::bind(&LLPanelEditWearable::onBtnTakeOff, this)); + + mArrowLeft = getChild("Arrow Left"); + mArrowLeft->setCommitCallback(boost::bind(&LLPanelEditWearable::onMoveToLayer, this, true)); + mArrowRight = getChild("Arrow Right"); + mArrowRight->setCommitCallback(boost::bind(&LLPanelEditWearable::onMoveToLayer, this, false)); + if (mSexRadio = findChild("sex radio")) { @@ -707,7 +720,7 @@ BOOL LLPanelEditWearable::postBuild() } mSave = getChild("Save"); - mSave->setCommitCallback(boost::bind(&LLPanelEditWearable::saveChanges, this, false, std::string()) ); + mSave->setCommitCallback(boost::bind(&LLPanelEditWearable::saveChanges, this, false, LLStringUtil::null) ); mSaveAs = getChild("Save As"); mSaveAs->setCommitCallback(boost::bind(&LLPanelEditWearable::onBtnSaveAs, this) ); @@ -774,12 +787,7 @@ BOOL LLPanelEditWearable::postBuild() if (mTab = findChild("layer_tabs")) { - LL_COMPILE_TIME_MESSAGE("layer_tabs needs re-implemented"); - for(U32 i = 1; i <= 6/*LLAgentWearables::MAX_CLOTHING_PER_TYPE*/; ++i) - { - LLPanel* new_panel = new LLPanel(llformat("%i",i)); - mTab->addTabPanel(new_panel, llformat("Layer %i",i)); - } + addLayerTabs(1, gAgentWearables.getWearableCount(mType)); mTab->setCommitCallback(boost::bind(&LLPanelEditWearable::onTabChanged, this, _1)); mTab->setValidateCallback(boost::bind(&LLPanelEditWearable::onTabPrecommit, this)); } @@ -805,13 +813,12 @@ void LLPanelEditWearable::draw() refreshWearables(false); LLViewerWearable* wearable = getWearable(); - BOOL has_wearable = (wearable != NULL ); - BOOL has_any_wearable = has_wearable || gAgentWearables.getWearableCount(mType); + BOOL has_wearable = wearable != nullptr; BOOL is_dirty = isDirty(); BOOL is_modifiable = FALSE; BOOL is_copyable = FALSE; BOOL is_complete = FALSE; - LLInventoryItem* item = NULL; + LLInventoryItem* item = nullptr; if (wearable && (item = gInventory.getItem(wearable->getItemID()))) { const LLPermissions& perm = item->getPermissions(); @@ -823,17 +830,8 @@ void LLPanelEditWearable::draw() mSave->setEnabled(has_wearable && is_modifiable && is_complete && is_dirty); mSaveAs->setEnabled(has_wearable && is_copyable && is_complete); mRevert->setEnabled(has_wearable && is_dirty ); - mTakeOff->setEnabled(has_wearable); - if (mCanTakeOff) mTakeOff->setVisible(has_wearable); - mCreateNew->setVisible(!has_any_wearable); - mNotWornI->setVisible(!has_any_wearable); - mNotWornT->setVisible(!has_any_wearable); - mNoModI->setVisible(has_wearable && !is_modifiable); - mNoModT->setVisible(has_wearable && !is_modifiable); mTitle->setVisible(has_wearable && is_modifiable && is_complete); - mTitleLoading->setVisible(has_wearable ? is_modifiable && !is_complete : has_any_wearable); - mPath->setVisible(has_wearable); - mSquare->setVisible(has_wearable && !is_modifiable); //lock icon + mTitleLoading->setVisible(has_wearable && is_modifiable && !is_complete); if (has_wearable && is_modifiable) { @@ -924,7 +922,7 @@ void LLPanelEditWearable::onTabChanged(LLUICtrl* ctrl) if (mPendingWearable) return; U32 tab_index = ((LLTabContainer*)ctrl)->getCurrentPanelIndex(); - U32 wearable_index = gAgentWearables.getWearableCount(mType) - tab_index - 1; + U32 wearable_index = tab_index ; if (wearable_index != mCurrentIndex ) { setWearableIndex(wearable_index); @@ -945,25 +943,17 @@ void LLPanelEditWearable::setWearableIndex(S32 index) if (mTab) { - U32 tab_index = gAgentWearables.getWearableCount(mType) - index - 1; - - if (mTab->getCurrentPanelIndex() != tab_index) - mTab->selectTab(tab_index); + if (mTab->getTabCount() && mTab->getCurrentPanelIndex() != index) + mTab->selectTab(index); } + LLViewerWearable* wearable = gAgentWearables.getViewerWearable(mType,mCurrentIndex); // Singu note: Set title even if the wearable didn't change: the name might have changed (when renamed). - if (wearable) - { - mTitle->setTextArg("[DESC]", wearable->getName()); - mNoModT->setTextArg("[DESC]", wearable->getName()); - } - else - { - mTitle->setTextArg("[DESC]", std::string(LLWearableType::getTypeLabel(mType))); - mNoModT->setTextArg("[DESC]", std::string(LLWearableType::getTypeLabel(mType))); - } + const auto& desc = wearable ? wearable->getName() : LLWearableType::getTypeLabel(mType); + mTitle->setTextArg("[DESC]", desc); + mNoModT->setTextArg("[DESC]", desc); if (wearable == getWearable()) return; @@ -999,7 +989,6 @@ void LLPanelEditWearable::setWearableIndex(S32 index) } updateScrollingPanelList(); - } void LLPanelEditWearable::refreshWearables(bool force_immediate) @@ -1028,10 +1017,22 @@ void LLPanelEditWearable::refreshWearables(bool force_immediate) if (mTab) { - LL_COMPILE_TIME_MESSAGE("layer_tabs needs re-implemented"); - for(U32 i = 0; i < 6/*LLAgentWearables::MAX_CLOTHING_PER_TYPE*/; ++i) + S32 layer_count = gAgentWearables.getWearableCount(mType); + S32 tab_count = mTab->getTabCount(); + if (tab_count > layer_count) // Remove some tabs { - mTab->enableTabButton(i, i < gAgentWearables.getWearableCount(mType)); + while (tab_count && tab_count > layer_count) + { + if (auto tab = mTab->getChild(getTabName(tab_count--), false, false)) + { + mTab->removeTabPanel(tab); + delete tab; + } + } + } + else if (layer_count > tab_count) // Add some tabs + { + addLayerTabs(tab_count+1, layer_count); } } setWearableIndex(index); @@ -1078,7 +1079,7 @@ void LLPanelEditWearable::onCommitSexChange() return; } - bool is_new_sex_male = (gSavedSettings.getU32("AvatarSex") ? SEX_MALE : SEX_FEMALE) == SEX_MALE; + bool is_new_sex_male = gSavedSettings.getU32("AvatarSex") ? true : false; LLViewerWearable* wearable = gAgentWearables.getViewerWearable(type, index); if (wearable) { @@ -1116,7 +1117,7 @@ bool LLPanelEditWearable::onSelectAutoWearOption(const LLSD& notification, const // Only auto wear the new item if the AutoWearNewClothing checkbox is selected. LLPointer cb = option == 0 ? - new LLBoostFuncInventoryCallback(boost::bind(&wear_on_avatar_cb,_1,false)) : NULL; + new LLBoostFuncInventoryCallback(boost::bind(&wear_on_avatar_cb,_1,false)) : nullptr; create_inventory_item(gAgent.getID(), gAgent.getSessionID(), folder_id, wearable->getTransactionID(), wearable->getName(), wearable->getDescription(), asset_type, LLInventoryType::IT_WEARABLE, wearable->getType(), @@ -1125,9 +1126,27 @@ bool LLPanelEditWearable::onSelectAutoWearOption(const LLSD& notification, const return false; } +void LLPanelEditWearable::onMoveToLayer(bool closer) +{ + const auto wearable = getWearable(); + auto& appearance_mgr(LLAppearanceMgr::instance()); + auto links = appearance_mgr.findCOFItemLinks(wearable->getItemID()); + if (links.empty()) return; + auto link = links.front(); + if (gAgentWearables.moveWearable(link, closer)) + { + gAgentAvatarp->wearableUpdated(mType, true); + /* Singu TODO: Figure out how to maintain focus on the current tab + U32 index(0); + gAgentWearables.getWearableIndex(wearable, index); + setWearableIndex(index); + */ + } +} + LLViewerWearable* LLPanelEditWearable::getWearable() const { - return mCurrentWearable;//gAgentWearables.getWearable(mType, mCurrentIndex); // TODO: MULTI-WEARABLE + return mCurrentWearable; } U32 LLPanelEditWearable::getIndex() const @@ -1445,23 +1464,46 @@ void LLPanelEditWearable::updateScrollingPanelList() void LLPanelEditWearable::updateScrollingPanelUI() { LLViewerWearable* wearable = getWearable(); - // do nothing if we don't have a valid wearable we're editing - if (!wearable) - { - return; - } - - LL_INFOS() << llformat("%#.8lX",wearable) << LL_ENDL; - LL_INFOS() << "cur_wearable->isDirty()=" << wearable->isDirty() << LL_ENDL; - LLViewerInventoryItem* item = gInventory.getItem(wearable->getItemID()); + BOOL is_modifiable = FALSE; + BOOL is_copyable = FALSE; + LLViewerInventoryItem* item = wearable ? gInventory.getItem(wearable->getItemID()) : nullptr; if (item) { - U32 perm_mask = item->getPermissions().getMaskOwner(); BOOL is_complete = item->isComplete(); LLScrollingPanelParam::sUpdateDelayFrames = 0; - mCustomizeFloater->getScrollingPanelList()->updatePanels((perm_mask & PERM_MODIFY) && is_complete); + const LLPermissions& perm = item->getPermissions(); + const auto& group_id(gAgent.getGroupID()); + is_modifiable = perm.allowModifyBy(gAgentID, group_id); + is_copyable = perm.allowCopyBy(gAgentID, group_id); + mCustomizeFloater->getScrollingPanelList()->updatePanels(is_modifiable && is_complete); } + + // Update some UI here instead of the draw call + bool has_wearable = wearable != nullptr; + bool max_layers = gAgentWearables.getClothingLayerCount() == LLAgentWearables::MAX_CLOTHING_LAYERS; + bool show_create_new = !has_wearable && !max_layers; + + mTakeOff->setEnabled(has_wearable); + if (mCanTakeOff) mTakeOff->setVisible(has_wearable); + mCreateNewLayer->setVisible(has_wearable && !max_layers); + mArrowLeft->setEnabled(has_wearable && gAgentWearables.getBottomWearable(mType) != wearable); + mArrowLeft->setVisible(has_wearable); + mArrowRight->setEnabled(has_wearable && gAgentWearables.getTopWearable(mType) != wearable); + mArrowRight->setVisible(has_wearable); + mCreateNew->setVisible(show_create_new); + mNotWornI->setVisible(show_create_new); + mNotWornT->setVisible(show_create_new); + mNoModI->setVisible(has_wearable && !is_modifiable); + mNoModT->setVisible(has_wearable && !is_modifiable); + mPath->setVisible(has_wearable); + mSquare->setVisible(has_wearable && !is_modifiable); //lock icon + + // do nothing else if we don't have a valid wearable we're editing + if (!wearable) return; + LL_INFOS() << llformat("%#.8lX", wearable) << LL_ENDL; + LL_INFOS() << "cur_wearable->isDirty()=" << wearable->isDirty() << LL_ENDL; + refreshWearables(false); } void LLPanelEditWearable::onBtnTakeOff() diff --git a/indra/newview/llpaneleditwearable.h b/indra/newview/llpaneleditwearable.h index 4bf1cc75e..a6da8c73c 100644 --- a/indra/newview/llpaneleditwearable.h +++ b/indra/newview/llpaneleditwearable.h @@ -56,6 +56,7 @@ public: LLPanelEditWearable( LLWearableType::EType type, LLFloaterCustomize* parent ); virtual ~LLPanelEditWearable(); + void addLayerTabs(U32 index, U32 last); /*virtual*/ BOOL postBuild(); /*virtual*/ BOOL isDirty() const; // LLUICtrl /*virtual*/ void draw(); @@ -108,6 +109,8 @@ public: void onBtnCreateNew(); static bool onSelectAutoWearOption(const LLSD& notification, const LLSD& response); + void onMoveToLayer(bool closer); + void onColorSwatchCommit(const LLUICtrl*); void onTexturePickerCommit(const LLUICtrl*); void setNewImageID(ETextureIndex te_index, LLUUID const& uuid); //Singu note: this used to be part of onTexturePickerCommit. @@ -137,7 +140,7 @@ private: //a bazillion pointless (and not particularly valid) times. Deferring to draw effectively sorts it all out. // Cached UI - LLUICtrl *mCreateNew, *mTakeOff, *mSexRadio, *mSave, *mSaveAs, *mRevert, *mNotWornT, *mNoModT, *mTitle, *mTitleLoading, *mPath, *mAvHeight; + LLUICtrl *mCreateNew, *mCreateNewLayer, *mTakeOff, *mArrowLeft, *mArrowRight, *mSexRadio, *mSave, *mSaveAs, *mRevert, *mNotWornT, *mNoModT, *mTitle, *mTitleLoading, *mPath, *mAvHeight; LLView *mNotWornI, *mNoModI, *mSquare; LLTabContainer* mTab; std::vector mSubpartBtns; diff --git a/indra/newview/llpanelevent.cpp b/indra/newview/llpanelevent.cpp index 5295d97b8..21efbafd9 100644 --- a/indra/newview/llpanelevent.cpp +++ b/indra/newview/llpanelevent.cpp @@ -54,6 +54,8 @@ #include "llweb.h" #include "llworldmap.h" #include "lluictrlfactory.h" +#include "hippogridmanager.h" +#include "lfsimfeaturehandler.h" //static std::list LLPanelEvent::sAllPanels; @@ -69,6 +71,10 @@ LLPanelEvent::~LLPanelEvent() sAllPanels.remove(this); } +void enable_create(const std::string& url, LLView* btn) +{ + btn->setEnabled(!url.empty()); +} BOOL LLPanelEvent::postBuild() { @@ -97,8 +103,15 @@ BOOL LLPanelEvent::postBuild() mNotifyBtn = getChild( "notify_btn"); mNotifyBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickNotify,this)); - mCreateEventBtn = getChild( "create_event_btn"); - mCreateEventBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickCreateEvent,this)); + mCreateEventBtn = getChild("create_event_btn"); + mCreateEventBtn->setClickedCallback(boost::bind(&LLPanelEvent::onClickCreateEvent, this)); + if (!gHippoGridManager->getConnectedGrid()->isSecondLife()) + { + auto& inst(LFSimFeatureHandler::instance()); + mCreateEventBtn->setEnabled(!inst.getEventsURL().empty()); + inst.setEventsURLCallback(boost::bind(enable_create, _1, mCreateEventBtn)); + } + return TRUE; } @@ -286,8 +299,8 @@ bool LLPanelEvent::callbackCreateEventWebPage(const LLSD& notification, const LL if (0 == option) { LL_INFOS() << "Loading events page " << EVENTS_URL << LL_ENDL; - - LLWeb::loadURL(EVENTS_URL); + const std::string& opensim_events = LFSimFeatureHandler::instance().getEventsURL(); + LLWeb::loadURL(opensim_events.empty() ? EVENTS_URL : opensim_events); } return false; } diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 94047f771..c09f96dcf 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -130,7 +130,10 @@ BOOL LLPanelGroupGeneral::postBuild() mInsignia->setCommitCallback(boost::bind(&LLPanelGroupGeneral::onCommitAny,this)); mDefaultIconID = mInsignia->getImageAssetID(); void show_picture(const LLUUID& id, const std::string& name); - getChild("bigimg")->setCommitCallback(boost::bind(boost::bind(show_picture, boost::bind(&LLTextureCtrl::getImageAssetID, mInsignia), "Group Insignia"))); + auto show_pic = [this] { show_picture(mInsignia->getImageAssetID(), "Group Insignia"); }; + auto show_pic_if_not_self = [=] { if (!mInsignia->canChange()) show_pic(); }; + mInsignia->setMouseUpCallback(std::bind(show_pic_if_not_self)); + getChild("bigimg")->setCommitCallback(std::bind(show_pic)); } mEditCharter = getChild("charter", recurse); diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index e51341416..5ab21d449 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -86,6 +86,8 @@ #include // #include + +#include "llsdserialize.h" #include "llstring.h" #include @@ -103,7 +105,7 @@ static bool nameSplit(const std::string& full, std::string& first, std::string& return false; first = fragments[0]; last = (fragments.size() == 1) ? - gHippoGridManager->getCurrentGrid()->isAurora() ? "" : "Resident" : + gHippoGridManager->getCurrentGrid()->isWhiteCore() ? LLStringUtil::null : "Resident" : fragments[1]; return (fragments.size() <= 2); } @@ -113,9 +115,9 @@ static std::string nameJoin(const std::string& first,const std::string& last, bo if (last.empty() || (strip_resident && boost::algorithm::iequals(last, "Resident"))) return first; else if (std::islower(last[0])) - return first + "." + last; + return first + '.' + last; else - return first + " " + last; + return first + ' ' + last; } static std::string getDisplayString(const std::string& first, const std::string& last, const std::string& grid, bool is_secondlife) @@ -124,7 +126,7 @@ static std::string getDisplayString(const std::string& first, const std::string& if (grid == gHippoGridManager->getDefaultGridName()) return nameJoin(first, last, is_secondlife); else - return nameJoin(first, last, is_secondlife) + " (" + grid + ")"; + return nameJoin(first, last, is_secondlife) + " (" + grid + ')'; } static std::string getDisplayString(const LLSavedLoginEntry& entry) @@ -132,22 +134,87 @@ static std::string getDisplayString(const LLSavedLoginEntry& entry) return getDisplayString(entry.getFirstName(), entry.getLastName(), entry.getGrid(), entry.isSecondLife()); } -class LLLoginRefreshHandler : public LLCommandHandler + +class LLLoginLocationAutoHandler : public LLCommandHandler { public: // don't allow from external browsers - LLLoginRefreshHandler() : LLCommandHandler("login_refresh", UNTRUSTED_BLOCK) { } + LLLoginLocationAutoHandler() : LLCommandHandler("location_login", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) { - LLPanelLogin::loadLoginPage(); + if ( tokens.size() == 0 || tokens.size() > 4 ) + return false; + + // unescape is important - uris with spaces are escaped in this code path + // (e.g. space -> %20) and the code to log into a region doesn't support that. + const std::string region = LLURI::unescape( tokens[0].asString() ); + + // just region name as payload + if ( tokens.size() == 1 ) + { + // region name only - slurl will end up as center of region + LLSLURL slurl(region); + LLPanelLogin::autologinToLocation(slurl); + } + else + // region name and x coord as payload + if ( tokens.size() == 2 ) + { + // invalid to only specify region and x coordinate + // slurl code will revert to same as region only, so do this anyway + LLSLURL slurl(region); + LLPanelLogin::autologinToLocation(slurl); + } + else + // region name and x/y coord as payload + if ( tokens.size() == 3 ) + { + // region and x/y specified - default z to 0 + F32 xpos; + std::istringstream codec(tokens[1].asString()); + codec >> xpos; + + F32 ypos; + codec.clear(); + codec.str(tokens[2].asString()); + codec >> ypos; + + const LLVector3 location(xpos, ypos, 0.0f); + LLSLURL slurl(region, location); + + LLPanelLogin::autologinToLocation(slurl); + } + else + // region name and x/y/z coord as payload + if ( tokens.size() == 4 ) + { + // region and x/y/z specified - ok + F32 xpos; + std::istringstream codec(tokens[1].asString()); + codec >> xpos; + + F32 ypos; + codec.clear(); + codec.str(tokens[2].asString()); + codec >> ypos; + + F32 zpos; + codec.clear(); + codec.str(tokens[3].asString()); + codec >> zpos; + + const LLVector3 location(xpos, ypos, zpos); + LLSLURL slurl(region, location); + + LLPanelLogin::autologinToLocation(slurl); + }; } return true; } }; - -LLLoginRefreshHandler gLoginRefreshHandler; +LLLoginLocationAutoHandler gLoginLocationAutoHandler; //--------------------------------------------------------------------------- // Public methods @@ -210,7 +277,7 @@ LLPanelLogin::LLPanelLogin(const LLRect& rect) { // no, so get the preference setting std::string defaultStartLocation = gSavedSettings.getString("LoginLocation"); - LL_INFOS("AppInit")<<"default LoginLocation '"<("start_location_combo"); + if (!combo) return; + S32 num_items = combo->getItemCount(); + for (S32 i = num_items - 1; i > 2; i--) + { + combo->remove(i); + } + + // Load favorites into the combo. + const auto grid = gHippoGridManager->getCurrentGrid(); + std::string first, last, password; + getFields(first, last, password); + auto user_defined_name(first + ' ' + last); + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites_" + grid->getGridName() + ".xml"); + std::string old_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) + { + file.open(old_filename); + if (!file.is_open()) return; + } + LLSDSerialize::fromXML(fav_llsd, file); + + for (LLSD::map_const_iterator iter = fav_llsd.beginMap(); + iter != fav_llsd.endMap(); ++iter) + { + // The account name in stored_favorites.xml has Resident last name even if user has + // a single word account name, so it can be compared case-insensitive with the + // user defined "firstname lastname". + S32 res = LLStringUtil::compareInsensitive(user_defined_name, iter->first); + if (res != 0) + { + LL_DEBUGS() << "Skipping favorites for " << iter->first << LL_ENDL; + continue; + } + + combo->addSeparator(); + LL_DEBUGS() << "Loading favorites for " << iter->first << LL_ENDL; + auto user_llsd = iter->second; + for (LLSD::array_const_iterator iter1 = user_llsd.beginArray(); + iter1 != user_llsd.endArray(); ++iter1) + { + std::string label = (*iter1)["name"].asString(); + std::string value = (*iter1)["slurl"].asString(); + if (!label.empty() && !value.empty()) + { + combo->add(label, value); + } + } + break; + } } void LLPanelLogin::setSiteIsAlive(bool alive) @@ -303,15 +431,40 @@ void LLPanelLogin::setSiteIsAlive(bool alive) } } +void LLPanelLogin::clearPassword() +{ + getChild("password_edit")->setValue(mIncomingPassword = mMungedPassword = LLStringUtil::null); +} + +void LLPanelLogin::hidePassword() +{ + // This is a MD5 hex digest of a password. + // We don't actually use the password input field, + // fill it with MAX_PASSWORD characters so we get a + // nice row of asterixes. + getChild("password_edit")->setValue("123456789!123456"); +} + void LLPanelLogin::mungePassword(const std::string& password) { // Re-md5 if we've changed at all if (password != mIncomingPassword) { - LLMD5 pass((unsigned char *)password.c_str()); - char munged_password[MD5HEX_STR_SIZE]; - pass.hex_digest(munged_password); - mMungedPassword = munged_password; + // Max "actual" password length is 16 characters. + // Hex digests are always 32 characters. + if (password.length() == MD5HEX_STR_BYTES) + { + hidePassword(); + mMungedPassword = password; + } + else + { + LLMD5 pass((unsigned char *)utf8str_truncate(password, gHippoGridManager->getCurrentGrid()->isOpenSimulator() ? 24 : 16).c_str()); + char munged_password[MD5HEX_STR_SIZE]; + pass.hex_digest(munged_password); + mMungedPassword = munged_password; + } + mIncomingPassword = password; } } @@ -319,7 +472,7 @@ void LLPanelLogin::mungePassword(const std::string& password) // (with some padding so the other login screen doesn't show through) void LLPanelLogin::reshapeBrowser() { - LLMediaCtrl* web_browser = getChild("login_html"); + auto web_browser = getChild("login_html"); LLRect rect = gViewerWindow->getWindowRectScaled(); LLRect html_rect; html_rect.setCenterAndSize( @@ -334,10 +487,13 @@ LLPanelLogin::~LLPanelLogin() { std::string login_hist_filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "saved_logins_sg2.xml"); LLSavedLogins::saveFile(mLoginHistoryData, login_hist_filepath); - LLPanelLogin::sInstance = NULL; + sInstance = nullptr; + + // Controls having keyboard focus by default + // must reset it on destroy. (EXT-2748) if (gFocusMgr.getDefaultKeyboardFocus() == this) - gFocusMgr.setDefaultKeyboardFocus(NULL); + gFocusMgr.setDefaultKeyboardFocus(nullptr); } // virtual @@ -390,7 +546,7 @@ BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask) if ( KEY_F2 == key ) { LL_INFOS() << "Spawning floater TOS window" << LL_ENDL; - LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,""); + LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,LLStringUtil::null); tos_dialog->startModal(); return TRUE; } @@ -428,8 +584,8 @@ void LLPanelLogin::giveFocus() BOOL have_username = !username.empty(); BOOL have_pass = !pass.empty(); - LLLineEditor* edit = NULL; - LLComboBox* combo = NULL; + LLLineEditor* edit = nullptr; + LLComboBox* combo = nullptr; if (have_username && !have_pass) { // User saved his name but not his password. Move @@ -487,29 +643,11 @@ void LLPanelLogin::setFields(const std::string& firstname, llassert_always(firstname.find(' ') == std::string::npos); login_combo->setLabel(nameJoin(firstname, lastname, false)); - // Max "actual" password length is 16 characters. - // Hex digests are always 32 characters. - if (password.length() == 32) - { - // This is a MD5 hex digest of a password. - // We don't actually use the password input field, - // fill it with MAX_PASSWORD characters so we get a - // nice row of asterixes. - const std::string filler("123456789!123456"); - sInstance->getChild("password_edit")->setValue(filler); - sInstance->mIncomingPassword = filler; - sInstance->mMungedPassword = password; - } - else - { - // this is a normal text password + sInstance->mungePassword(password); + if (sInstance->mIncomingPassword != sInstance->mMungedPassword) sInstance->getChild("password_edit")->setValue(password); - sInstance->mIncomingPassword = password; - LLMD5 pass((unsigned char *)password.c_str()); - char munged_password[MD5HEX_STR_SIZE]; - pass.hex_digest(munged_password); - sInstance->mMungedPassword = munged_password; - } + else + sInstance->hidePassword(); } // static @@ -526,33 +664,26 @@ void LLPanelLogin::setFields(const LLSavedLoginEntry& entry, bool takeFocus) LLComboBox* login_combo = sInstance->getChild("username_combo"); login_combo->setTextEntry(fullname); login_combo->resetTextDirty(); - //sInstance->getChild("username_combo")->setValue(fullname); + //login_combo->setValue(fullname); - std::string grid = entry.getGrid(); + const auto& grid = entry.getGrid(); //grid comes via LLSavedLoginEntry, which uses full grid names, not nicks - if(!grid.empty() && gHippoGridManager->getGrid(grid) && grid != gHippoGridManager->getCurrentGridName()) + if (!grid.empty() && gHippoGridManager->getGrid(grid) && grid != gHippoGridManager->getCurrentGridName()) { gHippoGridManager->setCurrentGrid(grid); } - - if (entry.getPassword().empty()) - { - sInstance->getChild("password_edit")->setValue(LLStringUtil::null); - remember_pass_check->setValue(LLSD(false)); - } - else - { - const std::string filler("123456789!123456"); - sInstance->getChild("password_edit")->setValue(filler); - sInstance->mIncomingPassword = filler; - sInstance->mMungedPassword = entry.getPassword(); - remember_pass_check->setValue(LLSD(true)); - } - if (takeFocus) + const auto& password = entry.getPassword(); + bool remember_pass = !password.empty(); + if (remember_pass) { - giveFocus(); + sInstance->mIncomingPassword = sInstance->mMungedPassword = password; + sInstance->hidePassword(); } + else sInstance->clearPassword(); + remember_pass_check->setValue(remember_pass); + + if (takeFocus) giveFocus(); } // static @@ -613,7 +744,7 @@ void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl) LL_DEBUGS("AppInit")<getChild("start_location_combo"); + auto location_combo = sInstance->getChild("start_location_combo"); /* * Determine whether or not the new_start_slurl modifies the grid. * @@ -632,12 +763,15 @@ void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl) location_combo->setTextEntry(new_start_slurl.getLocationString()); } break; + case LLSLURL::HOME_LOCATION: location_combo->setCurrentByIndex(0); // home location break; + case LLSLURL::LAST_LOCATION: location_combo->setCurrentByIndex(1); // last location break; + default: LL_WARNS("AppInit")<<"invalid login slurl, using home"<setCurrentByIndex(1); // home location @@ -653,14 +787,27 @@ void LLPanelLogin::setLocation(const LLSLURL& slurl) LLStartUp::setStartSLURL(slurl); // calls onUpdateStartSLURL, above } +void LLPanelLogin::autologinToLocation(const LLSLURL& slurl) +{ + LL_DEBUGS("AppInit")<<"automatically logging into Location "<onClickConnect(); + } +} + + // static void LLPanelLogin::close() { if (sInstance) { sInstance->getParent()->removeChild(sInstance); + delete sInstance; - sInstance = NULL; + sInstance = nullptr; } } @@ -766,7 +913,7 @@ void LLPanelLogin::loadLoginPage() { params["grid"] = gHippoGridManager->getCurrentGrid()->getGridNick(); } - else if (gHippoGridManager->getCurrentGrid()->getPlatform() == HippoGridInfo::PLATFORM_AURORA) + else if (gHippoGridManager->getCurrentGrid()->getPlatform() == HippoGridInfo::PLATFORM_WHITECORE) { params["grid"] = LLViewerLogin::getInstance()->getGridLabel(); } @@ -924,7 +1071,7 @@ void LLPanelLogin::onSelectGrid(LLUICtrl *ctrl) LLStringUtil::trim(grid); // Guard against copy paste if (!gHippoGridManager->getGrid(grid)) // We can't get an input grid by name or nick, perhaps a Login URI was entered { - HippoGridInfo* info(new HippoGridInfo("")); // Start off with empty grid name, otherwise we don't know what to name + HippoGridInfo* info(new HippoGridInfo(LLStringUtil::null)); // Start off with empty grid name, otherwise we don't know what to name info->setLoginUri(grid); try { @@ -964,11 +1111,47 @@ void LLPanelLogin::onSelectGrid(LLUICtrl *ctrl) } gHippoGridManager->setCurrentGrid(grid); ctrl->setValue(grid); + sInstance->addFavoritesToStartLocation(); + + /* + * Determine whether or not the value in the start_location_combo makes sense + * with the new grid value. + * + * Note that some forms that could be in the location combo are grid-agnostic, + * such as "MyRegion/128/128/0". There could be regions with that name on any + * number of grids, so leave them alone. Other forms, such as + * https://grid.example.com/region/Party%20Town/20/30/5 specify a particular + * grid; in those cases we want to clear the location. + */ + auto location_combo = sInstance->getChild("start_location_combo"); + S32 index = location_combo->getCurrentIndex(); + switch (index) + { + case 0: // last location + case 1: // home location + // do nothing - these are grid-agnostic locations + break; + + default: + { + std::string location = location_combo->getValue().asString(); + LLSLURL slurl(location); // generata a slurl from the location combo contents + if ( slurl.getType() == LLSLURL::LOCATION + && slurl.getGrid() != gHippoGridManager->getCurrentGridNick() + ) + { + // the grid specified by the location is not this one, so clear the combo + location_combo->setCurrentByIndex(0); // last location on the new grid + location_combo->setTextEntry(LLStringUtil::null); + } + } + break; + } } void LLPanelLogin::onLocationSLURL() { - LLComboBox* location_combo = getChild("start_location_combo"); + auto location_combo = getChild("start_location_combo"); std::string location = location_combo->getValue().asString(); LLStringUtil::trim(location); LL_DEBUGS("AppInit")<setNameEditted(true); + + sInstance->addFavoritesToStartLocation(); } void LLPanelLogin::onLoginComboLostFocus(LLComboBox* combo_box) { if (combo_box->isTextDirty()) { - getChild("password_edit")->setValue(mIncomingPassword = mMungedPassword = LLStringUtil::null); + clearPassword(); combo_box->resetTextDirty(); } } diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 661bc6df0..88b4a4c37 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -79,6 +79,7 @@ public: static void getFields(std::string& firstname, std::string& lastname, std::string& password); static void setLocation(const LLSLURL& slurl); + static void autologinToLocation(const LLSLURL& slurl); /// Call when preferences that control visibility may have changed static void updateLocationSelectorsVisibility(); @@ -94,6 +95,8 @@ public: static void refreshLoginPage(); static void giveFocus(); static void setAlwaysRefresh(bool refresh); + void clearPassword(); + void hidePassword(); void mungePassword(const std::string& password); // inherited from LLViewerMediaObserver @@ -104,6 +107,7 @@ public: private: void reshapeBrowser(); + void addFavoritesToStartLocation(); void onLocationSLURL(); void onClickConnect(); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 7e49db7d3..0ce1c93b8 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -40,6 +40,7 @@ #include "llresmgr.h" #include "llscrollcontainer.h" #include "llsdserialize.h" +#include "llsdparam.h" #include "llspinctrl.h" #include "lltooldraganddrop.h" #include "llviewermenu.h" @@ -47,6 +48,8 @@ #include "llpanelobjectinventory.h" #include "llappviewer.h" +#include "llradiogroup.h" + #include "rlvhandler.h" const std::string FILTERS_FILENAME("filters.xml"); @@ -57,7 +60,6 @@ const S32 INV_MIN_HEIGHT = 150; const S32 INV_FINDER_WIDTH = 160; const S32 INV_FINDER_HEIGHT = 408; -//BOOL LLPanelMainInventory::sOpenNextNewItem = FALSE; class LLFloaterInventoryFinder : public LLFloater { public: @@ -69,9 +71,12 @@ public: virtual void onClose(bool app_quitting); void changeFilter(LLInventoryFilter* filter); void updateElementsFromFilter(); + BOOL getCheckShowLinks(); BOOL getCheckShowEmpty(); BOOL getCheckSinceLogoff(); + U32 getDateSearchDirection(); + void onLinks(const LLSD& val); static void onTimeAgo(LLUICtrl*, void *); static void onCloseBtn(void* user_data); static void selectAllTypes(void* user_data); @@ -81,10 +86,10 @@ protected: LLPanelMainInventory* mPanelMainInventory; LLSpinCtrl* mSpinSinceDays; LLSpinCtrl* mSpinSinceHours; + LLUICtrl* mRadioLinks; LLInventoryFilter* mFilter; }; - ///---------------------------------------------------------------------------- /// LLPanelMainInventory ///---------------------------------------------------------------------------- @@ -155,7 +160,7 @@ BOOL LLPanelMainInventory::postBuild() { // "All Items" is the previous only view, so it gets the InventorySortOrder mActivePanel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER)); - mActivePanel->getFilter()->markDefault(); + mActivePanel->getFilter().markDefault(); mActivePanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, mActivePanel, _1, _2)); mResortActivePanel = true; } @@ -165,7 +170,9 @@ BOOL LLPanelMainInventory::postBuild() recent_items_panel->setSinceLogoff(TRUE); recent_items_panel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::RECENTITEMS_SORT_ORDER)); recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - recent_items_panel->getFilter()->markDefault(); + LLInventoryFilter& recent_filter = recent_items_panel->getFilter(); + recent_filter.setFilterObjectTypes(recent_filter.getFilterObjectTypes() & ~(0x1 << LLInventoryType::IT_CATEGORY)); + recent_filter.markDefault(); recent_items_panel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, recent_items_panel, _1, _2)); } LLInventoryPanel* worn_items_panel = getChild("Worn Items"); @@ -173,8 +180,8 @@ BOOL LLPanelMainInventory::postBuild() { worn_items_panel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::WORNITEMS_SORT_ORDER)); worn_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - worn_items_panel->getFilter()->markDefault(); - worn_items_panel->setFilterWorn(true); + worn_items_panel->getFilter().markDefault(); + worn_items_panel->setFilterWornItems(); worn_items_panel->setFilterLinks(LLInventoryFilter::FILTERLINK_EXCLUDE_LINKS); worn_items_panel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, worn_items_panel, _1, _2)); } @@ -191,19 +198,33 @@ BOOL LLPanelMainInventory::postBuild() file.close(); // Load the persistent "Recent Items" settings. - // Note that the "All Items" and "Worn Items" settings do not persist per-account. + // Note that the "All Items" settings do not persist. if(recent_items_panel) { - if(savedFilterState.has(recent_items_panel->getFilter()->getName())) + if(savedFilterState.has(recent_items_panel->getFilter().getName())) { LLSD recent_items = savedFilterState.get( - recent_items_panel->getFilter()->getName()); - recent_items_panel->getFilter()->fromLLSD(recent_items); + recent_items_panel->getFilter().getName()); + LLInventoryFilter::Params p; + LLParamSDParser parser; + parser.readSD(recent_items, p); + recent_items_panel->getFilter().fromParams(p); + } + } + if(worn_items_panel) + { + if(savedFilterState.has(worn_items_panel->getFilter().getName())) + { + LLSD worn_items = savedFilterState.get( + worn_items_panel->getFilter().getName()); + LLInventoryFilter::Params p; + LLParamSDParser parser; + parser.readSD(worn_items, p); + worn_items_panel->getFilter().fromParams(p); } } } - mFilterEditor = getChild("inventory search editor"); if (mFilterEditor) { @@ -236,48 +257,52 @@ LLPanelMainInventory::~LLPanelMainInventory( void ) LLInventoryPanel* all_items_panel = getChild("All Items"); if (all_items_panel) { - LLInventoryFilter* filter = all_items_panel->getFilter(); - if (filter) + LLSD filterState; + LLInventoryPanel::InventoryState p; + all_items_panel->getFilter().toParams(p.filter); + if (p.validateBlock(false)) { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; + LLParamSDParser().writeSD(filterState, p); + filterRoot[all_items_panel->getName()] = filterState; } } - LLInventoryPanel* recent_items_panel = getChild("Recent Items"); - if (recent_items_panel) + LLInventoryPanel* recent_panel = findChild("Recent Items"); + if (recent_panel) { - LLInventoryFilter* filter = recent_items_panel->getFilter(); - if (filter) + LLSD filterState; + LLInventoryPanel::InventoryState p; + recent_panel->getFilter().toParams(p.filter); + if (p.validateBlock(false)) { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; + LLParamSDParser().writeSD(filterState, p); + filterRoot[recent_panel->getName()] = filterState; } } - LLInventoryPanel* worn_items_panel = getChild("Worn Items"); - if (worn_items_panel) + LLInventoryPanel* worn_panel = findChild("Worn Items"); + if (worn_panel) { - LLInventoryFilter* filter = worn_items_panel->getFilter(); - if (filter) + LLSD filterState; + LLInventoryPanel::InventoryState p; + worn_panel->getFilter().toParams(p.filter); + if (p.validateBlock(false)) { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; + LLParamSDParser().writeSD(filterState, p); + filterRoot[worn_panel->getName()] = filterState; } } - std::ostringstream filterSaveName; - filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml"); - llofstream filtersFile(filterSaveName.str()); + std::string filterSaveName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, FILTERS_FILENAME)); + llofstream filtersFile(filterSaveName.c_str()); if(!LLSDSerialize::toPrettyXML(filterRoot, filtersFile)) { - LL_WARNS() << "Could not write to filters save file " << filterSaveName.str().c_str() << LL_ENDL; + LL_WARNS() << "Could not write to filters save file " << filterSaveName.c_str() << LL_ENDL; } else + { filtersFile.close(); + } vector_replace_with_last(sActiveViews, this); gInventory.removeObserver(this); @@ -363,16 +388,10 @@ BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask) // *TODO: remove take_keyboard_focus param LLPanelMainInventory* LLPanelMainInventory::showAgentInventory(BOOL take_keyboard_focus) { - if (gDisconnected || gNoRender) - { - return NULL; - } + if (gDisconnected) return NULL; // [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) - if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWINV)) - { - return NULL; - } + if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWINV)) return NULL; // [/RLVa:KB] LLPanelMainInventory* iv = LLPanelMainInventory::getActiveInventory(); @@ -401,11 +420,6 @@ LLPanelMainInventory* LLPanelMainInventory::showAgentInventory(BOOL take_keyboar iv->setTitle(std::string("Inventory")); iv->open(); /*Flawfinder: ignore*/ } - //if (take_keyboard_focus) - //{ - // iv->startSearch(); - // gFocusMgr.triggerFocusFlash(); - //} return iv; } @@ -416,13 +430,13 @@ LLPanelMainInventory* LLPanelMainInventory::getActiveInventory() S32 count = sActiveViews.size(); if(count > 0) { - iv = sActiveViews.at(0); + iv = sActiveViews.front(); S32 z_order = gFloaterView->getZOrder(iv); S32 z_next = 0; LLPanelMainInventory* next_iv = NULL; for(S32 i = 1; i < count; ++i) { - next_iv = sActiveViews.at(i); + next_iv = sActiveViews[i]; z_next = gFloaterView->getZOrder(next_iv); if(z_next < z_order) { @@ -446,9 +460,9 @@ void LLPanelMainInventory::toggleVisibility() } else if (1 == count) { - if (sActiveViews.at(0)->getVisible()) + if (sActiveViews.front()->getVisible()) { - sActiveViews.at(0)->close(); + sActiveViews.front()->close(); gSavedSettings.setBOOL("ShowInventory", FALSE); } else @@ -462,8 +476,7 @@ void LLPanelMainInventory::toggleVisibility() // is visible. // Close all the last one spawned. - S32 last_index = sActiveViews.size() - 1; - sActiveViews.at(last_index)->close(); + sActiveViews.back()->close(); } } @@ -475,6 +488,7 @@ void LLPanelMainInventory::cleanup() { sActiveViews.at(i)->destroy(); } + sActiveViews.clear(); } @@ -493,9 +507,8 @@ void LLPanelMainInventory::updateSortControls() void LLPanelMainInventory::resetFilters() { - LLFloaterInventoryFinder *finder = getFinder(); - getActivePanel()->getFilter()->resetDefault(); - if (finder) + getActivePanel()->getFilter().resetDefault(); + if (LLFloaterInventoryFinder* finder = getFinder()) { finder->updateElementsFromFilter(); } @@ -512,7 +525,6 @@ BOOL LLPanelMainInventory::filtersVisible(void* user_data) return self->getFinder() != NULL; } - void LLPanelMainInventory::onFilterEdit(const std::string& search_string ) { if (!mActivePanel) @@ -607,8 +619,7 @@ void LLPanelMainInventory::onQuickFilterCommit(LLUICtrl* ctrl, void* user_data) view->mActivePanel->setFilterTypes( filter_type ); // Force the filters window to update itself, if it's open. - LLFloaterInventoryFinder* finder = view->getFinder(); - if( finder ) + if (LLFloaterInventoryFinder* finder = view->getFinder()) finder->updateElementsFromFilter(); } } @@ -648,7 +659,7 @@ void LLPanelMainInventory::refreshQuickFilter(LLUICtrl* ctrl) filter_type &= filter_mask; - //LL_INFOS() << "filter_type: " << filter_type << LL_ENDL; + //LL_INFOS() << "filter_type: " << filter_type << LL_ENDL; std::string selection; if (filter_type == filter_mask) @@ -657,17 +668,14 @@ void LLPanelMainInventory::refreshQuickFilter(LLUICtrl* ctrl) } else { - const FilterEntry *entry = LLFilterDictionary::instance().lookup(filter_type); - if(entry) + if (const FilterEntry *entry = LLFilterDictionary::instance().lookup(filter_type)) selection = entry->mName; else selection = view->getString("filter_type_custom"); } // Select the chosen item by label text - BOOL result = quickfilter->setSimple( (selection) ); - - if( !result ) + if (!quickfilter->setSimple(selection)) { LL_INFOS() << "The item didn't exist: " << selection << LL_ENDL; } @@ -686,7 +694,7 @@ void LLPanelMainInventory::onResetAll(void* userdata) { self->mFilterEditor->setText(LLStringUtil::null); } - self->onFilterEdit(""); + self->onFilterEdit(LLStringUtil::null); self->mActivePanel->setFilterTypes(0xffffffffffffffffULL); if (auto* finder = self->getFinder()) LLFloaterInventoryFinder::selectAllTypes(finder); @@ -723,20 +731,20 @@ void LLPanelMainInventory::onCollapseAll(void* userdata) void LLPanelMainInventory::onFilterSelected() { // Find my index - mActivePanel = (LLInventoryPanel*)childGetVisibleTab("inventory filter tabs"); + mActivePanel = (LLInventoryPanel*)getChild("inventory filter tabs")->getCurrentPanel(); if (!mActivePanel) { return; } - LLInventoryFilter* filter = mActivePanel->getFilter(); + LLInventoryFilter& filter = mActivePanel->getFilter(); LLFloaterInventoryFinder *finder = getFinder(); if (finder) { - finder->changeFilter(filter); + finder->changeFilter(&filter); } - if (filter->isActive()) + if (filter.isActive()) { // If our filter is active we may be the first thing requiring a fetch so we better start it here. LLInventoryModelBackgroundFetch::instance().start(); @@ -798,7 +806,6 @@ void LLPanelMainInventory::draw() { refreshQuickFilter( mQuickFilterCombo ); } - if (mActivePanel && mResortActivePanel) { // EXP-756: Force resorting of the list the first time we draw the list: @@ -810,7 +817,6 @@ void LLPanelMainInventory::draw() mActivePanel->setSortOrder(order); mResortActivePanel = false; } - updateItemcountText(); LLFloater::draw(); } @@ -835,7 +841,7 @@ void LLPanelMainInventory::updateItemcountText() } void LLPanelMainInventory::setFilterTextFromFilter() { - mFilterText = mActivePanel->getFilter()->getFilterText(); + mFilterText = mActivePanel->getFilter().getFilterText(); } void LLPanelMainInventory::toggleFindOptions() @@ -897,7 +903,7 @@ LLFloaterInventoryFinder::LLFloaterInventoryFinder(const std::string& name, INV_FINDER_WIDTH, INV_FINDER_HEIGHT, DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES), mPanelMainInventory(inventory_view), - mFilter(inventory_view->getPanel()->getFilter()) + mFilter(&inventory_view->getPanel()->getFilter()) { LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inventory_view_finder.xml"); @@ -918,12 +924,21 @@ BOOL LLFloaterInventoryFinder::postBuild() mSpinSinceDays = getChild("spin_days_ago"); childSetCommitCallback("spin_days_ago", onTimeAgo, this); + mRadioLinks = getChild("radio_links"); + mRadioLinks->setCommitCallback(std::bind(&LLFloaterInventoryFinder::onLinks, this, std::placeholders::_2)); + childSetAction("Close", onCloseBtn, this); updateElementsFromFilter(); return TRUE; } +void LLFloaterInventoryFinder::onLinks(const LLSD& val) +{ + auto value = val.asInteger(); + mFilter->setFilterLinks(value == 0 ? LLInventoryFilter::FILTERLINK_INCLUDE_LINKS : value == 1 ? LLInventoryFilter::FILTERLINK_EXCLUDE_LINKS : LLInventoryFilter::FILTERLINK_ONLY_LINKS); +} + void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data) { LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data; @@ -973,11 +988,13 @@ void LLFloaterInventoryFinder::updateElementsFromFilter() // Get data needed for filter display U32 filter_types = mFilter->getFilterObjectTypes(); std::string filter_string = mFilter->getFilterSubString(); + LLInventoryFilter::EFilterLink show_links = mFilter->getFilterLinks(); LLInventoryFilter::EFolderShow show_folders = mFilter->getShowFolderState(); U32 hours = mFilter->getHoursAgo(); + U32 date_search_direction = mFilter->getDateSearchDirection(); // update the ui elements - LLFloater::setTitle(mFilter->getName()); + setTitle(mFilter->getName()); getChild("check_animation")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_ANIMATION)); @@ -991,10 +1008,14 @@ void LLFloaterInventoryFinder::updateElementsFromFilter() getChild("check_sound")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SOUND)); getChild("check_texture")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_TEXTURE)); getChild("check_snapshot")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SNAPSHOT)); + getChild("check_show_links")->setValue(show_links == LLInventoryFilter::FILTERLINK_INCLUDE_LINKS); getChild("check_show_empty")->setValue(show_folders == LLInventoryFilter::SHOW_ALL_FOLDERS); getChild("check_since_logoff")->setValue(mFilter->isSinceLogoff()); mSpinSinceHours->set((F32)(hours % 24)); mSpinSinceDays->set((F32)(hours / 24)); + auto value = mFilter->getFilterLinks(); + mRadioLinks->setValue(value == LLInventoryFilter::FILTERLINK_INCLUDE_LINKS ? 0 : value == LLInventoryFilter::FILTERLINK_EXCLUDE_LINKS ? 1 : 2); + getChild("date_search_direction")->setSelectedIndex(date_search_direction); } void LLFloaterInventoryFinder::draw() @@ -1073,13 +1094,16 @@ void LLFloaterInventoryFinder::draw() filtered_by_all_types = FALSE; } - if (!filtered_by_all_types) + if (!filtered_by_all_types || (mPanelMainInventory->getPanel()->getFilter().getFilterTypes() & LLInventoryFilter::FILTERTYPE_DATE)) { - // don't include folders in filter, unless I've selected everything + // don't include folders in filter, unless I've selected everything or filtering by date filter &= ~(0x1 << LLInventoryType::IT_CATEGORY); } // update the panel, panel will update the filter + mPanelMainInventory->getPanel()->setFilterLinks(getCheckShowLinks() ? + LLInventoryFilter::FILTERLINK_INCLUDE_LINKS : LLInventoryFilter::FILTERLINK_EXCLUDE_LINKS); + mPanelMainInventory->getPanel()->setShowFolderState(getCheckShowEmpty() ? LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); mPanelMainInventory->getPanel()->setFilterTypes(filter); @@ -1090,7 +1114,7 @@ void LLFloaterInventoryFinder::draw() } U32 days = (U32)mSpinSinceDays->get(); U32 hours = (U32)mSpinSinceHours->get(); - if (hours > 24) + if (hours >= 24) { days += hours / 24; hours = (U32)hours % 24; @@ -1101,6 +1125,7 @@ void LLFloaterInventoryFinder::draw() mPanelMainInventory->getPanel()->setHoursAgo(hours); mPanelMainInventory->getPanel()->setSinceLogoff(getCheckSinceLogoff()); mPanelMainInventory->setFilterTextFromFilter(); + mPanelMainInventory->getPanel()->setDateSearchDirection(getDateSearchDirection()); LLFloater::draw(); } @@ -1119,6 +1144,10 @@ void LLFloaterInventoryFinder::onClose(bool app_quitting) destroy(); } +BOOL LLFloaterInventoryFinder::getCheckShowLinks() +{ + return getChild("check_show_links")->getValue(); +} BOOL LLFloaterInventoryFinder::getCheckShowEmpty() { @@ -1131,6 +1160,11 @@ BOOL LLFloaterInventoryFinder::getCheckSinceLogoff() return getChild("check_since_logoff")->getValue(); } +U32 LLFloaterInventoryFinder::getDateSearchDirection() +{ + return getChild("date_search_direction")->getSelectedIndex(); +} + void LLFloaterInventoryFinder::onCloseBtn(void* user_data) { LLFloaterInventoryFinder* finderp = (LLFloaterInventoryFinder*)user_data; diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index f260576c6..2f74cbda2 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -1,4 +1,4 @@ -/** + /** * @file llpanelmaininventory.h * @brief llpanelmaininventory.h * class definition @@ -80,9 +80,8 @@ public: void* cargo_data, EAcceptance* accept, std::string& tooltip_msg); - /*virtual*/ void changed(U32 mask); + /*virtual*/ void changed(U32); /*virtual*/ void draw(); - LLInventoryPanel* getPanel() { return mActivePanel; } LLInventoryPanel* getActivePanel() { return mActivePanel; } @@ -92,6 +91,7 @@ public: const std::string& getFilterText() const { return mFilterText; } void setSelectCallback(const LLFolderView::signal_t::slot_type& cb); + void onFilterEdit(const std::string& search_string); // // Misc functions @@ -106,21 +106,18 @@ public: static void onFoldersByName(void *user_data); static BOOL checkFoldersByName(void *user_data); - void onFilterSelected(); const std::string getFilterSubString(); void setFilterSubString(const std::string& string); - - static void onQuickFilterCommit(LLUICtrl* ctrl, void* user_data); static void refreshQuickFilter(LLUICtrl* ctrl); - + static void onResetAll(void* userdata); static void onExpandAll(void* userdata); - static void onCollapseAll(void* userdata); - + static void onCollapseAll(void* userdata); + void updateSortControls(); void resetFilters(); @@ -161,6 +158,7 @@ protected: protected: LLFloaterInventoryFinder* getFinder(); + LLFilterEditor* mFilterEditor; LLComboBox* mQuickFilterCombo; LLTabContainer* mFilterTabs; @@ -176,7 +174,6 @@ protected: static std::vector sActiveViews; }; - #endif // LL_LLPANELMAININVENTORY_H diff --git a/indra/newview/llpanelmarketplaceoutboxinventory.cpp b/indra/newview/llpanelmarketplaceoutboxinventory.cpp deleted file mode 100644 index 9932c8641..000000000 --- a/indra/newview/llpanelmarketplaceoutboxinventory.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/** - * @file llpanelmarketplaceoutboxinventory.cpp - * @brief LLOutboxInventoryPanel class definition - * - * $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 "llviewerprecompiledheaders.h" - -#include "llpanelmarketplaceoutboxinventory.h" - -//#include "llfolderview.h" -//#include "llfoldervieweventlistener.h" -#include "llinventorybridge.h" -//#include "llinventoryfunctions.h" -#include "lltrans.h" -//#include "llviewerfoldertype.h" - - -// -// statics -// - -static LLRegisterWidget r1("outbox_inventory_panel"); -//static LLRegisterWidget r2("outbox_folder_view_folder"); - -// -// LLOutboxInventoryPanel Implementation -// - -LLOutboxInventoryPanel::LLOutboxInventoryPanel(const std::string& name, - const std::string& sort_order_setting, - const std::string& start_folder, - const LLRect& rect, - LLInventoryModel* inventory, - BOOL allow_multi_select, - LLView* parent_view) - : LLInventoryPanel(name, sort_order_setting, start_folder, rect, inventory, allow_multi_select, parent_view) -{ -} - -LLOutboxInventoryPanel::~LLOutboxInventoryPanel() -{ -} - -// virtual -void LLOutboxInventoryPanel::buildFolderView(/*const LLInventoryPanel::Params& params*/) -{ - // Determine the root folder in case specified, and - // build the views starting with that folder. - - LLUUID root_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - - if (root_id == LLUUID::null) - { - LL_WARNS() << "Outbox inventory panel has no root folder!" << LL_ENDL; - root_id = LLUUID::generateNewID(); - } - - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - - mFolderRoot = createFolderView(new_listener, true/*params.use_label_suffix()*/)->getHandle(); -} - -LLFolderViewFolder * LLOutboxInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) -{ - return new LLOutboxFolderViewFolder( - bridge->getDisplayName(), - bridge->getIcon(), - bridge->getIconOpen(), - LLUI::getUIImage("inv_link_overlay.tga"), - mFolderRoot.get(), - bridge); -} - -LLFolderViewItem * LLOutboxInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge) -{ - return new LLOutboxFolderViewItem( - bridge->getDisplayName(), - bridge->getIcon(), - bridge->getIconOpen(), - LLUI::getUIImage("inv_link_overlay.tga"), - bridge->getCreationDate(), - mFolderRoot.get(), - bridge); -} - -// -// LLOutboxFolderViewFolder Implementation -// - -LLOutboxFolderViewFolder::LLOutboxFolderViewFolder(const std::string& name, LLUIImagePtr icon, - LLUIImagePtr icon_open, - LLUIImagePtr icon_link, - LLFolderView* root, - LLFolderViewEventListener* listener) - : LLFolderViewFolder(name, icon, icon_open, icon_link, root, listener) -{ -} - -// -// LLOutboxFolderViewItem Implementation -// - -LLOutboxFolderViewItem::LLOutboxFolderViewItem(const std::string& name, LLUIImagePtr icon, - LLUIImagePtr icon_open, - LLUIImagePtr icon_overlay, - S32 creation_date, - LLFolderView* root, - LLFolderViewEventListener* listener) - : LLFolderViewItem(name, icon, icon_open, icon_overlay, creation_date, root, listener) -{ -} - -BOOL LLOutboxFolderViewItem::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - return TRUE; -} - -void LLOutboxFolderViewItem::openItem() -{ - // Intentionally do nothing to block attaching items from the outbox -} - -// eof diff --git a/indra/newview/llpanelmarketplaceoutboxinventory.h b/indra/newview/llpanelmarketplaceoutboxinventory.h deleted file mode 100644 index 0eee7ed9e..000000000 --- a/indra/newview/llpanelmarketplaceoutboxinventory.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @file llpanelmarketplaceoutboxinventory.h - * @brief LLOutboxInventoryPanel class declaration - * - * $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_OUTBOXINVENTORYPANEL_H -#define LL_OUTBOXINVENTORYPANEL_H - - -#include "llinventorypanel.h" -#include "llfolderviewitem.h" - - -class LLOutboxInventoryPanel : public LLInventoryPanel -{ -public: - LLOutboxInventoryPanel(const std::string& name, - const std::string& sort_order_setting, - const std::string& start_folder, - const LLRect& rect, - LLInventoryModel* inventory, - BOOL allow_multi_select, - LLView *parent_view = NULL); - ~LLOutboxInventoryPanel(); - - // virtual - void buildFolderView(/*const LLInventoryPanel::Params& params*/); - - // virtual - LLFolderViewFolder * createFolderViewFolder(LLInvFVBridge * bridge); - LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge); -}; - - -class LLOutboxFolderViewFolder : public LLFolderViewFolder -{ -public: - LLOutboxFolderViewFolder(const std::string& name, LLUIImagePtr icon, LLUIImagePtr icon_open, LLUIImagePtr icon_link, LLFolderView* root, LLFolderViewEventListener* listener); -}; - - -class LLOutboxFolderViewItem : public LLFolderViewItem -{ -public: - LLOutboxFolderViewItem(const std::string& name, LLUIImagePtr icon, LLUIImagePtr icon_open, LLUIImagePtr icon_overlay, S32 creation_date, LLFolderView* root, LLFolderViewEventListener* listener); - - // virtual - BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - void openItem(); -}; - - -#endif //LL_OUTBOXINVENTORYPANEL_H diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp index 55d07a021..9503de65f 100644 --- a/indra/newview/llpanelnearbymedia.cpp +++ b/indra/newview/llpanelnearbymedia.cpp @@ -93,6 +93,7 @@ LLPanelNearByMedia::LLPanelNearByMedia(bool standalone_panel) , mDebugInfoVisible(false) , mParcelMediaItem(nullptr) , mParcelAudioItem(nullptr) + , mStandalonePanel(standalone_panel) { mHoverTimer.stop(); diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index f9c145b54..cf28f992c 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -40,6 +40,7 @@ #include "roles_constants.h" #include "llagent.h" +#include "llavataractions.h" #include "llcallbacklist.h" #include "llfloaterbuycontents.h" #include "llfloaterbuycurrency.h" @@ -203,27 +204,11 @@ void LLTaskInvFVBridge::showProperties() } } -struct LLBuyInvItemData -{ - LLUUID mTaskID; - LLUUID mItemID; - LLAssetType::EType mType; - - LLBuyInvItemData(const LLUUID& task, - const LLUUID& item, - LLAssetType::EType type) : - mTaskID(task), mItemID(item), mType(type) - {} -}; - void LLTaskInvFVBridge::buyItem() { LL_INFOS() << "LLTaskInvFVBridge::buyItem()" << LL_ENDL; LLInventoryItem* item = findItem(); if(!item || !item->getSaleInfo().isForSale()) return; - LLBuyInvItemData* inv = new LLBuyInvItemData(mPanel->getTaskUUID(), - mUUID, - item->getType()); const LLSaleInfo& sale_info = item->getSaleInfo(); const LLPermissions& perm = item->getPermissions(); @@ -234,7 +219,6 @@ void LLTaskInvFVBridge::buyItem() { LLNotificationsUtil::add("Cannot_Purchase_an_Attachment"); LL_INFOS() << "Attempt to purchase an attachment" << LL_ENDL; - delete inv; } else { @@ -265,9 +249,9 @@ void LLTaskInvFVBridge::buyItem() } LLSD payload; - payload["task_id"] = inv->mTaskID; - payload["item_id"] = inv->mItemID; - payload["type"] = inv->mType; + payload["task_id"] = mPanel->getTaskUUID(); + payload["item_id"] = mUUID; + payload["type"] = item->getType(); LLNotificationsUtil::add(alertdesc, args, payload, LLTaskInvFVBridge::commitBuyItem); } } @@ -606,7 +590,7 @@ BOOL LLTaskInvFVBridge::isClipboardPasteable() const return FALSE; } -void LLTaskInvFVBridge::pasteFromClipboard(bool only_copies) +void LLTaskInvFVBridge::pasteFromClipboard(bool) { } @@ -667,11 +651,6 @@ BOOL LLTaskInvFVBridge::dragOrDrop(MASK mask, BOOL drop, return FALSE; } -//void LLTaskInvFVBridge::dropped() -//{ -// LL_WARNS() << "LLTaskInvFVBridge::dropped() - not implemented" << LL_ENDL; -//} - void pack_script_message(LLMessageSystem*, const LLInventoryItem*, const LLViewerObject*); void reset_script(const LLInventoryItem* item, const LLViewerObject* obj) @@ -761,7 +740,7 @@ void LLTaskInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) return; } - if(gAgent.allowOperation(PERM_OWNER, item->getPermissions(), + if(!gAgent.allowOperation(PERM_OWNER, item->getPermissions(), GP_OBJECT_MANIPULATE) && item->getSaleInfo().isForSale()) { @@ -980,16 +959,6 @@ BOOL LLTaskCategoryBridge::dragOrDrop(MASK mask, BOOL drop, case DAD_CALLINGCARD: case DAD_MESH: accept = LLToolDragAndDrop::isInventoryDropAcceptable(object, (LLViewerInventoryItem*)cargo_data); - // testzone - //if(LLToolDragAndDrop::isInventoryDropAcceptable( - // object, (LLViewerInventoryItem*)cargo_data) - /*if(object->permModify() - // - && (LLToolDragAndDrop::SOURCE_WORLD != LLToolDragAndDrop::getInstance()->getSource()) - && (LLToolDragAndDrop::SOURCE_NOTECARD != LLToolDragAndDrop::getInstance()->getSource())) - { - accept = TRUE; - }*/ if(accept && drop) { LLToolDragAndDrop::dropInventory(object, @@ -1172,7 +1141,6 @@ void LLTaskSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } else if (canOpenItem()) { - //items.push_back(std::string("Task Open")); if (!isItemCopyable()) { disabled_items.push_back(std::string("Task Open")); @@ -1238,6 +1206,7 @@ BOOL LLTaskCallingCardBridge::renameItem(const std::string& new_name) return FALSE; } + ///---------------------------------------------------------------------------- /// Class LLTaskScriptBridge ///---------------------------------------------------------------------------- @@ -1665,7 +1634,7 @@ void LLPanelObjectInventory::clearContents() if( mScroller ) { // removes mFolders - removeChild( mScroller ); + removeChild( mScroller ); //*TODO: Really shouldn't do this during draw()/refresh() mScroller->die(); mScroller = NULL; mFolders = NULL; @@ -1682,7 +1651,7 @@ void LLPanelObjectInventory::reset() LLRect dummy_rect(0, 1, 1, 0); mFolders = new LLFolderView(std::string("task inventory"), dummy_rect, getTaskUUID(), this, LLTaskInvFVBridge::createObjectBridge(this, NULL)); // this ensures that we never say "searching..." or "no items found" - mFolders->getFilter()->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); + mFolders->getFilter().setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); LLRect scroller_rect(0, getRect().getHeight(), getRect().getWidth(), 0); mScroller = new LLScrollContainer(std::string("task inventory scroller"), scroller_rect, mFolders ); @@ -1749,27 +1718,27 @@ void LLPanelObjectInventory::updateInventory() inventory_has_focus = gFocusMgr.childHasKeyboardFocus(mFolders); } - reset(); - LLViewerObject* objectp = gObjectList.findObject(mTaskUUID); if (objectp) { LLInventoryObject* inventory_root = objectp->getInventoryRoot(); LLInventoryObject::object_list_t contents; objectp->getInventoryContents(contents); + if (inventory_root) { - createFolderViews(inventory_root, contents); - mHaveInventory = TRUE; + reset(); mIsInventoryEmpty = FALSE; + createFolderViews(inventory_root, contents); mFolders->setEnabled(TRUE); } else { // TODO: create an empty inventory mIsInventoryEmpty = TRUE; - mHaveInventory = TRUE; } + + mHaveInventory = TRUE; } else { @@ -1780,10 +1749,11 @@ void LLPanelObjectInventory::updateInventory() // restore previous selection std::set::iterator selection_it; - BOOL first_item = TRUE; + bool first_item = true; for (selection_it = selected_items.begin(); selection_it != selected_items.end(); ++selection_it) { LLFolderViewItem* selected_item = mFolders->getItemByID(*selection_it); + if (selected_item) { //HACK: "set" first item then "change" each other one to get keyboard focus right @@ -1799,8 +1769,12 @@ void LLPanelObjectInventory::updateInventory() } } - mFolders->requestArrange(); + if (mFolders) + { + mFolders->requestArrange(); + } mInventoryNeedsUpdate = FALSE; + // Edit menu handler is set in onFocusReceived } // *FIX: This is currently a very expensive operation, because we have @@ -1942,13 +1916,18 @@ void LLPanelObjectInventory::refresh() } if(!has_inventory) { - mTaskUUID = LLUUID::null; - removeVOInventoryListener(); - clearContents(); + clearInventoryTask(); } //LL_INFOS() << "LLPanelObjectInventory::refresh() " << mTaskUUID << LL_ENDL; } +void LLPanelObjectInventory::clearInventoryTask() +{ + mTaskUUID = LLUUID::null; + removeVOInventoryListener(); + clearContents(); +} + void LLPanelObjectInventory::removeSelectedItem() { if(mFolders) @@ -2033,7 +2012,6 @@ void LLPanelObjectInventory::idle(void* user_data) { LLPanelObjectInventory* self = (LLPanelObjectInventory*)user_data; - if (self->mInventoryNeedsUpdate) { self->updateInventory(); diff --git a/indra/newview/llpanelobjectinventory.h b/indra/newview/llpanelobjectinventory.h index 736900c76..e43c1b37f 100644 --- a/indra/newview/llpanelobjectinventory.h +++ b/indra/newview/llpanelobjectinventory.h @@ -52,6 +52,7 @@ public: void refresh(); const LLUUID& getTaskUUID() { return mTaskUUID;} + void clearInventoryTask(); void removeSelectedItem(); void startRenamingSelectedItem(); @@ -77,7 +78,6 @@ protected: void createViewsForCategory(LLInventoryObject::object_list_t* inventory, LLInventoryObject* parent, LLFolderViewFolder* folder); - void clearContents(); private: diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index 55e58a4c9..899210edb 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -61,7 +61,7 @@ void show_picture(const LLUUID& id, const std::string& name) { // Try to show and focus existing preview - if (LLPreview::show(id)) return; + if (id.isNull() || LLPreview::show(id)) return; // If there isn't one, make a new preview S32 left, top; gFloaterView->getNewFloaterPosition(&left, &top); @@ -126,8 +126,11 @@ void LLPanelPick::reset() BOOL LLPanelPick::postBuild() { + auto show_pic = [this] { show_picture(mSnapshotCtrl->getImageAssetID(), mNameEditor->getText()); }; + auto show_pic_if_not_self = [=] { if (!mSnapshotCtrl->canChange()) show_pic(); }; mSnapshotCtrl = getChild("snapshot_ctrl"); mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelPick::onCommitAny, this)); + mSnapshotCtrl->setMouseUpCallback(std::bind(show_pic_if_not_self)); mNameEditor = getChild("given_name_editor"); mNameEditor->setCommitOnFocusLost(true); @@ -144,7 +147,7 @@ BOOL LLPanelPick::postBuild() mSetBtn->setCommitCallback(boost::bind(&LLPanelPick::onClickSet,this)); mOpenBtn = getChild("open_picture_btn"); - mOpenBtn->setCommitCallback(boost::bind(show_picture, boost::bind(&LLTextureCtrl::getImageAssetID, mSnapshotCtrl), boost::bind(&LLLineEditor::getText, mNameEditor))); + mOpenBtn->setCommitCallback(std::bind(show_pic)); getChild("pick_teleport_btn")->setCommitCallback(boost::bind(&LLPanelPick::onClickTeleport,this)); getChild("pick_map_btn")->setCommitCallback(boost::bind(&LLPanelPick::onClickMap,this)); diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 60f6c55f5..84146235c 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -118,6 +118,7 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeDown", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeDown, this)); mCommitCallbackRegistrar.add("MediaCtrl.Volume", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeSlider, this)); mCommitCallbackRegistrar.add("MediaCtrl.ToggleMute", boost::bind(&LLPanelPrimMediaControls::onToggleMute, this)); + mCommitCallbackRegistrar.add("MediaCtrl.MOAPStop", std::bind([this]() { getTargetMediaImpl()->setDisabled(true); })); mCommitCallbackRegistrar.add("MediaCtrl.ShowVolumeSlider", boost::bind(&LLPanelPrimMediaControls::showVolumeSlider, this)); mCommitCallbackRegistrar.add("MediaCtrl.HideVolumeSlider", boost::bind(&LLPanelPrimMediaControls::hideVolumeSlider, this)); mCommitCallbackRegistrar.add("MediaCtrl.SkipBack", boost::bind(&LLPanelPrimMediaControls::onClickSkipBack, this)); @@ -150,6 +151,7 @@ BOOL LLPanelPrimMediaControls::postBuild() mPauseCtrl = getChild("pause"); mStopCtrl = getChild("stop"); mMediaStopCtrl = getChild("media_stop"); + mMOAPStopCtrl = getChild("moap_stop"); mHomeCtrl = getChild("home"); mUnzoomCtrl = getChild("close"); // This is actually "unzoom" mOpenCtrl = getChild("new_window"); @@ -368,6 +370,7 @@ void LLPanelPrimMediaControls::updateShape() mReloadCtrl->setEnabled(false); mReloadCtrl->setVisible(false); mMediaStopCtrl->setVisible(has_focus); + mMOAPStopCtrl->setVisible(false); mHomeCtrl->setVisible(has_focus); mBackCtrl->setVisible(false); mFwdCtrl->setVisible(false); @@ -466,6 +469,7 @@ void LLPanelPrimMediaControls::updateShape() mPlayCtrl->setVisible(FALSE); mPauseCtrl->setVisible(FALSE); mMediaStopCtrl->setVisible(FALSE); + mMOAPStopCtrl->setVisible(!!media_plugin); mMediaAddressCtrl->setVisible(has_focus && !mini_controls); mMediaAddressCtrl->setEnabled(has_focus && !mini_controls); mMediaPlaySliderPanel->setVisible(FALSE); diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h index 7db687d23..2cf196a22 100644 --- a/indra/newview/llpanelprimmediacontrols.h +++ b/indra/newview/llpanelprimmediacontrols.h @@ -155,6 +155,7 @@ private: LLUICtrl *mPauseCtrl; LLUICtrl *mStopCtrl; LLUICtrl *mMediaStopCtrl; + LLUICtrl *mMOAPStopCtrl; LLUICtrl *mHomeCtrl; LLUICtrl *mUnzoomCtrl; LLUICtrl *mOpenCtrl; diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index d35d481ab..e40747eee 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -98,7 +98,7 @@ public: std::string verb = params[1].asString(); for (; !verb.empty() && std::ispunct(verb.back()); verb.pop_back()); - if (verb == "about") + if (verb == "about" || verb == "completename" || verb == "displayname" || verb == "username") { LLAvatarActions::showProfile(avatar_id); return true; diff --git a/indra/newview/llpanelskins.cpp b/indra/newview/llpanelskins.cpp index 53d5ce60f..834fd9832 100644 --- a/indra/newview/llpanelskins.cpp +++ b/indra/newview/llpanelskins.cpp @@ -116,8 +116,7 @@ void LLPanelSkins::refresh() LLButton* b = getChild("custom_skin_preview"); std::string imagename = data["preview_image"].asString(); if(imagename == "" || imagename == " " || !LLFile::isfile(imagename)) imagename = "preview.png"; - std::string imageprev(".."+gDirUtilp->getDirDelimiter()+ - ".."+gDirUtilp->getDirDelimiter()+ + std::string imageprev(path_name+ data["folder_name"].asString()+gDirUtilp->getDirDelimiter()+ "textures"+gDirUtilp->getDirDelimiter()+ imagename); diff --git a/indra/newview/llprefschat.cpp b/indra/newview/llprefschat.cpp index f62a4d4e9..f76df1679 100644 --- a/indra/newview/llprefschat.cpp +++ b/indra/newview/llprefschat.cpp @@ -179,7 +179,6 @@ void LLPrefsChatImpl::apply() gSavedSettings.setColor4("BackgroundChatColor", childGetValue("background")); gSavedSettings.setColor4("HTMLLinkColor", childGetValue("links")); - LLTextEditor::setLinkColor(childGetValue("links")); gSavedSettings.setBOOL("ArrowKeysMoveAvatar", childGetValue("arrow_keys_move_avatar_check")); gSavedSettings.setBOOL("ChatShowTimestamps", childGetValue("show_timestamps_check")); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index e5b8d4898..f191b3368 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -286,6 +286,8 @@ void LLPreviewNotecard::loadAsset() // request the asset. if (const LLInventoryItem* item = getItem()) { + bool modify = gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE); + if (modify) editor->setParseHTML(false); // Don't do the url parsing or we'll lose text! if (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) || gAgent.isGodlike()) @@ -344,8 +346,7 @@ void LLPreviewNotecard::loadAsset() editor->setEnabled(FALSE); mAssetStatus = PREVIEW_ASSET_LOADED; } - if(!gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), - GP_OBJECT_MANIPULATE)) + if(!modify) { editor->setEnabled(FALSE); // You can always save in task inventory @@ -387,7 +388,11 @@ void LLPreviewNotecard::onLoadComplete(LLVFS *vfs, // put a EOS at the end buffer[file_length] = 0; - + // Singu Note: Set Enabled first, it determines whether or not our text will be linked + const LLInventoryItem* item = preview->getItem(); + BOOL modifiable = item && gAgent.allowOperation(PERM_MODIFY, + item->getPermissions(), GP_OBJECT_MANIPULATE); + preview->setEnabled(modifiable); if (LLViewerTextEditor* previewEditor = preview->findChild("Notecard Editor")) { if ((file_length > 19) && !strncmp(buffer, "Linden text version", 19)) @@ -405,11 +410,6 @@ void LLPreviewNotecard::onLoadComplete(LLVFS *vfs, previewEditor->makePristine(); } - - const LLInventoryItem* item = preview->getItem(); - BOOL modifiable = item && gAgent.allowOperation(PERM_MODIFY, - item->getPermissions(), GP_OBJECT_MANIPULATE); - preview->setEnabled(modifiable); delete[] buffer; preview->mAssetStatus = PREVIEW_ASSET_LOADED; } diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index a2fba8e86..6fa3882ea 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -1,3 +1,5 @@ +// 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 llselectmgr.cpp * @brief A manager for selected objects and faces. @@ -52,6 +54,7 @@ // viewer includes #include "llagent.h" #include "llagentcamera.h" +#include "llattachmentsmgr.h" #include "llviewerwindow.h" #include "lldrawable.h" #include "llfloaterinspect.h" @@ -66,8 +69,8 @@ #include "llmenugl.h" #include "llmeshrepository.h" #include "llmutelist.h" -#include "llparcel.h" #include "llnotificationsutil.h" +#include "llparcel.h" // Rez under Land Group #include "llstatusbar.h" #include "llsurface.h" #include "lltool.h" @@ -83,7 +86,7 @@ #include "llviewermenu.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" -#include "llviewerparcelmgr.h" +#include "llviewerparcelmgr.h" // Rez under Land Group #include "llviewerregion.h" #include "llviewerstats.h" #include "llvoavatarself.h" @@ -256,7 +259,7 @@ void LLSelectMgr::updateEffects() //keep reference grid objects active struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { LLDrawable* drawable = object->mDrawable; if (drawable) @@ -280,7 +283,7 @@ void LLSelectMgr::overrideObjectUpdates() //override any position updates from simulator on objects being edited struct f : public LLSelectedNodeFunctor { - virtual bool apply(LLSelectNode* selectNode) + bool apply(LLSelectNode* selectNode) override { LLViewerObject* object = selectNode->getObject(); if (object && object->permMove() && !object->isPermanentEnforced()) @@ -525,17 +528,15 @@ BOOL LLSelectMgr::removeObjectFromSelections(const LLUUID &id) { BOOL object_found = FALSE; LLTool *tool = NULL; - if (!gNoRender) - { - tool = LLToolMgr::getInstance()->getCurrentTool(); - // It's possible that the tool is editing an object that is not selected - LLViewerObject* tool_editing_object = tool->getEditingObject(); - if( tool_editing_object && tool_editing_object->mID == id) - { - tool->stopEditing(); - object_found = TRUE; - } + tool = LLToolMgr::getInstance()->getCurrentTool(); + + // It's possible that the tool is editing an object that is not selected + LLViewerObject* tool_editing_object = tool->getEditingObject(); + if( tool_editing_object && tool_editing_object->mID == id) + { + tool->stopEditing(); + object_found = TRUE; } // Iterate through selected objects list and kill the object @@ -651,7 +652,7 @@ bool LLSelectMgr::enableLinkObjects() { struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { LLViewerObject *root_object = (object == NULL) ? NULL : object->getRootEdit(); return object->permModify() && !object->isPermanentEnforced() && @@ -1563,7 +1564,7 @@ void LLSelectMgr::cleanup() struct LLSelectMgrSendFunctor : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -1601,8 +1602,12 @@ void LLSelectMgr::selectionSetImage(const LLUUID& imageid) LLViewerInventoryItem* mItem; LLUUID mImageID; f(LLViewerInventoryItem* item, const LLUUID& id) : mItem(item), mImageID(id) {} - bool apply(LLViewerObject* objectp, S32 te) + bool apply(LLViewerObject* objectp, S32 te) override { + if(objectp && !objectp->permModify()) + { + return false; + } if (mItem) { if (te == -1) // all faces @@ -1633,23 +1638,26 @@ void LLSelectMgr::selectionSetImage(const LLUUID& imageid) } return true; } - } setfunc(item, imageid); + }; + + f setfunc(item, imageid); getSelection()->applyToTEs(&setfunc); struct g : public LLSelectedObjectFunctor { LLViewerInventoryItem* mItem; g(LLViewerInventoryItem* item) : mItem(item) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (!mItem) { object->sendTEUpdate(); - // 1 particle effect per object // if(!gSavedSettings.getBOOL("DisablePointAtAndBeam")) { // + // 1 particle effect per object LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE); effectp->setSourceObject(gAgentAvatarp); effectp->setTargetObject(object); @@ -1674,7 +1682,7 @@ void LLSelectMgr::selectionSetColor(const LLColor4 &color) { LLColor4 mColor; f(const LLColor4& c) : mColor(c) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1698,7 +1706,7 @@ void LLSelectMgr::selectionSetColorOnly(const LLColor4 &color) { LLColor4 mColor; f(const LLColor4& c) : mColor(c) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1725,7 +1733,7 @@ void LLSelectMgr::selectionSetAlphaOnly(const F32 alpha) { F32 mAlpha; f(const F32& a) : mAlpha(a) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1749,7 +1757,7 @@ void LLSelectMgr::selectionRevertColors() { LLObjectSelectionHandle mSelectedObjects; f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1770,13 +1778,47 @@ void LLSelectMgr::selectionRevertColors() getSelection()->applyToObjects(&sendfunc); } +void LLSelectMgr::selectionRevertShinyColors() +{ + struct f : public LLSelectedTEFunctor + { + LLObjectSelectionHandle mSelectedObjects; + f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} + bool apply(LLViewerObject* object, S32 te) override + { + if (object->permModify()) + { + LLSelectNode* nodep = mSelectedObjects->findNode(object); + if (nodep && te < (S32)nodep->mSavedShinyColors.size()) + { + LLColor4 color = nodep->mSavedShinyColors[te]; + // update viewer side color in anticipation of update from simulator + LLMaterialPtr old_mat = object->getTE(te)->getMaterialParams(); + if (!old_mat.isNull()) + { + LLMaterialPtr new_mat = gFloaterTools->getPanelFace()->createDefaultMaterial(old_mat); + new_mat->setSpecularLightColor(color); + object->getTE(te)->setMaterialParams(new_mat); + LLMaterialMgr::getInstance()->put(object->getID(), te, *new_mat); + } + } + } + return true; + } + } setfunc(mSelectedObjects); + getSelection()->applyToTEs(&setfunc); + + LLSelectMgrSendFunctor sendfunc; + getSelection()->applyToObjects(&sendfunc); +} + BOOL LLSelectMgr::selectionRevertTextures() { struct f : public LLSelectedTEFunctor { LLObjectSelectionHandle mSelectedObjects; f(LLObjectSelectionHandle sel) : mSelectedObjects(sel) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1816,7 +1858,7 @@ void LLSelectMgr::selectionSetBumpmap(U8 bumpmap) { U8 mBump; f(const U8& b) : mBump(b) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1838,7 +1880,7 @@ void LLSelectMgr::selectionSetTexGen(U8 texgen) { U8 mTexgen; f(const U8& t) : mTexgen(t) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1861,7 +1903,7 @@ void LLSelectMgr::selectionSetShiny(U8 shiny) { U8 mShiny; f(const U8& t) : mShiny(t) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1883,7 +1925,7 @@ void LLSelectMgr::selectionSetFullbright(U8 fullbright) { U8 mFullbright; f(const U8& t) : mFullbright(t) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1899,7 +1941,8 @@ void LLSelectMgr::selectionSetFullbright(U8 fullbright) { U8 mFullbright; g(const U8& t) : mFullbright(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -1932,7 +1975,7 @@ void LLSelectMgr::selectionSetMedia(U8 media_type, const LLSD &media_data) U8 mMediaFlags; const LLSD &mMediaData; f(const U8& t, const LLSD& d) : mMediaFlags(t), mMediaData(d) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) { @@ -1948,7 +1991,7 @@ void LLSelectMgr::selectionSetMedia(U8 media_type, const LLSD &media_data) llassert(mMediaData.isMap()); const LLTextureEntry *texture_entry = object->getTE(te); if (!mMediaData.isMap() || - (NULL != texture_entry) && !texture_entry->hasMedia() && !mMediaData.has(LLMediaEntry::HOME_URL_KEY)) + ((NULL != texture_entry) && !texture_entry->hasMedia() && !mMediaData.has(LLMediaEntry::HOME_URL_KEY))) { // skip adding/updating media } @@ -1976,7 +2019,7 @@ void LLSelectMgr::selectionSetMedia(U8 media_type, const LLSD &media_data) struct f2 : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2004,7 +2047,7 @@ void LLSelectMgr::selectionSetGlow(F32 glow) { F32 mGlow; f1(F32 glow) : mGlow(glow) {}; - bool apply(LLViewerObject* object, S32 face) + bool apply(LLViewerObject* object, S32 face) override { if (object->permModify()) { @@ -2018,7 +2061,7 @@ void LLSelectMgr::selectionSetGlow(F32 glow) struct f2 : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2037,7 +2080,7 @@ void LLSelectMgr::selectionSetMaterialParams(LLSelectedTEMaterialFunctor* materi LLMaterialPtr mMaterial; f1(LLSelectedTEMaterialFunctor* material_func) : _material_func(material_func) {} - bool apply(LLViewerObject* object, S32 face) + bool apply(LLViewerObject* object, S32 face) override { if (object && object->permModify() && _material_func) { @@ -2057,7 +2100,7 @@ void LLSelectMgr::selectionSetMaterialParams(LLSelectedTEMaterialFunctor* materi struct f2 : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2073,7 +2116,7 @@ void LLSelectMgr::selectionRemoveMaterial() { struct f1 : public LLSelectedTEFunctor { - bool apply(LLViewerObject* object, S32 face) + bool apply(LLViewerObject* object, S32 face) override { if (object->permModify()) { @@ -2088,7 +2131,7 @@ void LLSelectMgr::selectionRemoveMaterial() struct f2 : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2129,7 +2172,7 @@ BOOL LLSelectMgr::selectionGetGlow(F32 *glow) F32 lglow = 0.f; struct f1 : public LLSelectedTEGetFunctor { - F32 get(LLViewerObject* object, S32 face) + F32 get(LLViewerObject* object, S32 face) override { return object->getTE(face)->getGlow(); } @@ -2147,7 +2190,8 @@ void LLSelectMgr::selectionSetPhysicsType(U8 type) { U8 mType; f(const U8& t) : mType(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2166,7 +2210,8 @@ void LLSelectMgr::selectionSetFriction(F32 friction) { F32 mFriction; f(const F32& friction) : mFriction(friction) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2185,7 +2230,8 @@ void LLSelectMgr::selectionSetGravity(F32 gravity ) { F32 mGravity; f(const F32& gravity) : mGravity(gravity) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2204,7 +2250,8 @@ void LLSelectMgr::selectionSetDensity(F32 density ) { F32 mDensity; f(const F32& density ) : mDensity(density) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2223,7 +2270,8 @@ void LLSelectMgr::selectionSetRestitution(F32 restitution) { F32 mRestitution; f(const F32& restitution ) : mRestitution(restitution) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2246,7 +2294,8 @@ void LLSelectMgr::selectionSetMaterial(U8 material) { U8 mMaterial; f(const U8& t) : mMaterial(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -2268,7 +2317,8 @@ BOOL LLSelectMgr::selectionAllPCode(LLPCode code) { LLPCode mCode; f(const LLPCode& t) : mCode(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if (object->getPCode() != mCode) { @@ -2319,6 +2369,7 @@ void LLSelectMgr::selectionSetIncludeInSearch(bool include_in_search) "ObjectIncludeInSearch", packAgentAndSessionID, packObjectIncludeInSearch, + logNoOp, &include_in_search, SEND_ONLY_ROOTS); } @@ -2338,7 +2389,8 @@ BOOL LLSelectMgr::selectionGetClickAction(U8 *out_action) { U8 mAction; f(const U8& t) : mAction(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { if ( mAction != object->getClickAction()) { @@ -2357,7 +2409,8 @@ void LLSelectMgr::selectionSetClickAction(U8 action) { U8 mAction; f(const U8& t) : mAction(t) {} - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { object->setClickAction(mAction); return true; @@ -2368,6 +2421,7 @@ void LLSelectMgr::selectionSetClickAction(U8 action) sendListToRegions("ObjectClickAction", packAgentAndSessionID, packObjectClickAction, + logNoOp, &action, SEND_INDIVIDUALS); } @@ -2403,7 +2457,7 @@ void LLSelectMgr::sendGodlikeRequest(const std::string& request, const std::stri } else { - sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, &data, SEND_ONLY_ROOTS); + sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, logNoOp, &data, SEND_ONLY_ROOTS); } } @@ -2431,6 +2485,23 @@ void LLSelectMgr::packGodlikeHead(void* user_data) } } +// static +void LLSelectMgr::logNoOp(LLSelectNode* node, void *) +{ +} + +// static +void LLSelectMgr::logAttachmentRequest(LLSelectNode* node, void *) +{ + LLAttachmentsMgr::instance().onAttachmentRequested(node->mItemID); +} + +// static +void LLSelectMgr::logDetachRequest(LLSelectNode* node, void *) +{ + LLAttachmentsMgr::instance().onDetachRequested(node->mItemID); +} + // static void LLSelectMgr::packObjectIDAsParam(LLSelectNode* node, void *) { @@ -2448,7 +2519,7 @@ void LLSelectMgr::selectionTexScaleAutofit(F32 repeats_per_meter) { F32 mRepeatsPerMeter; f(const F32& t) : mRepeatsPerMeter(t) {} - bool apply(LLViewerObject* object, S32 te) + bool apply(LLViewerObject* object, S32 te) override { if (object->permModify()) @@ -2698,6 +2769,35 @@ BOOL LLSelectMgr::selectGetRootsModify() return TRUE; } +//----------------------------------------------------------------------------- +// selectGetSameRegion() - return TRUE if all objects are in same region +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetSameRegion() +{ + if (getSelection()->isEmpty()) + { + return TRUE; + } + LLViewerObject* object = getSelection()->getFirstObject(); + if (!object) + { + return FALSE; + } + LLViewerRegion* current_region = object->getRegion(); + + for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); + iter != getSelection()->root_end(); iter++) + { + LLSelectNode* node = *iter; + object = node->getObject(); + if (!node->mValid || !object || current_region != object->getRegion()) + { + return FALSE; + } + } + + return TRUE; +} //----------------------------------------------------------------------------- // selectGetNonPermanentEnforced() - return TRUE if all objects are not @@ -3554,22 +3654,20 @@ bool LLSelectMgr::confirmDelete(const LLSD& notification, const LLSD& response, // attempt to derez into the trash. LLDeRezInfo info(DRD_TRASH, trash_id); LLSelectMgr::getInstance()->sendListToRegions("DeRezObject", - packDeRezHeader, - packObjectLocalID, - (void*) &info, - SEND_ONLY_ROOTS); + packDeRezHeader, + packObjectLocalID, + logNoOp, + (void*) &info, + SEND_ONLY_ROOTS); // VEFFECT: Delete Object - one effect for all deletes - if(!gSavedSettings.getBOOL("DisablePointAtAndBeam")) + if (!gSavedSettings.getBOOL("DisablePointAtAndBeam") && (LLSelectMgr::getInstance()->mSelectedObjects->mSelectType != SELECT_TYPE_HUD)) { - if (LLSelectMgr::getInstance()->mSelectedObjects->mSelectType != SELECT_TYPE_HUD) - { - LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); - effectp->setPositionGlobal( LLSelectMgr::getInstance()->getSelectionCenterGlobal() ); - effectp->setColor(LLColor4U(gAgent.getEffectColor())); - F32 duration = 0.5f; - duration += LLSelectMgr::getInstance()->mSelectedObjects->getObjectCount() / 64.f; - effectp->setDuration(duration); - } + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp->setPositionGlobal( LLSelectMgr::getInstance()->getSelectionCenterGlobal() ); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + F32 duration = 0.5f; + duration += LLSelectMgr::getInstance()->mSelectedObjects->getObjectCount() / 64.f; + effectp->setDuration(duration); } gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR); @@ -3594,6 +3692,7 @@ void LLSelectMgr::selectForceDelete() "ObjectDelete", packDeleteHeader, packObjectLocalID, + logNoOp, (void*)TRUE, SEND_ONLY_ROOTS); } @@ -3767,7 +3866,7 @@ void LLSelectMgr::selectDuplicate(const LLVector3& offset, BOOL select_copy) data.offset = offset; data.flags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); - sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); + sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, logNoOp, &data, SEND_ONLY_ROOTS); if (select_copy) { @@ -3822,7 +3921,7 @@ void LLSelectMgr::repeatDuplicate() data.offset = LLVector3::zero; data.flags = 0x0; - sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); + sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, logNoOp, &data, SEND_ONLY_ROOTS); // move current selection based on delta from duplication position and update duplication position for (LLObjectSelection::root_iterator iter = getSelection()->root_begin(); @@ -3901,7 +4000,7 @@ void LLSelectMgr::selectDuplicateOnRay(const LLVector3 &ray_start_region, data.mFlags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); sendListToRegions("ObjectDuplicateOnRay", - packDuplicateOnRayHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); + packDuplicateOnRayHead, packObjectLocalID, logNoOp, &data, SEND_ONLY_ROOTS); if (select_copy) { @@ -3919,21 +4018,19 @@ void LLSelectMgr::packDuplicateOnRayHead(void *user_data) msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); - LLUUID group_id = gAgent.getGroupID(); - // Apply always rez under owned land group setting for objects - // duplicated by shift-dragging or duplicating - if (gSavedSettings.getBOOL("AscentAlwaysRezInGroup")) + // Rez under Land Group + static LLCachedControl AlchemyRezUnderLandGroup(gSavedSettings, "AscentAlwaysRezInGroup"); + LLUUID group_id = gAgent.getGroupID(); + if (AlchemyRezUnderLandGroup) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (gAgent.isInGroup(parcel->getGroupID())) - { - group_id = parcel->getGroupID(); - } - else if (gAgent.isInGroup(parcel->getOwnerID())) - { - group_id = parcel->getOwnerID(); - } + LLParcel* land_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // Is the agent in the land group + if (gAgent.isInGroup(land_parcel->getGroupID())) + group_id = land_parcel->getGroupID(); + // Is the agent in the land group (the group owns the land) + else if(gAgent.isInGroup(land_parcel->getOwnerID())) + group_id = land_parcel->getOwnerID(); } msg->addUUIDFast(_PREHASH_GroupID, group_id); @@ -3968,6 +4065,7 @@ void LLSelectMgr::sendMultipleUpdate(U32 type) "MultipleObjectUpdate", packAgentAndSessionID, packMultipleUpdate, + logNoOp, &type, send_type); } @@ -4031,7 +4129,7 @@ void LLSelectMgr::sendOwner(const LLUUID& owner_id, data.group_id = group_id; data.override = override; - sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); + sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, logNoOp, &data, SEND_ONLY_ROOTS); } // static @@ -4055,7 +4153,7 @@ void LLSelectMgr::packOwnerHead(void *user_data) void LLSelectMgr::sendGroup(const LLUUID& group_id) { LLUUID local_group_id(group_id); - sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, &local_group_id, SEND_ONLY_ROOTS); + sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, logNoOp, &local_group_id, SEND_ONLY_ROOTS); } @@ -4079,7 +4177,7 @@ void LLSelectMgr::sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, con LLBuyData buy; buy.mCategoryID = category_id; buy.mSaleInfo = sale_info; - sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, &buy, SEND_ONLY_ROOTS); + sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, logNoOp, &buy, SEND_ONLY_ROOTS); } // static @@ -4123,7 +4221,7 @@ void LLSelectMgr::selectionSetObjectPermissions(U8 field, data.mMask = mask; data.mOverride = override; - sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, &data, SEND_ONLY_ROOTS); + sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, logNoOp, &data, SEND_ONLY_ROOTS); } void LLSelectMgr::packPermissionsHead(void* user_data) @@ -4166,6 +4264,7 @@ void LLSelectMgr::deselectAll() "ObjectDeselect", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_INDIVIDUALS); @@ -4196,6 +4295,7 @@ void LLSelectMgr::deselectAllForStandingUp() "ObjectDeselect", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_INDIVIDUALS); @@ -4307,19 +4407,21 @@ void LLSelectMgr::selectionSetObjectName(const std::string& name) std::string name_copy(name); // we only work correctly if 1 object is selected. - if(mSelectedObjects->getRootObjectCount() /*== 1*/) // Singu Note: It was requested that the user be able to bulk rename + if(mSelectedObjects->getRootObjectCount() >= 1) // { sendListToRegions("ObjectName", packAgentAndSessionID, packObjectName, + logNoOp, (void*)(&name_copy), SEND_ONLY_ROOTS); } - else if(mSelectedObjects->getObjectCount() /*== 1*/) + else if(mSelectedObjects->getObjectCount() >= 1) // { sendListToRegions("ObjectName", packAgentAndSessionID, packObjectName, + logNoOp, (void*)(&name_copy), SEND_INDIVIDUALS); } @@ -4330,19 +4432,21 @@ void LLSelectMgr::selectionSetObjectDescription(const std::string& desc) std::string desc_copy(desc); // we only work correctly if 1 object is selected. - if (mSelectedObjects->getRootObjectCount() /*== 1*/) // Singu Note: It was requested that the user be able to bulk change description + if(mSelectedObjects->getRootObjectCount() >= 1) // { sendListToRegions("ObjectDescription", packAgentAndSessionID, packObjectDescription, + logNoOp, (void*)(&desc_copy), SEND_ONLY_ROOTS); } - else if (mSelectedObjects->getObjectCount() /*== 1*/) + else if(mSelectedObjects->getObjectCount() >= 1) // { sendListToRegions("ObjectDescription", packAgentAndSessionID, packObjectDescription, + logNoOp, (void*)(&desc_copy), SEND_INDIVIDUALS); } @@ -4356,6 +4460,7 @@ void LLSelectMgr::selectionSetObjectCategory(const LLCategory& category) sendListToRegions("ObjectCategory", packAgentAndSessionID, packObjectCategory, + logNoOp, (void*)(&category), SEND_ONLY_ROOTS); } @@ -4365,6 +4470,7 @@ void LLSelectMgr::selectionSetObjectSaleInfo(const LLSaleInfo& sale_info) sendListToRegions("ObjectSaleInfo", packAgentAndSessionID, packObjectSaleInfo, + logNoOp, (void*)(&sale_info), SEND_ONLY_ROOTS); } @@ -4398,10 +4504,14 @@ void LLSelectMgr::sendAttach(U8 attachment_point, bool replace) "ObjectAttach", packAgentIDAndSessionAndAttachment, packObjectIDAndRotation, + logAttachmentRequest, &attachment_point, SEND_ONLY_ROOTS ); if (!build_mode) { + // After "ObjectAttach" server will unsubscribe us from properties updates + // so either deselect objects or resend selection after attach packet reaches server + // In case of build_mode LLPanelObjectInventory::refresh() will deal with selection deselectAll(); } } @@ -4418,6 +4528,7 @@ void LLSelectMgr::sendDetach() "ObjectDetach", packAgentAndSessionID, packObjectLocalID, + logDetachRequest, NULL, SEND_ONLY_ROOTS ); } @@ -4434,6 +4545,7 @@ void LLSelectMgr::sendDropAttachment() "ObjectDrop", packAgentAndSessionID, packObjectLocalID, + logDetachRequest, NULL, SEND_ONLY_ROOTS); } @@ -4453,6 +4565,7 @@ void LLSelectMgr::sendLink() "ObjectLink", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_ONLY_ROOTS); } @@ -4468,7 +4581,7 @@ void LLSelectMgr::sendDelink() { //on delink, any modifyable object should f() {} - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (object->permModify()) { @@ -4490,6 +4603,7 @@ void LLSelectMgr::sendDelink() "ObjectDelink", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_INDIVIDUALS); } @@ -4542,6 +4656,7 @@ void LLSelectMgr::sendSelect() "ObjectSelect", packAgentAndSessionID, packObjectLocalID, + logNoOp, NULL, SEND_INDIVIDUALS); } @@ -4563,7 +4678,7 @@ void LLSelectMgr::selectionDump() { struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { object->dump(); return true; @@ -4576,7 +4691,7 @@ void LLSelectMgr::saveSelectedObjectColors() { struct f : public LLSelectedNodeFunctor { - virtual bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { node->saveColors(); return true; @@ -4585,12 +4700,25 @@ void LLSelectMgr::saveSelectedObjectColors() getSelection()->applyToNodes(&func); } +void LLSelectMgr::saveSelectedShinyColors() +{ + struct f : public LLSelectedNodeFunctor + { + bool apply(LLSelectNode* node) override + { + node->saveShinyColors(); + return true; + } + } func; + getSelection()->applyToNodes(&func); +} + void LLSelectMgr::saveSelectedObjectTextures() { // invalidate current selection so we update saved textures struct f : public LLSelectedNodeFunctor { - virtual bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { node->mValid = FALSE; return true; @@ -4618,7 +4746,8 @@ void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type) EActionType mActionType; LLSelectMgr* mManager; f(EActionType a, LLSelectMgr* p) : mActionType(a), mManager(p) {} - virtual bool apply(LLSelectNode* selectNode) + + bool apply(LLSelectNode* selectNode) override { LLViewerObject* object = selectNode->getObject(); if (!object) @@ -4678,13 +4807,21 @@ struct LLSelectMgrApplyFlags : public LLSelectedObjectFunctor LLSelectMgrApplyFlags(U32 flags, BOOL state) : mFlags(flags), mState(state) {} U32 mFlags; BOOL mState; - virtual bool apply(LLViewerObject* object) + + bool apply(LLViewerObject* object) override { - if ( object->permModify() && // preemptive permissions check - object->isRoot()) // don't send for child objects + if ( object->permModify()) { - object->setFlags( mFlags, mState); - } + if (object->isRoot()) // don't send for child objects + { + object->setFlags( mFlags, mState); + } + else if (FLAGS_WORLD & mFlags && ((LLViewerObject*)object->getRoot())->isSelected()) + { + // FLAGS_WORLD are shared by all items in linkset + object->setFlagsWithoutUpdate(FLAGS_WORLD & mFlags, mState); + } + }; return true; } }; @@ -4759,23 +4896,19 @@ void LLSelectMgr::packAgentAndSessionAndGroupID(void* user_data) // static void LLSelectMgr::packDuplicateHeader(void* data) { - LLUUID group_id(gAgent.getGroupID()); - - // Apply always rez under owned land group setting for objects - // duplicated by shift-dragging or duplicating - if (gSavedSettings.getBOOL("AscentAlwaysRezInGroup")) + // Rez under Land Group + static LLCachedControl AlchemyRezUnderLandGroup(gSavedSettings, "AscentAlwaysRezInGroup"); + LLUUID group_id = gAgent.getGroupID(); + if (AlchemyRezUnderLandGroup) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (gAgent.isInGroup(parcel->getGroupID())) - { - group_id = parcel->getGroupID(); - } - else if (gAgent.isInGroup(parcel->getOwnerID())) - { - group_id = parcel->getOwnerID(); - } + LLParcel* land_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // Is the agent in the land group + if (gAgent.isInGroup(land_parcel->getGroupID())) + group_id = land_parcel->getGroupID(); + // Is the agent in the land group (the group owns the land) + else if(gAgent.isInGroup(land_parcel->getOwnerID())) + group_id = land_parcel->getOwnerID(); } - packAgentAndSessionAndGroupID(&group_id); LLDuplicateData* dup_data = (LLDuplicateData*) data; @@ -4934,10 +5067,12 @@ void LLSelectMgr::packPermissions(LLSelectNode* node, void *user_data) void LLSelectMgr::sendListToRegions(const std::string& message_name, void (*pack_header)(void *user_data), void (*pack_body)(LLSelectNode* node, void *user_data), + void (*log_func)(LLSelectNode* node, void *user_data), void *user_data, ESendType send_type) { LLSelectNode* node; + LLSelectNode* linkset_root = NULL; LLViewerRegion* last_region; LLViewerRegion* current_region; @@ -4945,10 +5080,12 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, S32 packets_sent = 0; S32 objects_in_this_packet = 0; + bool link_operation = message_name == "ObjectLink"; + //clear update override data (allow next update through) struct f : public LLSelectedNodeFunctor { - virtual bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { node->mLastPositionLocal.setVec(0,0,0); node->mLastRotation = LLQuaternion(); @@ -4964,7 +5101,8 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, { std::queue& nodes_to_send; push_all(std::queue& n) : nodes_to_send(n) {} - virtual bool apply(LLSelectNode* node) + + bool apply(LLSelectNode* node) override { if (node->getObject()) { @@ -4978,7 +5116,8 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, std::queue& nodes_to_send; bool mRoots; push_some(std::queue& n, bool roots) : nodes_to_send(n), mRoots(roots) {} - virtual bool apply(LLSelectNode* node) + + bool apply(LLSelectNode* node) override { if (node->getObject()) { @@ -5053,8 +5192,16 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, && (! gMessageSystem->isSendFull(NULL)) && (objects_in_this_packet < MAX_OBJECTS_PER_PACKET)) { + if (link_operation && linkset_root == NULL) + { + // linksets over 254 will be split into multiple messages, + // but we need to provide same root for all messages or we will get separate linksets + linkset_root = node; + } // add another instance of the body of the data (*pack_body)(node, user_data); + // do any related logging + (*log_func)(node, user_data); ++objects_sent; ++objects_in_this_packet; @@ -5079,6 +5226,22 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, gMessageSystem->newMessage(message_name.c_str()); (*pack_header)(user_data); + if (linkset_root != NULL) + { + if (current_region != last_region) + { + // root should be in one region with the child, reset it + linkset_root = NULL; + } + else + { + // add root instance into new message + (*pack_body)(linkset_root, user_data); + ++objects_sent; + ++objects_in_this_packet; + } + } + // don't move to the next object, we still need to add the // body data. } @@ -5203,7 +5366,8 @@ void LLSelectMgr::processObjectProperties(LLMessageSystem* msg, void** user_data { LLUUID mID; f(const LLUUID& id) : mID(id) {} - virtual bool apply(LLSelectNode* node) + + bool apply(LLSelectNode* node) override { return (node->getObject() && node->getObject()->mID == mID); } @@ -5356,7 +5520,8 @@ void LLSelectMgr::processObjectPropertiesFamily(LLMessageSystem* msg, void** use { LLUUID mID; f(const LLUUID& id) : mID(id) {} - virtual bool apply(LLSelectNode* node) + + bool apply(LLSelectNode* node) override { return (node->getObject() && node->getObject()->mID == mID); } @@ -5434,7 +5599,7 @@ void LLSelectMgr::updateSilhouettes() { struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { object->setChanged(LLXform::SILHOUETTE); return true; @@ -5735,9 +5900,7 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) } LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getFocusedObjectID(); - // //for (S32 pass = 0; pass < 2; pass++) - // { for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); iter != mSelectedObjects->end(); iter++) @@ -5854,6 +6017,7 @@ LLSelectNode::LLSelectNode(LLViewerObject* object, BOOL glow) { selectAllTEs(FALSE); saveColors(); + saveShinyColors(); } LLSelectNode::LLSelectNode(const LLSelectNode& nodep) @@ -5899,6 +6063,11 @@ LLSelectNode::LLSelectNode(const LLSelectNode& nodep) { mSavedColors.push_back(*color_iter); } + mSavedShinyColors.clear(); + for (color_iter = nodep.mSavedShinyColors.begin(); color_iter != nodep.mSavedShinyColors.end(); ++color_iter) + { + mSavedShinyColors.push_back(*color_iter); + } saveTextures(nodep.mSavedTextures); } @@ -5951,6 +6120,11 @@ S32 LLSelectNode::getLastSelectedTE() return mLastTESelected; } +S32 LLSelectNode::getLastOperatedTE() +{ + return mLastTESelected; +} + LLViewerObject* LLSelectNode::getObject() { if (!mObject) @@ -5982,6 +6156,26 @@ void LLSelectNode::saveColors() } } +void LLSelectNode::saveShinyColors() +{ + if (mObject.notNull()) + { + mSavedShinyColors.clear(); + for (S32 i = 0; i < mObject->getNumTEs(); i++) + { + const LLMaterialPtr mat = mObject->getTE(i)->getMaterialParams(); + if (!mat.isNull()) + { + mSavedShinyColors.push_back(LLColor4(mat->getSpecularLightColor())); + } + else + { + mSavedShinyColors.push_back(LLColor4::white); + } + } + } +} + void LLSelectNode::saveTextures(const uuid_vec_t& textures) { if (mObject.notNull()) @@ -6212,7 +6406,6 @@ void LLSelectNode::renderOneWireframe(const LLColor4& color) { gGL.blendFunc(LLRender::BF_SOURCE_COLOR, LLRender::BF_ONE); LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL); - if (shader) { gGL.diffuseColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); @@ -6245,6 +6438,7 @@ void LLSelectNode::renderOneWireframe(const LLColor4& color) LLGLEnable offset; gGL.setSceneBlendType(LLRender::BT_ALPHA); + gGL.diffuseColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); gGL.setPolygonOffset(3.f, 3.f); gGL.setLineWidth(3.f); @@ -6360,10 +6554,11 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color) gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); gGL.begin(LLRender::LINES); { + gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); + for(S32 i = 0; i < (S32)mSilhouetteVertices.size(); i += 2) { u_coord += u_divisor * LLSelectMgr::sHighlightUScale; - gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); gGL.texCoord2f( u_coord, v_coord ); gGL.vertex3fv( mSilhouetteVertices[i].mV); u_coord += u_divisor * LLSelectMgr::sHighlightUScale; @@ -6450,11 +6645,6 @@ void dialog_refresh_all() // and call into this function. Yuck. LLSelectMgr::getInstance()->mUpdateSignal(); - if (gNoRender) - { - return; - } - // *TODO: Eliminate all calls into outside classes below, make those // objects register with the update signal. @@ -6742,7 +6932,7 @@ void LLSelectMgr::undo() { BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); LLUUID group_id(gAgent.getGroupID()); - sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); + sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, logNoOp, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); } //----------------------------------------------------------------------------- @@ -6760,7 +6950,7 @@ void LLSelectMgr::redo() { BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); LLUUID group_id(gAgent.getGroupID()); - sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); + sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, logNoOp, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); } //----------------------------------------------------------------------------- @@ -6875,7 +7065,7 @@ void LLSelectMgr::validateSelection() { struct f : public LLSelectedObjectFunctor { - virtual bool apply(LLViewerObject* object) + bool apply(LLViewerObject* object) override { if (!LLSelectMgr::getInstance()->canSelectObject(object)) { @@ -7628,7 +7818,7 @@ LLSelectNode* LLObjectSelection::getFirstMoveableNode(BOOL get_root_first) { struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); return obj && obj->permMove() && !obj->isPermanentEnforced(); @@ -7645,7 +7835,7 @@ LLViewerObject* LLObjectSelection::getFirstCopyableObject(BOOL get_parent) { struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); return obj && obj->permCopy() && !obj->isAttachment(); @@ -7664,7 +7854,7 @@ LLViewerObject* LLObjectSelection::getFirstDeleteableObject() struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); // you can delete an object if you are the owner @@ -7693,7 +7883,7 @@ LLViewerObject* LLObjectSelection::getFirstEditableObject(BOOL get_parent) { struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); return obj && obj->permModify(); @@ -7709,7 +7899,7 @@ LLViewerObject* LLObjectSelection::getFirstMoveableObject(BOOL get_parent) { struct f : public LLSelectedNodeFunctor { - bool apply(LLSelectNode* node) + bool apply(LLSelectNode* node) override { LLViewerObject* obj = node->getObject(); return obj && obj->permMove() && !obj->isPermanentEnforced(); diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 91e7fcaf3..5252c5a17 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -146,6 +146,7 @@ public: void selectTE(S32 te_index, BOOL selected); BOOL isTESelected(S32 te_index); S32 getLastSelectedTE(); + S32 getLastOperatedTE(); S32 getTESelectMask() { return mTESelectMask; } void renderOneWireframe(const LLColor4& color); void renderOneSilhouette(const LLColor4 &color); @@ -155,6 +156,7 @@ public: void setObject(LLViewerObject* object); // *NOTE: invalidate stored textures and colors when # faces change void saveColors(); + void saveShinyColors(); void saveTextures(const uuid_vec_t& textures); void saveTextureScaleRatios(LLRender::eTexIndex index_to_query); @@ -191,6 +193,7 @@ public: std::string mSitName; U64 mCreationDate; std::vector mSavedColors; + std::vector mSavedShinyColors; uuid_vec_t mSavedTextures; std::vector mTextureScaleRatios; std::vector mSilhouetteVertices; // array of vertices to render silhouette of object @@ -206,6 +209,7 @@ protected: class LLObjectSelection : public LLRefCount { friend class LLSelectMgr; + friend class LLSafeHandle; protected: ~LLObjectSelection(); @@ -218,7 +222,7 @@ public: { bool operator()(LLSelectNode* node) { - return (node->getObject() != NULL); + return (node->getObject() != nullptr); } }; typedef boost::filter_iterator iterator; @@ -229,7 +233,7 @@ public: { bool operator()(LLSelectNode* node) { - return (node->getObject() != NULL) && node->mValid; + return (node->getObject() != nullptr) && node->mValid; } }; typedef boost::filter_iterator valid_iterator; @@ -267,8 +271,8 @@ public: BOOL isEmpty() const; - LLSelectNode* getFirstNode(LLSelectedNodeFunctor* func = NULL); - LLSelectNode* getFirstRootNode(LLSelectedNodeFunctor* func = NULL, BOOL non_root_ok = FALSE); + LLSelectNode* getFirstNode(LLSelectedNodeFunctor* func = nullptr); + LLSelectNode* getFirstRootNode(LLSelectedNodeFunctor* func = nullptr, BOOL non_root_ok = FALSE); LLViewerObject* getFirstSelectedObject(LLSelectedNodeFunctor* func, BOOL get_parent = FALSE); LLViewerObject* getFirstObject(); LLViewerObject* getFirstRootObject(BOOL non_root_ok = FALSE); @@ -298,8 +302,8 @@ public: F32 getSelectedLinksetPhysicsCost(); S32 getSelectedObjectRenderCost(); - F32 getSelectedObjectStreamingCost(S32* total_bytes = NULL, S32* visible_bytes = NULL); - U32 getSelectedObjectTriangleCount(S32* vcount = NULL); + F32 getSelectedObjectStreamingCost(S32* total_bytes = nullptr, S32* visible_bytes = nullptr); + U32 getSelectedObjectTriangleCount(S32* vcount = nullptr); S32 getTECount(); S32 getRootObjectCount(); @@ -323,6 +327,15 @@ public: bool applyToRootNodes(LLSelectedNodeFunctor* func, bool firstonly = false); bool applyToNodes(LLSelectedNodeFunctor* func, bool firstonly = false); + /* + * Used to apply (no-copy) textures to the selected object or + * selected face/faces of the object. + * This method moves (no-copy) texture to the object's inventory + * and doesn't make copy of the texture for each face. + * Then this only texture is used for all selected faces. + */ + void applyNoCopyTextureToTEs(LLViewerInventoryItem* item); + ESelectType getSelectType() const { return mSelectType; } private: @@ -333,10 +346,8 @@ private: void deleteAllNodes(); void cleanupNodes(); - -private: list_t mList; - const LLObjectSelection &operator=(const LLObjectSelection &); + const LLObjectSelection& operator=(const LLObjectSelection&) = delete; LLPointer mPrimaryObject; std::map, LLSelectNode*> mSelectNodeMap; @@ -350,6 +361,9 @@ typedef LLSafeHandle LLObjectSelectionHandle; extern template class LLSelectMgr* LLSingleton::getInstance(); #endif +// For use with getFirstTest() +struct LLSelectGetFirstTest; + class LLSelectMgr : public LLEditMenuHandler, public LLSingleton { public: @@ -383,20 +397,19 @@ public: static void cleanupGlobals(); // LLEditMenuHandler interface - virtual BOOL canUndo() const; - virtual void undo(); + BOOL canUndo() const override; + void undo() override; - virtual BOOL canRedo() const; - virtual void redo(); + BOOL canRedo() const override; + void redo() override; - virtual BOOL canDoDelete() const; - virtual void doDelete(); + BOOL canDoDelete() const override; + void doDelete() override; - virtual void selectAll(); - virtual BOOL canSelectAll() const; - - virtual void deselect(); - virtual BOOL canDeselect() const; + void selectAll() override; + BOOL canSelectAll() const override; + void deselect() override; + BOOL canDeselect() const override; virtual void duplicate(); virtual BOOL canDuplicate() const; @@ -516,6 +529,7 @@ public: //////////////////////////////////////////////////////////////// void saveSelectedObjectTransform(EActionType action_type); void saveSelectedObjectColors(); + void saveSelectedShinyColors(); void saveSelectedObjectTextures(); // Sets which texture channel to query for scale and rot of display @@ -544,6 +558,7 @@ public: void selectionSetColorOnly(const LLColor4 &color); // Set only the RGB channels void selectionSetAlphaOnly(const F32 alpha); // Set only the alpha channel void selectionRevertColors(); + void selectionRevertShinyColors(); BOOL selectionRevertTextures(); void selectionSetBumpmap( U8 bumpmap ); void selectionSetTexGen( U8 texgen ); @@ -587,6 +602,9 @@ public: BOOL selectGetRootsModify(); BOOL selectGetModify(); + // returns TRUE if all objects are in same region + BOOL selectGetSameRegion(); + // returns TRUE if is all objects are non-permanent-enforced BOOL selectGetRootsNonPermanentEnforced(); BOOL selectGetNonPermanentEnforced(); @@ -728,6 +746,7 @@ private: void sendListToRegions( const std::string& message_name, void (*pack_header)(void *user_data), void (*pack_body)(LLSelectNode* node, void *user_data), + void (*log_func)(LLSelectNode* node, void *user_data), void *user_data, ESendType send_type); @@ -763,6 +782,9 @@ private: static void packHingeHead(void *user_data); static void packPermissionsHead(void* user_data); static void packGodlikeHead(void* user_data); + static void logNoOp(LLSelectNode* node, void *user_data); + static void logAttachmentRequest(LLSelectNode* node, void *user_data); + static void logDetachRequest(LLSelectNode* node, void *user_data); static bool confirmDelete(const LLSD& notification, const LLSD& response, LLObjectSelectionHandle handle); public: diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index d22178377..e4523889a 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -1,5 +1,7 @@ +// 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 llurlsimstring.cpp (was llsimurlstring.cpp) + * @file llslurl.cpp (was llsimurlstring.cpp) * @brief Handles "SLURL fragments" like Ahern/123/45 for * startup processing, login screen, prefs, etc. * @@ -48,7 +50,8 @@ const char* LLSLURL::SLURL_COM = "slurl.com"; const char* LLSLURL::WWW_SLURL_COM = "www.slurl.com"; const char* LLSLURL::MAPS_SECONDLIFE_COM = "maps.secondlife.com"; -const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info"; +const char* LLSLURL::SLURL_X_GRID_INFO_SCHEME = "x-grid-info"; +const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info"; // <- deprecated! const char* LLSLURL::SLURL_APP_PATH = "app"; const char* LLSLURL::SLURL_REGION_PATH = "region"; const char* LLSLURL::SIM_LOCATION_HOME = "home"; @@ -214,7 +217,8 @@ LLSLURL::LLSLURL(const std::string& slurl) } else if((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME) || (slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) || - (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)) + (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_INFO_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)) // deprecated legacy { // We're dealing with either a Standalone style slurl or slurl.com slurl if ((slurl_uri.hostName() == LLSLURL::SLURL_COM) || @@ -239,7 +243,7 @@ LLSLURL::LLSLURL(const std::string& slurl) // As it's a Standalone grid/open, we will always have a hostname, as Standalone/open style // urls are properly formed, unlike the stinky maingrid style - mGrid = slurl_uri.hostName(); + mGrid = slurl_uri.hostNameAndPort(); } if (path_array.size() == 0) { @@ -266,7 +270,7 @@ LLSLURL::LLSLURL(const std::string& slurl) } else { - // not a valid https/http/x-grid-location-info slurl, so it'll likely just be a URL + // not a valid https/http/x-grid-info slurl, so it'll likely just be a URL return; } } @@ -304,7 +308,14 @@ LLSLURL::LLSLURL(const std::string& slurl) // at this point, head of the path array should be [ , , , ] where x, y and z // are collectively optional // are optional + mRegion = LLURI::unescape(path_array[0].asString()); + + if(LLStringUtil::containsNonprintable(mRegion)) + { + LLStringUtil::stripNonprintable(mRegion); + } + path_array.erase(0); // parse the x, y, and optionally z @@ -313,11 +324,11 @@ LLSLURL::LLSLURL(const std::string& slurl) mPosition = LLVector3(path_array); // this construction handles LLSD without all components (values default to 0.f) if((F32(mPosition[VX]) < 0.f) || - (mPosition[VX] > 8192.f/*REGION_WIDTH_METERS*/) || + (mPosition[VX] > 8192.f) || (F32(mPosition[VY]) < 0.f) || - (mPosition[VY] > 8192.f/*REGION_WIDTH_METERS*/) || + (mPosition[VY] > 8192.f) || (F32(mPosition[VZ]) < 0.f) || - (mPosition[VZ] > 8192.f/*REGION_HEIGHT_METERS*/)) + (mPosition[VZ] > 8192.f)) { mType = INVALID; return; @@ -444,9 +455,9 @@ std::string LLSLURL::getSLURLString() const app_url << SYSTEM_GRID_APP_SLURL_BASE << "/" << mAppCmd; else app_url << llformat(DEFAULT_APP_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()) << "/" << mAppCmd; - for(LLSD::array_const_iterator i = mAppPath.beginArray(); + for(auto i = mAppPath.beginArray(); i != mAppPath.endArray(); - i++) + ++i) { app_url << "/" << i->asString(); } diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h index b86cf7949..3beff26c7 100644 --- a/indra/newview/llslurl.h +++ b/indra/newview/llslurl.h @@ -44,6 +44,7 @@ public: static const char* WWW_SLURL_COM; static const char* SECONDLIFE_COM; static const char* MAPS_SECONDLIFE_COM; + static const char* SLURL_X_GRID_INFO_SCHEME; static const char* SLURL_X_GRID_LOCATION_INFO_SCHEME; static LLSLURL START_LOCATION; static const char* SIM_LOCATION_HOME; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 08c5005a9..a3ce7c158 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -217,6 +217,7 @@ #include "generichandlers.h" // +#include "floaterlocalassetbrowse.h" #include "llpanellogin.h" //#include "llfloateravatars.h" //#include "llactivation.h" @@ -859,7 +860,7 @@ bool idle_startup() { LL_DEBUGS("AppInit") << "STATE_BROWSER_INIT" << LL_ENDL; //std::string msg = LLTrans::getString("LoginInitializingBrowser"); - //set_startup_status(0.03f, msg.c_str(), gAgent.mMOTD.c_str()); + //set_startup_status(0.03f, msg.c_str(), gAgent.mMOTD); display_startup(); // LLViewerMedia::initBrowser(); LLStartUp::setStartupState( STATE_LOGIN_SHOW ); @@ -890,6 +891,8 @@ bool idle_startup() LLToolMgr::getInstance()->initTools(); display_startup(); + // Load local textures now, maybe someone wants to use them in UI (why?) + LocalAssetBrowser::instance(); // // Quickly get something onscreen to look at. gViewerWindow->initWorldUI(); display_startup(); @@ -944,7 +947,11 @@ bool idle_startup() display_startup(); // Push our window frontmost - gViewerWindow->getWindow()->show(); + // Singu Note: Actually, don't! But flash the window to let the user know + auto& window(*gViewerWindow->getWindow()); + window.show(false); + if (gSavedSettings.getBOOL("LiruFlashWhenMinimized")) // No, we're not minimized, but if you flash my bar, I will give you the biggest SIGSEGV ~Liru <3 + window.flashIcon(5.f); display_startup(); // DEV-16927. The following code removes errant keystrokes that happen while the window is being @@ -1132,9 +1139,6 @@ bool idle_startup() //LLPanelLogin::close(); } - //For HTML parsing in text boxes. - LLTextEditor::setLinkColor( gSavedSettings.getColor4("HTMLLinkColor") ); - // Load URL History File LLURLHistory::loadFile("url_history.xml"); // Load media plugin cookies @@ -1586,9 +1590,9 @@ bool idle_startup() if (!secondlife || !boost::algorithm::iequals(lastname, "Resident")) { - name += " " + lastname; + name += ' ' + lastname; } - if (gSavedSettings.getBOOL("LiruGridInTitle")) gWindowTitle += "- " + gHippoGridManager->getCurrentGrid()->getGridName() + " "; + if (gSavedSettings.getBOOL("LiruGridInTitle")) gWindowTitle += "- " + gHippoGridManager->getCurrentGrid()->getGridName() + ' '; gViewerWindow->getWindow()->setTitle(gWindowTitle += "- " + name); if (!secondlife) @@ -2342,6 +2346,12 @@ bool idle_startup() } display_startup(); + + // *TODO : Uncomment that line once the whole grid migrated to SLM and suppress it from LLAgent::handleTeleportFinished() (llagent.cpp) + //check_merchant_status(); + + display_startup(); + // We're successfully logged in. gSavedSettings.setBOOL("FirstLoginThisInstall", FALSE); @@ -2673,15 +2683,15 @@ bool idle_startup() update_texture_fetch(); display_startup(); set_startup_status(0.9f + 0.1f * wearables_time / MAX_WEARABLES_TIME, - LLTrans::getString("LoginDownloadingClothing").c_str(), - gAgent.mMOTD.c_str()); + LLTrans::getString("LoginDownloadingClothing"), + gAgent.mMOTD); display_startup(); return TRUE; } if (STATE_CLEANUP == LLStartUp::getStartupState()) { - set_startup_status(1.0, "", ""); + set_startup_status(1.0, LLStringUtil::null, LLStringUtil::null); display_startup(); // Let the map know about the inventory. @@ -4121,7 +4131,7 @@ bool process_login_success_response(std::string& password, U32& first_sim_size_x LLWorldMap::gotMapServerURL(true); } - bool opensim = gHippoGridManager->getConnectedGrid()->isOpenSimulator(); + bool opensim = !gHippoGridManager->getConnectedGrid()->isSecondLife(); if (opensim) { std::string web_profile_url = response["web_profile_url"]; diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index c3178bff4..4dbb92f1c 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -56,6 +56,7 @@ #include "llpathfindingnavmeshstatus.h" #include "llimview.h" #include "lltextbox.h" +#include "lltrans.h" #include "llui.h" #include "llviewerparceloverlay.h" #include "llviewerregion.h" @@ -235,7 +236,7 @@ mIsNavMeshDirty(false) mSearchBevel->setVisible(show_search); mTextParcelName->setClickedCallback(boost::bind(onClickParcelInfo)); - mTextBalance->setClickedCallback(boost::bind(LLStatusBar::sendMoneyBalanceRequest)); + mTextBalance->setClickedCallback(boost::bind(LLStatusBar::sendMoneyBalanceRequest, true)); // TODO: Disable buying currency when connected to non-SL grids // that don't support currency yet -- MC @@ -672,8 +673,10 @@ void LLStatusBar::setUPC(S32 upc) } // static -void LLStatusBar::sendMoneyBalanceRequest() +void LLStatusBar::sendMoneyBalanceRequest(bool from_user) { + void cmdline_printchat(const std::string& message); + if (from_user) cmdline_printchat(LLTrans::getString("refreshing balance")); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_MoneyBalanceRequest); msg->nextBlockFast(_PREHASH_AgentData); @@ -895,7 +898,7 @@ public: if (tokens.size() == 1 && tokens[0].asString() == "request") { - LLStatusBar::sendMoneyBalanceRequest(); + LLStatusBar::sendMoneyBalanceRequest(true); return true; } return false; diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h index 1db2806c7..3acf9998f 100644 --- a/indra/newview/llstatusbar.h +++ b/indra/newview/llstatusbar.h @@ -66,7 +66,7 @@ public: void creditBalance(S32 credit); // Request the latest currency balance from the server - static void sendMoneyBalanceRequest(); + static void sendMoneyBalanceRequest(bool from_user = false); void setHealth(S32 percent); diff --git a/indra/newview/llstylemap.cpp b/indra/newview/llstylemap.cpp index a0c64505e..ff6159e4e 100644 --- a/indra/newview/llstylemap.cpp +++ b/indra/newview/llstylemap.cpp @@ -58,7 +58,7 @@ const LLStyleSP &LLStyleMap::lookupAgent(const LLUUID &source) LLStyleSP style(new LLStyle); if (source.notNull()) { - style->setColor(gSavedSettings.getColor4("HTMLLinkColor")); + style->setColor(gSavedSettings.getColor4("HTMLAgentColor")); std::string link = llformat("secondlife:///app/agent/%s/about",source.asString().c_str()); style->setLinkHREF(link); } @@ -77,7 +77,7 @@ const LLStyleSP &LLStyleMap::lookup(const LLUUID& id, const std::string& link) LLStyleSP style(new LLStyle); if (id.notNull() && !link.empty()) { - style->setColor(gSavedSettings.getColor4("HTMLLinkColor")); + style->setColor(gSavedSettings.getColor4("HTMLAgentColor")); style->setLinkHREF(link); } else @@ -102,6 +102,6 @@ void LLStyleMap::update() { LLStyleSP &style = iter->second; // Update the link color in case it has been changed. - style->setColor(gSavedSettings.getColor4("HTMLLinkColor")); + style->setColor(gSavedSettings.getColor4("HTMLAgentColor")); } } diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index b7358fa60..a64125b1d 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -695,11 +695,10 @@ void LLFloaterTexturePicker::draw() LLFolderView* folder_view = mInventoryPanel->getRootFolder(); if (!folder_view) return; - LLInventoryFilter* filter = folder_view->getFilter(); - if (!filter) return; + LLInventoryFilter& filter = folder_view->getFilter(); - bool is_filter_active = folder_view->getCompletedFilterGeneration() < filter->getCurrentGeneration() && - filter->isNotDefault(); + bool is_filter_active = folder_view->getCompletedFilterGeneration() < filter.getCurrentGeneration() && + filter.isNotDefault(); // After inventory panel filter is applied we have to update // constraint rect for the selected item because of folder view @@ -1306,7 +1305,7 @@ void LLTextureCtrl::setEnabled( BOOL enabled ) mCaption->setEnabled( enabled ); mEnable = enabled; - LLView::setEnabled( enabled ); + //LLView::setEnabled( enabled ); // } void LLTextureCtrl::setValid(BOOL valid ) diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index 718aa1106..5d43dfdf0 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -150,6 +150,8 @@ public: void onFloaterCommit(ETexturePickOp op); void onFloaterCommit(ETexturePickOp op, LLUUID id); // tag: vaa emerald local_asset_browser + bool canChange() { return mEnable; } // + // This call is returned when a drag is detected. Your callback // should return TRUE if the drag is acceptable. void setDragCallback(drag_n_drop_callback cb) { mDragCallback = cb; } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 1c970d57a..77b7eb1d0 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1241,7 +1241,8 @@ bool LLTextureFetchWorker::doWork(S32 param) if (region) { - std::string http_url = region->getHttpUrl() ; + std::string http_url = region->getViewerAssetUrl(); + if (http_url.empty()) http_url = region->getCapability("GetTexture"); if (!http_url.empty()) { if (mFTType != FTT_DEFAULT) diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index e26d65d93..c8f7334b8 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -42,7 +42,7 @@ #include "llappearancemgr.h" #include "lldictionary.h" #include "llfirstuse.h" -#include "llfloaterinventory.h" +#include "llfloateravatarpicker.h" #include "llfloatertools.h" #include "llgesturemgr.h" #include "llgiveinventory.h" @@ -52,40 +52,26 @@ #include "llinventorybridge.h" #include "llinventorydefines.h" #include "llinventoryfunctions.h" -#include "llinventorypanel.h" -#include "llmutelist.h" -#include "llnotify.h" +#include "llparcel.h" // Rez under Land Group #include "llpreviewnotecard.h" +#include "llrootview.h" #include "llselectmgr.h" #include "lltoolmgr.h" #include "lltrans.h" -#include "llviewertexturelist.h" -#include "llviewerinventory.h" -#include "llviewerobject.h" #include "llviewerobjectlist.h" +#include "llviewerparcelmgr.h" // Rez under Land Group #include "llviewerregion.h" #include "llviewerstats.h" #include "llviewerwindow.h" #include "llvoavatarself.h" -#include "llvolume.h" #include "llworld.h" #include "llpanelface.h" -// -#include "llappviewer.h" // System Folders -#include "llparcel.h" // always rez -#include "llviewerparcelmgr.h" // always rez -// // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1) #include "rlvhandler.h" #include "rlvlocks.h" // [/RLVa:KB] -// MAX ITEMS is based on (sizeof(uuid)+2) * count must be < MTUBYTES -// or 18 * count < 1200 => count < 1200/18 => 66. I've cut it down a -// bit from there to give some pad. -const S32 MAX_ITEMS = 42; - // syntactic sugar #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember)) @@ -288,15 +274,15 @@ void LLCategoryDropDescendentsObserver::done() std::set unique_ids; for(S32 i = 0; i < count; ++i) { - unique_ids.insert(items.at(i)->getUUID()); + unique_ids.insert(items.get(i)->getUUID()); } uuid_vec_t ids; std::back_insert_iterator copier(ids); std::copy(unique_ids.begin(), unique_ids.end(), copier); LLCategoryDropObserver* dropper; - dropper = new LLCategoryDropObserver(mObjectID, mSource); - dropper->fetchItems(ids); - if(dropper->isEverythingComplete()) + dropper = new LLCategoryDropObserver(ids, mObjectID, mSource); + dropper->startFetch(); + if (dropper->isDone()) { dropper->done(); } @@ -360,14 +346,15 @@ LLToolDragAndDrop::LLDragAndDropDictionary::LLDragAndDropDictionary() }; LLToolDragAndDrop::LLToolDragAndDrop() -: LLTool(std::string("draganddrop"), NULL), - mDragStartX(0), - mDragStartY(0), - mSource(SOURCE_AGENT), - mCursor(UI_CURSOR_NO), - mLastAccept(ACCEPT_NO), - mDrop(FALSE), - mCurItemIndex(0) +: LLTool(std::string("draganddrop"), NULL), + mCargoCount(0), + mDragStartX(0), + mDragStartY(0), + mSource(SOURCE_AGENT), + mCursor(UI_CURSOR_NO), + mLastAccept(ACCEPT_NO), + mDrop(FALSE), + mCurItemIndex(0) { } @@ -380,7 +367,7 @@ void LLToolDragAndDrop::setDragStart(S32 x, S32 y) BOOL LLToolDragAndDrop::isOverThreshold(S32 x,S32 y) { - static LLCachedControl drag_and_drop_threshold(gSavedSettings,"DragAndDropDistanceThreshold"); + static LLCachedControl drag_and_drop_threshold(gSavedSettings,"DragAndDropDistanceThreshold", 3); S32 mouse_delta_x = x - mDragStartX; S32 mouse_delta_y = y - mDragStartY; @@ -524,6 +511,7 @@ void LLToolDragAndDrop::beginMultiDrag( void LLToolDragAndDrop::endDrag() { + mEndDragSignal(); LLSelectMgr::getInstance()->unhighlightAll(); setMouseCapture(FALSE); } @@ -580,6 +568,8 @@ ECursorType LLToolDragAndDrop::acceptanceToCursor( EAcceptance acceptance ) mCursor = UI_CURSOR_NOLOCKED; break; + + case ACCEPT_NO: mCursor = UI_CURSOR_NO; break; @@ -674,33 +664,41 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, sOperationId++; } + // For people drag and drop we don't need an actual inventory object, + // instead we need the current cargo id, which should be a person id. + bool is_uuid_dragged = (mSource == SOURCE_PEOPLE); + if(top_view) { handled = TRUE; for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); - EAcceptance item_acceptance = ACCEPT_NO; handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); - if (handled) - { - // use sort order to determine priority of acceptance - *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); - } } - else + else if (is_uuid_dragged) { - return; + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } + if (handled) + { + // use sort order to determine priority of acceptance + *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); } } @@ -717,20 +715,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + EAcceptance item_acceptance; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - - EAcceptance item_acceptance; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } if (handled) @@ -747,20 +752,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); - if (!cargo) + // fix for EXT-3191 + if (cargo) { - handled = FALSE; - break; + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)cargo, + &item_acceptance, + mToolTipMsg); + } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); } - - EAcceptance item_acceptance = ACCEPT_NO; - handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, - mCargoTypes[mCurItemIndex], - (void*)cargo, - &item_acceptance, - mToolTipMsg); if (handled) { // use sort order to determine priority of acceptance @@ -780,17 +792,25 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + EAcceptance item_acceptance; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - EAcceptance item_acceptance; handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } @@ -802,6 +822,7 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, if ( !handled ) { + // *TODO: Suppress the "outbox" case once "marketplace" is used everywhere for everyone // Disallow drag and drop to 3D from the outbox const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); if (outbox_id.notNull()) @@ -816,7 +837,21 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, } } } - + // Disallow drag and drop to 3D from the marketplace + const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + if (marketplacelistings_id.notNull()) + { + for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++) + { + if (gInventory.isObjectDescendentOf(mCargoIDs[item_index], outbox_id)) + { + *acceptance = ACCEPT_NO; + mToolTipMsg = LLTrans::getString("TooltipOutboxDragToWorld"); + return; + } + } + } + dragOrDrop3D( x, y, mask, drop, acceptance ); } } @@ -929,8 +964,7 @@ void LLToolDragAndDrop::pick(const LLPickInfo& pick_info) const S32 item_index = mCurItemIndex; const EDragAndDropType dad_type = mCargoTypes[item_index]; // Call the right implementation function - (U32)callMemberFunction(*this, - LLDragAndDropDictionary::instance().get(dad_type, target)) + callMemberFunction(*this, LLDragAndDropDictionary::instance().get(dad_type, target)) (hit_obj, hit_face, pick_info.mKeyMask, TRUE); } } @@ -1170,6 +1204,7 @@ void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj, { switch (LLSelectMgr::getInstance()->getTextureChannel()) { + case 0: default: { @@ -1378,19 +1413,21 @@ void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target, msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - // Alway rez objects as land group if available. - if (gSavedSettings.getBOOL("AscentAlwaysRezInGroup")) + // Rez under Land Group + static LLCachedControl AlchemyRezUnderLandGroup(gSavedSettings, "AscentAlwaysRezInGroup"); + LLUUID group_id = gAgent.getGroupID(); + if (AlchemyRezUnderLandGroup) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if(gAgent.isInGroup(parcel->getGroupID())) - msg->addUUIDFast(_PREHASH_GroupID, parcel->getGroupID()); - else if(gAgent.isInGroup(parcel->getOwnerID())) - msg->addUUIDFast(_PREHASH_GroupID, parcel->getOwnerID()); - else - msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + LLParcel* land_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // Is the agent in the land group + if (gAgent.isInGroup(land_parcel->getGroupID())) + group_id = land_parcel->getGroupID(); + // Is the agent in the land group (the group owns the land) + else if (gAgent.isInGroup(land_parcel->getOwnerID())) + group_id = land_parcel->getOwnerID(); } - else - msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + + msg->addUUIDFast(_PREHASH_GroupID, group_id); msg->nextBlock("RezData"); // if it's being rezzed from task inventory, we need to enable @@ -1453,6 +1490,7 @@ void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target, gInventory.deleteObject(item->getUUID()); gInventory.notifyObservers(); } + // if (gSavedSettings.getBOOL("BroadcastViewerEffects")) { @@ -1466,6 +1504,7 @@ void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target, // } // + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_REZ_COUNT); } @@ -1521,6 +1560,7 @@ void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj, // *FIX: only show this if panel not expanded? gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS); } + // if (gSavedSettings.getBOOL("BroadcastViewerEffects")) { @@ -1537,18 +1577,6 @@ void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj, gFloaterTools->dirty(); } -struct LLGiveInventoryInfo -{ - LLUUID mToAgentID; - LLUUID mInventoryObjectID; - LLUUID mIMSessionID; - LLGiveInventoryInfo(const LLUUID& to_agent, const LLUUID& obj_id, const LLUUID &im_session_id = LLUUID::null) : - mToAgentID(to_agent), - mInventoryObjectID(obj_id), - mIMSessionID(im_session_id) - {} -}; - // accessor that looks at permissions, copyability, and names of // inventory items to determine if a drop would be ok. EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item) @@ -1557,9 +1585,7 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL if(!item || !obj) return ACCEPT_NO; // HACK: downcast LLViewerInventoryItem* vitem = (LLViewerInventoryItem*)item; - if(!vitem->isFinished()) return ACCEPT_NO; - if (vitem->getIsLinkType()) return ACCEPT_NO; // No giving away links // deny attempts to drop from an object onto itself. This is to @@ -1643,12 +1669,87 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL } +static void give_inventory_cb(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + // if Cancel pressed + if (option == 1) + { + return; + } + + LLSD payload = notification["payload"]; + const LLUUID& session_id = payload["session_id"]; + const LLUUID& agent_id = payload["agent_id"]; + LLViewerInventoryItem * inv_item = gInventory.getItem(payload["item_id"]); + LLViewerInventoryCategory * inv_cat = gInventory.getCategory(payload["item_id"]); + if (NULL == inv_item && NULL == inv_cat) + { + llassert( FALSE ); + return; + } + bool successfully_shared; + if (inv_item) + { + successfully_shared = LLGiveInventory::doGiveInventoryItem(agent_id, inv_item, session_id); + } + else + { + successfully_shared = LLGiveInventory::doGiveInventoryCategory(agent_id, inv_cat, session_id); + } + if (successfully_shared) + { + if ("avatarpicker" == payload["d&d_dest"].asString()) + { + if (LLFloaterAvatarPicker::instanceExists()) + LLFloaterAvatarPicker::getInstance()->close(); + } + LLNotificationsUtil::add("ItemsShared"); + } +} + +static void show_object_sharing_confirmation(const std::string name, + LLInventoryObject* inv_item, + const LLSD& dest, + const LLUUID& dest_agent, + const LLUUID& session_id = LLUUID::null) +{ + if (!inv_item) + { + llassert(NULL != inv_item); + return; + } + LLSD substitutions; + substitutions["RESIDENTS"] = name; + substitutions["ITEMS"] = inv_item->getName(); + LLSD payload; + payload["agent_id"] = dest_agent; + payload["item_id"] = inv_item->getUUID(); + payload["session_id"] = session_id; + payload["d&d_dest"] = dest.asString(); + LLNotificationsUtil::add("ShareItemsConfirmation", substitutions, payload, &give_inventory_cb); +} + +static void get_name_cb(const LLUUID& id, + const std::string& full_name, + LLInventoryObject* inv_obj, + const LLSD& dest, + const LLUUID& dest_agent) +{ + show_object_sharing_confirmation(full_name, + inv_obj, + dest, + id, + LLUUID::null); +} + // function used as drag-and-drop handler for simple agent give inventory requests //static bool LLToolDragAndDrop::handleGiveDragAndDrop(LLUUID dest_agent, LLUUID session_id, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, - EAcceptance* accept) + EAcceptance* accept, + const LLSD& dest) { // check the type switch(cargo_type) @@ -1753,13 +1854,13 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv( } // must not be already wearing it - // - //LLVOAvatar* my_avatar = gAgentAvatarp; - //if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) - //{ - // return ACCEPT_NO; - //} - // + /* + LLVOAvatarSelf* avatar = gAgentAvatarp; + if( !avatar || avatar->isWearingAttachment(item->getUUID()) ) + { + return ACCEPT_NO; + } + */ const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); if(gInventory.isObjectDescendentOf(item->getUUID(), outbox_id)) @@ -1827,13 +1928,14 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnLand( locateInventory(item, cat); if (!item || !item->isFinished()) return ACCEPT_NO; - // - //LLVOAvatar* my_avatar = gAgentAvatarp; - //if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) - //{ - // return ACCEPT_NO; - //} - // + /* + LLVOAvatarSelf* my_avatar = gAgentAvatarp; + if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) + { + return ACCEPT_NO; + } + */ + EAcceptance accept; BOOL remove_inventory; @@ -1902,13 +2004,13 @@ EAcceptance LLToolDragAndDrop::dad3dRezObjectOnObject( LLViewerInventoryCategory* cat; locateInventory(item, cat); if(!item || !item->isFinished()) return ACCEPT_NO; - // - //LLVOAvatar* my_avatar = gAgentAvatarp; - //if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) - //{ - // return ACCEPT_NO; - //} - // + /* + LLVOAvatarSelf* my_avatar = gAgentAvatarp; + if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) ) + { + return ACCEPT_NO; + } + */ if((mask & MASK_CONTROL)) { @@ -2011,23 +2113,20 @@ EAcceptance LLToolDragAndDrop::dad3dApplyToObject( { LL_DEBUGS() << "LLToolDragAndDrop::dad3dApplyToObject()" << LL_ENDL; - // + /* // Fuck this - // *HACK: In order to resolve SL-22177, we need to block drags // from notecards and objects onto other objects. - //if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource)) - //{ - // return ACCEPT_NO; - //} - // + if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource)) + { + return ACCEPT_NO; + } + */ LLViewerInventoryItem* item; LLViewerInventoryCategory* cat; locateInventory(item, cat); - if(!item || !item->isFinished()) return ACCEPT_NO; - EAcceptance rv = willObjectAcceptInventory(obj, item); if((mask & MASK_CONTROL)) { @@ -2644,7 +2743,8 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( item = NULL; cat = NULL; - if (mCargoIDs.empty()) + if (mCargoIDs.empty() + || (mSource == SOURCE_PEOPLE)) ///< There is no inventory item for people drag and drop. { return NULL; } @@ -2674,7 +2774,7 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( } else if(mSource == SOURCE_NOTECARD) { - LLPreviewNotecard* preview = dynamic_cast(LLPreview::find(mSourceID)); + LLPreviewNotecard* preview = static_cast(LLPreview::find(mSourceID)); if(preview) { item = (LLViewerInventoryItem*)preview->getDragItem(); @@ -2689,11 +2789,11 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryCategory::cat_array_t& cats, LLViewerInventoryItem::item_array_t& items) { - if(mCargoIDs.count() == 0) return NULL; + if(mCargoIDs.size() == 0) return NULL; if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY)) { // The object should be in user inventory. - for (S32 i = 0; i < mCargoIDs.count(); i++) + for (S32 i = 0; i < mCargoIDs.size(); i++) { LLInventoryItem* item = gInventory.getItem(mCargoIDs[i]); if (item) @@ -2717,7 +2817,7 @@ LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryC || (mCargoType == DAD_ROOT_CATEGORY)) { // The object should be in user inventory. - for (S32 i = 0; i < mCargoIDs.count(); i++) + for (S32 i = 0; i < mCargoIDs.size(); i++) { LLInventoryCategory* category = (LLInventoryCategory*)obj->getInventoryObject(mCargoIDs[i]); if (category) @@ -2728,7 +2828,7 @@ LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryC } else { - for (S32 i = 0; i < mCargoIDs.count(); i++) + for (S32 i = 0; i < mCargoIDs.size(); i++) { LLInventoryItem* item = (LLInventoryItem*)obj->getInventoryObject(mCargoIDs[i]); if (item) diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index 09de0e6dd..4d5cacb31 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -33,10 +33,10 @@ #ifndef LL_TOOLDRAGANDDROP_H #define LL_TOOLDRAGANDDROP_H +#include "lldictionary.h" #include "lltool.h" #include "llview.h" #include "lluuid.h" -#include "stdenums.h" #include "llassetstorage.h" #include "llpermissions.h" #include "llwindow.h" @@ -50,6 +50,8 @@ class LLPickInfo; class LLToolDragAndDrop : public LLTool, public LLSingleton { public: + typedef boost::signals2::signal enddrag_signal_t; + LLToolDragAndDrop(); // overridden from LLTool @@ -68,7 +70,9 @@ public: SOURCE_AGENT, SOURCE_WORLD, SOURCE_NOTECARD, - SOURCE_LIBRARY + SOURCE_LIBRARY, + SOURCE_VIEWER, + SOURCE_PEOPLE }; void beginDrag(EDragAndDropType type, @@ -86,9 +90,23 @@ public: const LLUUID& getObjectID() const { return mObjectID; } EAcceptance getLastAccept() { return mLastAccept; } + boost::signals2::connection setEndDragCallback( const enddrag_signal_t::slot_type& cb ) { return mEndDragSignal.connect(cb); } + + void setCargoCount(U32 count) { mCargoCount = count; } + void resetCargoCount() { mCargoCount = 0; } + U32 getCargoCount() const { return (mCargoCount > 0) ? mCargoCount : mCargoIDs.size(); } + S32 getCargoIndex() const { return mCurItemIndex; } + uuid_vec_t::size_type getCargoIDsCount() const { return mCargoIDs.size(); } static S32 getOperationId() { return sOperationId; } + // deal with permissions of object, etc. returns TRUE if drop can + // proceed, otherwise FALSE. + static BOOL handleDropTextureProtections(LLViewerObject* hit_obj, + LLInventoryItem* item, + LLToolDragAndDrop::ESource source, + const LLUUID& src_id); + protected: enum EDropTarget { @@ -100,6 +118,7 @@ protected: DT_COUNT = 5 }; +protected: // dragOrDrop3dImpl points to a member of LLToolDragAndDrop that // takes parameters (LLViewerObject* obj, S32 face, MASK, BOOL // drop) and returns a BOOL if drop is ok @@ -110,11 +129,14 @@ protected: EAcceptance* acceptance); void dragOrDrop3D(S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* acceptance); + static void pickCallback(const LLPickInfo& pick_info); void pick(const LLPickInfo& pick_info); protected: + U32 mCargoCount; + S32 mDragStartX; S32 mDragStartY; @@ -135,6 +157,9 @@ protected: BOOL mDrop; S32 mCurItemIndex; std::string mToolTipMsg; + std::string mCustomMsg; + + enddrag_signal_t mEndDragSignal; protected: // 3d drop functions. these call down into the static functions @@ -209,13 +234,6 @@ protected: // inventory items to determine if a drop would be ok. static EAcceptance willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item); - // deal with permissions of object, etc. returns TRUE if drop can - // proceed, otherwise FALSE. - static BOOL handleDropTextureProtections(LLViewerObject* hit_obj, - LLInventoryItem* item, - LLToolDragAndDrop::ESource source, - const LLUUID& src_id); - public: // helper functions static BOOL isInventoryDropAcceptable(LLViewerObject* obj, LLInventoryItem* item) { return (ACCEPT_YES_COPY_SINGLE <= willObjectAcceptInventory(obj, item)); } @@ -253,7 +271,8 @@ public: static bool handleGiveDragAndDrop(LLUUID agent, LLUUID session, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, - EAcceptance* accept); + EAcceptance* accept, + const LLSD& dest = LLSD()); // Classes used for determining 3d drag and drop types. private: diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index b9535d36a..eb976171d 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -144,7 +144,7 @@ bool LLURLDispatcherImpl::dispatch(const LLSLURL& slurl, bool LLURLDispatcherImpl::dispatchRightClick(const LLSLURL& slurl) { const bool right_click = true; - LLMediaCtrl* web = NULL; + LLMediaCtrl* web = nullptr; const bool trusted_browser = false; return dispatchCore(slurl, "clicked", right_click, web, trusted_browser); } @@ -187,11 +187,18 @@ bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string LLPanelLogin::setLocation(slurl); return true; } + LLSLURL _slurl = slurl; + const std::string& grid = slurl.getGrid(); + const std::string& current_grid = gHippoGridManager->getCurrentGrid()->getGridName(); + if (grid != current_grid) + { + _slurl = LLSLURL(llformat("%s:%s", grid.c_str(), slurl.getRegion().c_str()), slurl.getPosition()); + } // Request a region handle by name LLWorldMapMessage::getInstance()->sendNamedRegionRequest(slurl.getRegion(), LLURLDispatcherImpl::regionNameCallback, - slurl.getSLURLString(), + _slurl.getSLURLString(), LLUI::sConfigGroup->getBOOL("SLURLTeleportDirectly")); // don't teleport return true; } @@ -263,7 +270,8 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& //--------------------------------------------------------------------------- // Teleportation links are handled here because they are tightly coupled -// to URL parsing and sim-fragment parsing +// to SLURL parsing and sim-fragment parsing + class LLTeleportHandler : public LLCommandHandler { public: @@ -272,8 +280,9 @@ public: // cause a constant teleport loop. JC LLTeleportHandler() : LLCommandHandler("teleport", UNTRUSTED_THROTTLE) { } + bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) + LLMediaCtrl* web) override { // construct a "normal" SLURL, resolve the region to // a global position, and teleport to it @@ -287,12 +296,11 @@ public: tokens[3].asReal()); } - LLSD args; - args["LOCATION"] = tokens[0]; - // Region names may be %20 escaped. std::string region_name = LLURI::unescape(tokens[0]); + LLSD args; + args["LOCATION"] = region_name; LLSD payload; payload["region_name"] = region_name; @@ -351,7 +359,7 @@ bool LLURLDispatcher::dispatchRightClick(const std::string& slurl) } // static -bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl) +bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl, bool trusted_content) { // *NOTE: Text editors are considered sources of trusted URLs // in order to make avatar profile links in chat history work. @@ -359,9 +367,9 @@ bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl) // receiving resident will see it and must affirmatively // click on it. // *TODO: Make this trust model more refined. JC - const bool trusted_browser = true; - LLMediaCtrl* web = NULL; - return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), "clicked", web, trusted_browser); + + LLMediaCtrl* web = nullptr; + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), "clicked", web, trusted_content); } diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h index 82e1d3152..9b05260af 100644 --- a/indra/newview/llurldispatcher.h +++ b/indra/newview/llurldispatcher.h @@ -2,36 +2,29 @@ * @file llurldispatcher.h * @brief Central registry for all SL URL handlers * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2010&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$ */ #ifndef LLURLDISPATCHER_H #define LLURLDISPATCHER_H - class LLMediaCtrl; @@ -49,6 +42,8 @@ public: // secondlife://RegionName/123/45/67/ // secondlife:///app/agent/3d6181b0-6a4b-97ef-18d8-722652995cf1/show // sl://app/foo/bar + // @param nav_type + // type of navigation type (see LLQtWebKit::LLWebPage::acceptNavigationRequest) // @param web // Pointer to LLMediaCtrl sending URL, can be NULL // @param trusted_browser @@ -58,7 +53,7 @@ public: static bool dispatchRightClick(const std::string& slurl); - static bool dispatchFromTextEditor(const std::string& slurl); + static bool dispatchFromTextEditor(const std::string& slurl, bool trusted_content); }; #endif diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 90260ed6f..7e41b4bdd 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -524,88 +524,31 @@ asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool i // - gestures // - everything else. // - llassert_always(50 == LLViewerAssetType::AT_COUNT); // Multiple asset definitions are floating around so this requires some // maintenance and attention. - static const LLViewerAssetStats::EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] = - { - LLViewerAssetStats::EVACTextureTempHTTPGet, // (0) AT_TEXTURE - LLViewerAssetStats::EVACSoundUDPGet, // AT_SOUND - LLViewerAssetStats::EVACOtherGet, // AT_CALLINGCARD - LLViewerAssetStats::EVACOtherGet, // AT_LANDMARK - LLViewerAssetStats::EVACOtherGet, // AT_SCRIPT - LLViewerAssetStats::EVACWearableUDPGet, // AT_CLOTHING - LLViewerAssetStats::EVACOtherGet, // AT_OBJECT - LLViewerAssetStats::EVACOtherGet, // AT_NOTECARD - LLViewerAssetStats::EVACOtherGet, // AT_CATEGORY - LLViewerAssetStats::EVACOtherGet, // AT_ROOT_CATEGORY - LLViewerAssetStats::EVACOtherGet, // (10) AT_LSL_TEXT - LLViewerAssetStats::EVACOtherGet, // AT_LSL_BYTECODE - LLViewerAssetStats::EVACOtherGet, // AT_TEXTURE_TGA - LLViewerAssetStats::EVACWearableUDPGet, // AT_BODYPART - LLViewerAssetStats::EVACOtherGet, // AT_TRASH - LLViewerAssetStats::EVACOtherGet, // AT_SNAPSHOT_CATEGORY - LLViewerAssetStats::EVACOtherGet, // AT_LOST_AND_FOUND - LLViewerAssetStats::EVACSoundUDPGet, // AT_SOUND_WAV - LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_TGA - LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_JPEG - LLViewerAssetStats::EVACGestureUDPGet, // (20) AT_ANIMATION - LLViewerAssetStats::EVACGestureUDPGet, // AT_GESTURE - LLViewerAssetStats::EVACOtherGet, // AT_SIMSTATE - LLViewerAssetStats::EVACOtherGet, // AT_FAVORITE - LLViewerAssetStats::EVACOtherGet, // AT_LINK - LLViewerAssetStats::EVACOtherGet, // AT_LINK_FOLDER - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // (30) - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // (40) - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // AT_MESH - // (50) - }; - - if (at < 0 || at >= LLViewerAssetType::AT_COUNT) + switch (at) { + case LLAssetType::AT_TEXTURE: + return is_temp ? with_http ? LLViewerAssetStats::EVACTextureTempHTTPGet : LLViewerAssetStats::EVACTextureTempUDPGet + : with_http ? LLViewerAssetStats::EVACTextureTempHTTPGet : LLViewerAssetStats::EVACTextureNonTempUDPGet; + break; + case LLAssetType::AT_SOUND: + case LLAssetType::AT_SOUND_WAV: + return LLViewerAssetStats::EVACSoundUDPGet; + break; + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + return LLViewerAssetStats::EVACWearableUDPGet; + break; + case LLAssetType::AT_ANIMATION: + case LLAssetType::AT_GESTURE: + return LLViewerAssetStats::EVACGestureUDPGet; + break; + case LLAssetType::AT_LANDMARK: + default: return LLViewerAssetStats::EVACOtherGet; + break; } - LLViewerAssetStats::EViewerAssetCategories ret(asset_to_bin_map[at]); - if (LLViewerAssetStats::EVACTextureTempHTTPGet == ret) - { - // Indexed with [is_temp][with_http] - static const LLViewerAssetStats::EViewerAssetCategories texture_bin_map[2][2] = - { - { - LLViewerAssetStats::EVACTextureNonTempUDPGet, - LLViewerAssetStats::EVACTextureNonTempHTTPGet, - }, - { - LLViewerAssetStats::EVACTextureTempUDPGet, - LLViewerAssetStats::EVACTextureTempHTTPGet, - } - }; - - ret = texture_bin_map[is_temp][with_http]; - } - return ret; } - } // anonymous namespace diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index bfd9c083b..7b6a6dd0c 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -33,9 +33,12 @@ #include "message.h" #include "llagent.h" +#include "llviewerregion.h" + #include "lltransfersourceasset.h" #include "lltransfertargetvfile.h" #include "llviewerassetstats.h" +#include "llworld.h" ///---------------------------------------------------------------------------- /// LLViewerAssetRequest @@ -138,7 +141,6 @@ void LLViewerAssetStorage::storeAssetData( LLVFile vfile(mVFS, asset_id, asset_type, LLVFile::READ); S32 asset_size = vfile.getSize(); - LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type); req->mUpCallback = callback; req->mUserData = user_data; @@ -324,6 +326,7 @@ void LLViewerAssetStorage::storeAssetData( } } + /** * @brief Allocate and queue an asset fetch request for the viewer * @@ -360,9 +363,13 @@ void LLViewerAssetStorage::queueRequestUDP( BOOL duplicate, BOOL is_priority) { + LL_DEBUGS("ViewerAsset") << "Request asset via HTTP " << uuid << " type " << LLAssetType::lookup(atype) << LL_ENDL; + if (mUpstreamHost.isOk()) { - bool with_http = false; + const auto region = gAgent.getRegion(); + // Fallback on UDP if we have no cap or haven't received caps. This means missing some UDP-only region assets before caps received, but that's better for HTTP only. + bool with_http = !region || !region->capabilitiesReceived() || !region->getViewerAssetUrl().empty(); LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype, with_http); req->mDownCallback = callback; req->mUserData = user_data; @@ -373,28 +380,33 @@ void LLViewerAssetStorage::queueRequestUDP( // are piggy-backing and will artificially lower averages. req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); } - mPendingDownloads.push_back(req); + // This is the same as the current UDP logic - don't re-request a duplicate. if (!duplicate) { - // send request message to our upstream data provider - // Create a new asset transfer. - LLTransferSourceParamsAsset spa; - spa.setAsset(uuid, atype); - - // Set our destination file, and the completion callback. - LLTransferTargetParamsVFile tpvf; - tpvf.setAsset(uuid, atype); - tpvf.setCallback(downloadCompleteCallback, *req); - - LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid << LL_ENDL; - LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); - ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); - - bool with_http = false; bool is_temp = false; LLViewerAssetStatsFF::record_enqueue_main(atype, with_http, is_temp); + if (!with_http) // maintain this code for older grids + { + // send request message to our upstream data provider + // Create a new asset transfer. + LLTransferSourceParamsAsset spa; + spa.setAsset(uuid, atype); + + // Set our destination file, and the completion callback. + LLTransferTargetParamsVFile tpvf; + tpvf.setAsset(uuid, atype); + tpvf.setCallback(downloadCompleteCallback, *req); + + LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid << LL_ENDL; + LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); + ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); + } + else + { + LLViewerAssetStorage::assetRequestCoro(req, uuid, atype, callback, user_data); + } } } else @@ -408,3 +420,138 @@ void LLViewerAssetStorage::queueRequestUDP( } } +extern AIHTTPTimeoutPolicy HTTPGetResponder_timeout; +class LLViewerAssetResponder : public LLHTTPClient::ResponderWithCompleted +{ +public: + LLViewerAssetResponder(const LLUUID& id, LLAssetType::EType type) : LLHTTPClient::ResponderWithCompleted() + , uuid(id), atype(type) + {} +private: + LLUUID uuid; + LLAssetType::EType atype; + + void completedRaw(LLChannelDescriptors const& channels, buffer_ptr_t const& buffer) override + { + if (LLApp::isQuitting()) + { + // Bail out if result arrives after shutdown has been started. + return; + } + + LL_DEBUGS("ViewerAsset") << "request succeeded, url " << mURL << LL_ENDL; + + S32 result_code = LL_ERR_NOERR; + LLExtStat ext_status = LL_EXSTAT_NONE; + + if (!isGoodStatus(mStatus)) + { + LL_DEBUGS("ViewerAsset") << "request failed, status " << mStatus << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_NONE; + } + else + { + std::string raw; + decode_raw_body(channels, buffer, raw); + + S32 size = raw.size(); + if (size > 0) + { + // This create-then-rename flow is modeled on + // LLTransferTargetVFile, which is what was used in the UDP + // case. + LLUUID temp_id; + temp_id.generate(); + LLVFile vf(gAssetStorage->mVFS, temp_id, atype, LLVFile::WRITE); + vf.setMaxSize(size); + if (!vf.write((const U8*)raw.data(), size)) + { + // TODO asset-http: handle error + LL_WARNS("ViewerAsset") << "Failure in vf.write()" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_VFS_CORRUPT; + } + else if (!vf.rename(uuid, atype)) + { + LL_WARNS("ViewerAsset") << "rename failed" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_VFS_CORRUPT; + } + } + else + { + // TODO asset-http: handle invalid size case + LL_WARNS("ViewerAsset") << "bad size" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_NONE; + } + } + + // Clean up pending downloads and trigger callbacks + gAssetStorage->removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); + } + AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy() const override { return HTTPGetResponder_timeout; } + char const* getName() const override { return "assetRequestCoro"; } +}; + +void LLViewerAssetStorage::capsRecvForRegion(const LLUUID& uuid, LLAssetType::EType atype, const LLUUID& region_id) +{ + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(region_id); + if (!regionp) + { + LL_WARNS("ViewerAsset") << "region not found for region_id " << region_id << LL_ENDL; + } + else + { + mViewerAssetUrl = regionp->getViewerAssetUrl(); + } + + LL_WARNS_ONCE("ViewerAsset") << "capsRecv got event" << LL_ENDL; + LL_WARNS_ONCE("ViewerAsset") << "region " << gAgent.getRegion() << " mViewerAssetUrl " << mViewerAssetUrl << LL_ENDL; + if (mViewerAssetUrl.empty()) + { + LL_WARNS_ONCE("ViewerAsset") << "asset request fails: caps received but no viewer asset cap found" << LL_ENDL; + auto result_code = LL_ERR_ASSET_REQUEST_FAILED; + auto ext_status = LL_EXSTAT_NONE; + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); + return; + } + std::string url = getAssetURL(mViewerAssetUrl, uuid,atype); + LL_DEBUGS("ViewerAsset") << "request url: " << url << LL_ENDL; + + LLHTTPClient::get(url, new LLViewerAssetResponder(uuid, atype)); +} + +void LLViewerAssetStorage::assetRequestCoro( + LLViewerAssetRequest *req, + const LLUUID uuid, + LLAssetType::EType atype, + LLGetAssetCallback callback, + void *user_data) +{ + if (!gAgent.getRegion()) + { + LL_WARNS_ONCE("ViewerAsset") << "Asset request fails: no region set" << LL_ENDL; + auto result_code = LL_ERR_ASSET_REQUEST_FAILED; + auto ext_status = LL_EXSTAT_NONE; + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); + return; + } + else if (!gAgent.getRegion()->capabilitiesReceived()) + { + LL_WARNS_ONCE("ViewerAsset") << "Waiting for capabilities" << LL_ENDL; + + gAgent.getRegion()->setCapabilitiesReceivedCallback( + boost::bind(&LLViewerAssetStorage::capsRecvForRegion, this, uuid, atype, _1)); + } + else capsRecvForRegion(uuid, atype, gAgent.getRegion()->getRegionID()); +} + +std::string LLViewerAssetStorage::getAssetURL(const std::string& cap_url, const LLUUID& uuid, LLAssetType::EType atype) +{ + std::string type_name = LLAssetType::lookup(atype); + std::string url = cap_url + "/?" + type_name + "_id=" + uuid.asString(); + return url; +} + diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index c71a68527..e1e110187 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -32,6 +32,8 @@ class LLVFile; +class LLViewerAssetRequest; + class LLViewerAssetStorage : public LLAssetStorage { public: @@ -73,7 +75,6 @@ protected: BOOL duplicate, BOOL is_priority); - void queueRequestUDP(const LLUUID& uuid, LLAssetType::EType type, void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), @@ -81,6 +82,17 @@ protected: BOOL duplicate, BOOL is_priority); + void capsRecvForRegion(const LLUUID& uuid, LLAssetType::EType atype, const LLUUID& region_id); + + void assetRequestCoro(LLViewerAssetRequest *req, + const LLUUID uuid, + LLAssetType::EType atype, + void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), + void *user_data); + + std::string getAssetURL(const std::string& cap_url, const LLUUID& uuid, LLAssetType::EType atype); + + std::string mViewerAssetUrl; }; #endif diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp index 3eee60882..b98767b1b 100644 --- a/indra/newview/llviewerassettype.cpp +++ b/indra/newview/llviewerassettype.cpp @@ -88,7 +88,10 @@ LLViewerAssetDictionary::LLViewerAssetDictionary() addEntry(LLViewerAssetType::AT_MESH, new ViewerAssetEntry(DAD_MESH)); + addEntry(LLViewerAssetType::AT_UNKNOWN, new ViewerAssetEntry(DAD_NONE)); + addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE)); + addEntry(LLViewerAssetType::AT_SETTINGS, new ViewerAssetEntry(DAD_NONE)); }; EDragAndDropType LLViewerAssetType::lookupDragAndDropType(EType asset_type) diff --git a/indra/newview/llviewerfoldertype.cpp b/indra/newview/llviewerfoldertype.cpp index fbabfa540..4ce8d9693 100644 --- a/indra/newview/llviewerfoldertype.cpp +++ b/indra/newview/llviewerfoldertype.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llviewerfoldertype.h" +#include "llcontrol.h" #include "lldictionary.h" #include "llmemory.h" #include "llvisualparam.h" @@ -131,12 +132,16 @@ LLViewerFolderDictionary::LLViewerFolderDictionary() addEntry(LLFolderType::FT_OUTFIT, new ViewerFolderEntry("New Outfit", "inv_folder_outfit.tga", "inv_folder_outfit.tga", TRUE, false)); addEntry(LLFolderType::FT_MY_OUTFITS, new ViewerFolderEntry("My Outfits", "inv_folder_outfit.tga", "inv_folder_outfit.tga", TRUE, false)); addEntry(LLFolderType::FT_MESH, new ViewerFolderEntry("Meshes", "inv_folder_mesh.tga", "inv_folder_mesh.tga", FALSE, false)); - + + bool boxes_invisible = !gSavedSettings.getBOOL("InventoryOutboxMakeVisible"); addEntry(LLFolderType::FT_INBOX, new ViewerFolderEntry("Received Items", "inv_folder_inbox.tga", "inv_folder_inbox.tga", FALSE, false)); - addEntry(LLFolderType::FT_OUTBOX, new ViewerFolderEntry("Merchant Outbox", "inv_folder_outbox.tga", "inv_folder_outbox.tga", FALSE, false)); + addEntry(LLFolderType::FT_OUTBOX, new ViewerFolderEntry("Merchant Outbox", "inv_folder_outbox.tga", "inv_folder_outbox.tga", FALSE, true)); addEntry(LLFolderType::FT_BASIC_ROOT, new ViewerFolderEntry("Basic Root", "inv_folder_plain_open.tga", "inv_folder_plain_closed.tga", FALSE, false)); + addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new ViewerFolderEntry("Marketplace Listings", "inv_folder_plain_open.tga", "inv_folder_plain_closed.tga", FALSE, boxes_invisible)); + addEntry(LLFolderType::FT_MARKETPLACE_STOCK, new ViewerFolderEntry("New Stock", "Inv_StockFolderOpen", "Inv_StockFolderClosed", FALSE, false, "default")); + addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new ViewerFolderEntry("New Version", "Inv_VersionFolderOpen", "Inv_VersionFolderClosed", FALSE, false, "default")); addEntry(LLFolderType::FT_SUITCASE, new ViewerFolderEntry("My Suitcase", "inv_folder_plain_open.tga", "inv_folder_plain_closed.tga", FALSE, false)); addEntry(LLFolderType::FT_NONE, new ViewerFolderEntry("New Folder", "inv_folder_plain_open.tga", "inv_folder_plain_closed.tga", FALSE, false, "default")); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index e07ccebeb..65887fdae 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -50,8 +50,8 @@ #include "llinventorypanel.h" #include "llfloaterinventory.h" #include "llfloaterperms.h" +#include "lllandmarkactions.h" -#include "lllandmark.h" #include "llviewerassettype.h" #include "llviewerregion.h" #include "llviewerobjectlist.h" @@ -62,6 +62,7 @@ #include "llcommandhandler.h" #include "llviewermessage.h" #include "llavatarnamecache.h" +#include "llfavoritesbar.h" #include "llsdutil.h" @@ -401,28 +402,28 @@ void LLViewerInventoryItem::fetchFromServer(void) const { LL_WARNS(LOG_INV) << "Agent Region is absent" << LL_ENDL; } + } - if (!url.empty()) - { - LLSD body; - body["agent_id"] = gAgent.getID(); - body["items"][0]["owner_id"] = mPermissions.getOwner(); - body["items"][0]["item_id"] = mUUID; + if (!url.empty()) + { + LLSD body; + body["agent_id"] = gAgent.getID(); + body["items"][0]["owner_id"] = mPermissions.getOwner(); + body["items"][0]["item_id"] = mUUID; - LLHTTPClient::post(url, body, new LLInventoryModel::FetchItemHttpHandler(body)); - } - else - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("FetchInventory"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("OwnerID", mPermissions.getOwner()); - msg->addUUID("ItemID", mUUID); - gAgent.sendReliableMessage(); - } + LLHTTPClient::post(url, body, new LLInventoryModel::FetchItemHttpHandler(body)); + } + else + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("FetchInventory"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->nextBlock("InventoryData"); + msg->addUUID("OwnerID", mPermissions.getOwner()); + msg->addUUID("ItemID", mUUID); + gAgent.sendReliableMessage(); } } } @@ -809,6 +810,36 @@ bool LLViewerInventoryCategory::exportFileLocal(LLFILE* fp) const return true; } +bool LLViewerInventoryCategory::acceptItem(LLInventoryItem* inv_item) +{ + if (!inv_item) + { + return false; + } + + // Only stock folders have limitation on which item they will accept + bool accept = true; + if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + // If the item is copyable (i.e. non stock) do not accept the drop in a stock folder + if (inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + accept = false; + } + else + { + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(getUUID(),cat_array,item_array); + // Destination stock folder must be empty OR types of incoming and existing items must be identical and have the same permissions + accept = (!item_array->size() || + ((item_array->at(0)->getInventoryType() == inv_item->getInventoryType()) && + (item_array->at(0)->getPermissions().getMaskNextOwner() == inv_item->getPermissions().getMaskNextOwner()))); + } + } + return accept; +} + void LLViewerInventoryCategory::determineFolderType() { /* Do NOT uncomment this code. This is for future 2.1 support of ensembles. @@ -1090,13 +1121,6 @@ void create_notecard_cb(const LLUUID& inv_item) } } -void AddFavoriteLandmarkCallback::fire(const LLUUID& inv_item_id) -{ - if (mTargetLandmarkId.isNull()) return; - - //gInventory.rearrangeFavoriteLandmarks(inv_item_id, mTargetLandmarkId); // MULTI-WEARABLES TODO -} - LLInventoryCallbackManager gInventoryCallbacks; void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id, @@ -2136,17 +2160,21 @@ const std::string& LLViewerInventoryItem::getName() const return LLInventoryItem::getName(); } -#if 0 S32 LLViewerInventoryItem::getSortField() const { return LLFavoritesOrderStorage::instance().getSortIndex(mUUID); } +//void LLViewerInventoryItem::setSortField(S32 sortField) +//{ +// LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField); +// getSLURL(); +//} + void LLViewerInventoryItem::getSLURL() { LLFavoritesOrderStorage::instance().getSLURL(mAssetUUID); } -#endif const LLPermissions& LLViewerInventoryItem::getPermissions() const { @@ -2244,6 +2272,33 @@ U32 LLViewerInventoryItem::getCRC32() const return LLInventoryItem::getCRC32(); } +// *TODO: mantipov: should be removed with LMSortPrefix patch in llinventorymodel.cpp, EXT-3985 +static char getSeparator() { return '@'; } +BOOL LLViewerInventoryItem::extractSortFieldAndDisplayName(const std::string& name, S32* sortField, std::string* displayName) +{ + const char separator = getSeparator(); + const std::string::size_type separatorPos = name.find(separator, 0); + + BOOL result = FALSE; + + if (separatorPos < std::string::npos) + { + if (sortField) + { + *sortField = std::stoi(name.substr(0, separatorPos)); + } + + if (displayName) + { + *displayName = name.substr(separatorPos + 1, std::string::npos); + } + + result = TRUE; + } + + return result; +} + // This returns true if the item that this item points to // doesn't exist in memory (i.e. LLInventoryModel). The baseitem // might still be in the database but just not loaded yet. diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 7b9db7234..8825b0417 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -63,6 +63,9 @@ public: virtual const LLUUID& getAssetUUID() const; virtual const LLUUID& getProtectedAssetUUID() const; // returns LLUUID::null if current agent does not have permission to expose this asset's UUID to the user virtual const std::string& getName() const; + virtual S32 getSortField() const; + //virtual void setSortField(S32 sortField); + virtual void getSLURL(); //Caches SLURL for landmark. //*TODO: Find a better way to do it and remove this method from here. virtual const LLPermissions& getPermissions() const; virtual const bool getIsFullPerm() const; // 'fullperm' in the popular sense: modify-ok & copy-ok & transfer-ok, no special god rules applied virtual const LLUUID& getCreatorUUID() const; @@ -75,6 +78,8 @@ public: virtual time_t getCreationDate() const; virtual U32 getCRC32() const; // really more of a checksum. + static BOOL extractSortFieldAndDisplayName(const std::string& name, S32* sortField, std::string* displayName); + // construct a complete viewer inventory item LLViewerInventoryItem(const LLUUID& uuid, const LLUUID& parent_uuid, const LLPermissions& permissions, @@ -232,6 +237,9 @@ public: virtual void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); virtual BOOL unpackMessage(const LLSD& category); + // returns true if the category object will accept the incoming item + bool acceptItem(LLInventoryItem* inv_item); + private: friend class LLInventoryModel; void localizeName(); // intended to be called from the LLInventoryModel diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 80aaac6cb..4b552ca10 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -41,6 +41,7 @@ #include "llnotificationsutil.h" #include "llfeaturemanager.h" #include "llsecondlifeurls.h" +#include "llurlaction.h" // #include "llfloaterblacklist.h" // @@ -74,6 +75,7 @@ #include "llfloatergodtools.h" #include "llfloaterhtmlcurrency.h" #include "llfloaterland.h" +#include "llfloatermarketplacelistings.h" #include "llfloatermute.h" #include "llfloateropenobject.h" #include "llfloaterpathfindingcharacters.h" @@ -94,6 +96,7 @@ #include "llhudeffecttrail.h" #include "llhudmanager.h" #include "llinventoryfunctions.h" +#include "llmarketplacefunctions.h" #include "llmimetypes.h" #include "llmenuoptionpathfindingrebakenavmesh.h" #include "llmutelist.h" @@ -103,6 +106,7 @@ #include "llselectmgr.h" #include "llstatusbar.h" #include "lltextureview.h" +#include "lltoolbar.h" #include "lltoolcomp.h" #include "lltoolgrab.h" #include "lltoolmgr.h" @@ -127,6 +131,7 @@ #include "llfloaternotificationsconsole.h" // +#include "lltexteditor.h" // Initialize the text editor menu listeners in here #include "llfloatermessagelog.h" #include "shfloatermediaticker.h" #include "llpacketring.h" @@ -584,6 +589,33 @@ void rebuild_context_menus() gAgentAvatarp->buildContextMenus(); } +void set_merchant_SLM_menu() +{ + // DD-170 : SLM Alpha and Beta program : for the moment, we always show the SLM menu and + // tools so that all merchants can try out the UI, even if not migrated. + // *TODO : Keep SLM UI hidden for non migrated merchant in released viewer + gMenuHolder->getChild("MarketplaceListings")->setVisible(TRUE); + gToolBar->getChild("marketplace_listings_btn")->setEnabled(true); +} + +void check_merchant_status() +{ + if (!gSavedSettings.getBOOL("InventoryOutboxDisplayBoth")) + { + // Reset the SLM status: we actually want to check again, that's the point of calling check_merchant_status() + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED); + + // Hide SLM related menu item + gMenuHolder->getChild("MarketplaceListings")->setVisible(FALSE); + + // Also disable the toolbar button for Marketplace Listings + gToolBar->getChild("marketplace_listings_btn")->setEnabled(false); + + // Launch an SLM test connection to get the merchant status + LLMarketplaceData::instance().initializeSLM(boost::bind(&set_merchant_SLM_menu)); + } +} + void init_menus() { S32 top = gViewerWindow->getRootView()->getRect().getHeight(); @@ -3004,17 +3036,22 @@ class LLAvatarFreeze : public view_listener_t } }; +void do_script_count(bool del, LLViewerObject* object = nullptr) +{ + if (object || (object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject())) + { + if (ScriptCounter::getInstance(object->getID())) return; + ScriptCounter* sc = new ScriptCounter(del, object); + sc->requestInventories(); + // sc will destroy itself + } +} + class LLScriptCount : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { - if (LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()) - { - if (ScriptCounter::getInstance(object->getID())) return true; - ScriptCounter* sc = new ScriptCounter(false, object); - sc->requestInventories(); - // sc will destroy itself - } + do_script_count(false, userdata["data"].asString() == "agent" ? gAgentAvatarp : nullptr); return true; } }; @@ -3023,13 +3060,7 @@ class LLScriptDelete : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { - if (LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()) - { - if (ScriptCounter::getInstance(object->getID())) return true; - ScriptCounter* sc = new ScriptCounter(true, object); - sc->requestInventories(); - // sc will destroy itself - } + do_script_count(true); return true; } }; @@ -3038,7 +3069,7 @@ class LLObjectVisibleScriptCount : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + LLViewerObject* object = userdata["data"].asString() == "agent" ? gAgentAvatarp : LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); bool new_value = (object != NULL); gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value); @@ -3050,20 +3081,20 @@ class LLObjectEnableScriptDelete : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + auto objects = LLSelectMgr::getInstance()->getSelection(); + LLViewerObject* object = objects->getPrimaryObject(); bool new_value = (object != NULL); if(new_value) - for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) + for (LLObjectSelection::root_iterator iter = objects->root_begin(); + iter != objects->root_end(); iter++) { LLSelectNode* selectNode = *iter; LLViewerObject* object = selectNode->getObject(); - if(object) - if(!object->permModify()) - { - new_value=false; - break; - } + if (object && !object->permModify()) + { + new_value=false; + break; + } } gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value); @@ -4088,7 +4119,8 @@ void reset_view_final( BOOL proceed ) } gAgentCamera.resetView(TRUE, TRUE); gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR); - gSavedSettings.setBOOL("SinguMotionResetsCamera", true); + if (gSavedSettings.getBOOL("SinguMotionResetsCameraReset")) + gSavedSettings.setBOOL("SinguMotionResetsCamera", true); if(gAgentCamera.cameraCustomizeAvatar() && LLFloaterCustomize::instanceExists()) LLFloaterCustomize::getInstance()->close(); @@ -8894,6 +8926,15 @@ class SinguVisibleDebugConsole : public view_listener_t } }; +class SinguUrlAction : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLUrlAction::clickAction(userdata.asStringRef(), true); + return true; + } +}; + class VisibleSecondLife : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) @@ -9301,6 +9342,25 @@ class ListToggleMute : public view_listener_t } }; +struct MarketplaceViewSortAction : view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + LLFloaterMarketplaceListings::findInstance()->mPanelListings->onViewSortMenuItemClicked(userdata); + return true; + } +}; + +struct MarketplaceViewSortCheckItem : view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + gMenuHolder->findControl(userdata["control"].asString()) + ->setValue(LLFloaterMarketplaceListings::findInstance()->mPanelListings->onViewSortMenuItemCheck(userdata["data"])); + return true; + } +}; + void addMenu(view_listener_t *menu, const std::string& name) { sMenus.push_back(menu); @@ -9579,6 +9639,7 @@ void initialize_menus() addMenu(new SinguCheckPoseStand(), "CheckPoseStand"); addMenu(new SinguRebake(), "Rebake"); addMenu(new SinguVisibleDebugConsole(), "VisibleRegionDebugConsole"); + addMenu(new SinguUrlAction(), "URLAction"); addMenu(new LLSyncAnimations(), "Tools.ResyncAnimations"); // [RLVa:KB] - Checked: 2010-01-18 (RLVa-1.1.0m) | Added: RLVa-1.1.0m | OK @@ -9627,6 +9688,14 @@ void initialize_menus() add_radar_listeners(); + // Text Editor menus + LLTextEditor::setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0)); + LLTextEditor::setIsFriendCallback(LLAvatarActions::isFriend); + LLTextEditor::addMenuListeners(); + + addMenu(new MarketplaceViewSortAction, "Marketplace.ViewSort.Action"); + addMenu(new MarketplaceViewSortCheckItem, "Marketplace.ViewSort.CheckItem"); + class LLViewBuildMode : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index 72a2b80fa..b3f2cda7b 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -89,6 +89,7 @@ BOOL enable_god_full(void* user_data); BOOL enable_god_liaison(void* user_data); BOOL enable_god_basic(void* user_data); void set_underclothes_menu_options(); +void check_merchant_status(); void exchange_callingcard(const LLUUID& dest_id); diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index f99b3c7be..9b9fdd105 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -409,11 +409,17 @@ class LLFileUploadBulk : public view_listener_t // // Also fix single upload to charge first, then refund // - S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - const char* notification_type = expected_upload_cost ? "BulkTemporaryUpload" : "BulkTemporaryUploadFree"; - LLSD args; - args["UPLOADCOST"] = gHippoGridManager->getConnectedGrid()->getUploadFee(); - LLNotificationsUtil::add(notification_type, args, LLSD(), onConfirmBulkUploadTemp); + const auto grid(gHippoGridManager->getConnectedGrid()); + if (grid->isSecondLife()) // For SL, we can't do temp uploads anymore. + { + doBulkUpload(); + } + else + { + S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + const char* notification_type = expected_upload_cost ? "BulkTemporaryUpload" : "BulkTemporaryUploadFree"; + LLNotificationsUtil::add(notification_type, LLSD().with("UPLOADCOST", grid->getUploadFee()), LLSD(), onConfirmBulkUploadTemp); + } return true; } @@ -428,10 +434,15 @@ class LLFileUploadBulk : public view_listener_t else // cancel return false; + doBulkUpload(enabled); + return true; + } + + static void doBulkUpload(bool temp = false) + { AIFilePicker* filepicker = AIFilePicker::create(); filepicker->open(FFLOAD_ALL, "", "openfile", true); - filepicker->run(boost::bind(&LLFileUploadBulk::onConfirmBulkUploadTemp_continued, enabled, filepicker)); - return true; + filepicker->run(boost::bind(&LLFileUploadBulk::onConfirmBulkUploadTemp_continued, temp, filepicker)); } static void onConfirmBulkUploadTemp_continued(bool enabled, AIFilePicker* filepicker) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 629868e17..65da02e4c 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -77,6 +77,7 @@ #include "llinventorybridge.h" #include "llinventorymodel.h" #include "llinventorypanel.h" +#include "llmarketplacefunctions.h" #include "llmutelist.h" #include "llnotify.h" #include "llnotifications.h" @@ -1667,7 +1668,7 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& // send the message msg->sendReliable(mHost); - if (gSavedSettings.getBOOL("LogInventoryDecline")) + if (!mFromGroup && gSavedSettings.getBOOL("LogInventoryDecline")) { // [RLVa:KB] - Checked: 2010-09-23 (RLVa-1.2.1e) | Added: RLVa-1.2.1e if ( (rlv_handler_t::isEnabled()) && @@ -2240,6 +2241,20 @@ void autoresponder_finish(bool show_autoresponded, const LLUUID& session_id, con } } +const std::string NOT_ONLINE_MSG("User not online - message will be stored and delivered later."); +const std::string NOT_ONLINE_INVENTORY("User not online - inventory has been saved."); +void translate_if_needed(std::string& message) +{ + if (message == NOT_ONLINE_MSG) + { + message = LLTrans::getString("not_online_msg"); + } + else if (message == NOT_ONLINE_INVENTORY) + { + message = LLTrans::getString("not_online_inventory"); + } +} + void process_improved_im(LLMessageSystem *msg, void **user_data) { if (gNoRender) @@ -2308,6 +2323,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) return; // NaCl End + // Singu TODO: LLIMProcessing goes here + if (chat.mSourceType == CHAT_SOURCE_SYSTEM) + { // Translate server message if required (MAINT-6109) + translate_if_needed(message); + } + // make sure that we don't have an empty or all-whitespace name LLStringUtil::trim(name); if (name.empty()) @@ -5248,6 +5269,8 @@ void process_kill_object(LLMessageSystem *mesgsys, void **user_data) num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData); + bool different_region = mesgsys->getSender().getIPandPort() != gAgent.getRegion()->getHost().getIPandPort(); + for (i = 0; i < num_objects; i++) { mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i); @@ -5273,6 +5296,11 @@ void process_kill_object(LLMessageSystem *mesgsys, void **user_data) LLViewerObject *objectp = gObjectList.findObject(id); if (objectp) { + if (different_region && gAgentAvatarp == objectp->getAvatar()) + { + LL_WARNS() << "Region other than our own killing our attachments!!" << LL_ENDL; + continue; + } // Display green bubble on kill if ( gShowObjectUpdates ) { @@ -6739,8 +6767,8 @@ void update_region_restart(const LLSD& llsdBlock) if (restarting_floater) { restarting_floater->updateTime(seconds); - if (!restarting_floater->isMinimized()) - restarting_floater->center(); + /*if (!restarting_floater->isMinimized()) + restarting_floater->center();*/ } else { @@ -6778,7 +6806,7 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem) LL_WARNS() << "attempt_standard_notification: Attempted to read notification parameter data into LLSD but failed:" << llsdRaw << LL_ENDL; } } - + if ( (notificationID == "RegionEntryAccessBlocked") || (notificationID == "LandClaimAccessBlocked") || @@ -6830,6 +6858,25 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem) LLUI::sAudioCallback(LLUUID(gSavedSettings.getString("UISndRestart"))); return true; // Floater is enough. } + else + + // Special Marketplace update notification + if (notificationID == "SLM_UPDATE_FOLDER") + { + std::string state = llsdBlock["state"].asString(); + if (state == "deleted") + { + // Perform the deletion viewer side, no alert shown in this case + LLMarketplaceData::instance().deleteListing(llsdBlock["listing_id"].asInteger()); + return true; + } + else + { + // In general, no message will be displayed, all we want is to get the listing updated in the marketplace floater + // If getListing() fails though, the message of the alert will be shown by the caller of attempt_standard_notification() + return LLMarketplaceData::instance().getListing(llsdBlock["listing_id"].asInteger()); + } + } LLNotificationsUtil::add(notificationID, llsdBlock); return true; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 52012b09d..a050c5278 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -1776,6 +1776,11 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, if (sent_parentp && (sent_parentp != this) && !sent_parentp->isDead()) { + if (((LLViewerObject*)sent_parentp)->isAvatar()) + { + //LL_DEBUGS("Avatar") << "ATT got object update for attachment " << LL_ENDL; + } + // // We have a viewer object for the parent, and it's not dead. // Do the actual reparenting here. diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index c99724e26..0f45eea51 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -459,7 +459,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, compressed_dp.unpackU32(local_id, "LocalID"); compressed_dp.unpackU8(pcode, "PCode"); } - else + else //OUT_TERSE_IMPROVED { compressed_dp.unpackU32(local_id, "LocalID"); getUUIDFromLocal(fullid, @@ -468,7 +468,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, gMessageSystem->getSenderPort()); if (fullid.isNull()) { - // LL_WARNS() << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << ":" << gMessageSystem->getSenderPort() << LL_ENDL; + LL_DEBUGS() << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << ":" << gMessageSystem->getSenderPort() << LL_ENDL; mNumUnknownUpdates++; } } diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index c528e3f8f..069f65310 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -145,7 +145,7 @@ LLViewerParcelMgr::LLViewerParcelMgr() mCollisionParcel = new LLParcel(); // Aurora Sim - mParcelsPerEdge = S32(8192.f / PARCEL_GRID_STEP_METERS); // 8192 is the maximum region size on Aurora and solves the audio problem. + mParcelsPerEdge = S32(8192.f / PARCEL_GRID_STEP_METERS); // 8192 is the maximum region size on WhiteCore and solves the audio problem. //mParcelsPerEdge = S32( REGION_WIDTH_METERS / PARCEL_GRID_STEP_METERS ); // Aurora Sim mHighlightSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)]; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 8e1007063..028ad4a52 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -63,7 +63,6 @@ #include "llfloateravatarlist.h" #include "llfloatergodtools.h" #include "llfloaterperms.h" -#include "llfloaterreporter.h" #include "llfloaterregioninfo.h" #include "llhttpnode.h" #include "llregioninfomodel.h" @@ -417,7 +416,7 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, mColoName("unknown"), mProductSKU("unknown"), mProductName("unknown"), - mHttpUrl(""), + mViewerAssetUrl(""), mCacheLoaded(FALSE), mCacheDirty(FALSE), mReleaseNotesRequested(FALSE), @@ -904,7 +903,6 @@ void LLViewerRegion::processRegionInfo(LLMessageSystem* msg, void**) LLRegionInfoModel::instance().update(msg); LLFloaterGodTools::processRegionInfo(msg); LLFloaterRegionInfo::processRegionInfo(msg); - LLFloaterReporter::processRegionInfo(msg); } void LLViewerRegion::setCacheID(const LLUUID& id) @@ -1984,6 +1982,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("CreateInventoryCategory"); capabilityNames.append("CustomMenuAction"); capabilityNames.append("DispatchRegionInfo"); + capabilityNames.append("DirectDelivery"); capabilityNames.append("EnvironmentSettings"); capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); @@ -2048,7 +2047,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("UpdateScriptAgent"); capabilityNames.append("UpdateScriptTask"); capabilityNames.append("UploadBakedTexture"); - capabilityNames.append("ViewerAsset"); + capabilityNames.append("ViewerAsset"); capabilityNames.append("ViewerMetrics"); capabilityNames.append("ViewerStartAuction"); capabilityNames.append("ViewerStats"); @@ -2158,9 +2157,20 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u else { mImpl->mCapabilities[name] = url; - if(name == "GetTexture" || name == "ViewerAsset") + if(name == "ViewerAsset") { - mHttpUrl = url ; + /*==============================================================*/ + // The following inserted lines are a hack for testing MAINT-7081, + // which is why the indentation and formatting are left ugly. + const char* VIEWERASSET = getenv("VIEWERASSET"); + if (VIEWERASSET) + { + mImpl->mCapabilities[name] = VIEWERASSET; + mViewerAssetUrl = VIEWERASSET; + } + else + /*==============================================================*/ + mViewerAssetUrl = url; } } } @@ -2171,9 +2181,20 @@ void LLViewerRegion::setCapabilityDebug(const std::string& name, const std::stri if ( ! ( name == "EventQueueGet" || name == "UntrustedSimulatorMessage" || name == "SimulatorFeatures" ) ) { mImpl->mSecondCapabilitiesTracker[name] = url; - if(name == "GetTexture" || name == "ViewerAsset") + if(name == "ViewerAsset") { - mHttpUrl = url ; + /*==============================================================*/ + // The following inserted lines are a hack for testing MAINT-7081, + // which is why the indentation and formatting are left ugly. + const char* VIEWERASSET = getenv("VIEWERASSET"); + if (VIEWERASSET) + { + mImpl->mSecondCapabilitiesTracker[name] = VIEWERASSET; + mViewerAssetUrl = VIEWERASSET; + } + else + /*==============================================================*/ + mViewerAssetUrl = url; } } diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index d32eb1746..d02ab5a53 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -373,7 +373,7 @@ public: friend std::ostream& operator<<(std::ostream &s, const LLViewerRegion ®ion); /// implements LLCapabilityProvider virtual std::string getDescription() const; - std::string getHttpUrl() const { return mHttpUrl ;} + std::string getViewerAssetUrl() const { return mViewerAssetUrl; } LLSpatialPartition* getSpatialPartition(U32 type); @@ -479,7 +479,7 @@ private: std::string mColoName; std::string mProductSKU; std::string mProductName; - std::string mHttpUrl; + std::string mViewerAssetUrl; // Maps local ids to cache entries. // Regions can have order 10,000 objects, so assume diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index d646a2b83..01d2014d2 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -578,34 +578,20 @@ LLViewerTextEditor::LLViewerTextEditor(const std::string& name, S32 max_length, const std::string& default_text, const LLFontGL* font, - BOOL allow_embedded_items) - : LLTextEditor(name, rect, max_length, default_text, font, allow_embedded_items), + BOOL allow_embedded_items, + bool parse_html) + : LLTextEditor(name, rect, max_length, default_text, font, allow_embedded_items, parse_html), mDragItemChar(0), mDragItemSaved(FALSE), mInventoryCallback(new LLEmbeddedNotecardOpener) { mEmbeddedItemList = new LLEmbeddedItems(this); mInventoryCallback->setEditor(this); - - // *TODO: Add right click menus for SLURLs - // Build the right click menu - // make the popup menu available - - //LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_slurl.xml", this); - //if (!menu) - //{ - // menu = new LLMenuGL(LLStringUtil::null); - //} - //menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor")); - //// menu->setVisible(FALSE); - //mPopupMenuHandle = menu->getHandle(); } LLViewerTextEditor::~LLViewerTextEditor() { delete mEmbeddedItemList; - - // The inventory callback may still be in use by gInventoryCallbackManager... // so set its reference to this to null. mInventoryCallback->setEditor(NULL); @@ -672,6 +658,7 @@ BOOL LLViewerTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* s return TRUE; } +// Singu TODO: This is mostly duplicated from LLTextEditor BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -794,114 +781,28 @@ BOOL LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - if (!mDragItem) + if (!mIsSelecting && mDragItem && hasMouseCapture()) { - // leave hover segment active during drag and drop - mHoverSegment = NULL; - } - if(hasMouseCapture() ) - { - if( mIsSelecting ) + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y ); + if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) { - if (x != mLastSelectionX || y != mLastSelectionY) - { - mLastSelectionX = x; - mLastSelectionY = y; - } + LLToolDragAndDrop::getInstance()->beginDrag( + LLViewerAssetType::lookupDragAndDropType( mDragItem->getType() ), + mDragItem->getUUID(), + LLToolDragAndDrop::SOURCE_NOTECARD, + getSourceID(), mObjectID); - if( y > getTextRect().mTop ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 ); - } - else - if( y < getTextRect().mBottom ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 ); - } - - setCursorAtLocalPos( x, y, TRUE ); - mSelectionEnd = mCursorPos; - - updateScrollFromCursor(); - getWindow()->setCursor(UI_CURSOR_IBEAM); - } - else if( mDragItem ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) - { - LLToolDragAndDrop::getInstance()->beginDrag( - LLViewerAssetType::lookupDragAndDropType( mDragItem->getType() ), - mDragItem->getUUID(), - LLToolDragAndDrop::SOURCE_NOTECARD, - getSourceID(), mObjectID); - - return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); - } - getWindow()->setCursor(UI_CURSOR_HAND); + return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); } + getWindow()->setCursor(UI_CURSOR_HAND); LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL; handled = TRUE; } - if( !handled ) - { - // Pass to children - handled = LLView::childrenHandleHover(x, y, mask) != NULL; - } - - if( handled ) - { - // Delay cursor flashing - resetKeystrokeTimer(); - } - - // Opaque - if( !handled && mTakesNonScrollClicks) - { - // Check to see if we're over an HTML-style link - if( !mSegments.empty() ) - { - LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment ) - { - if(cur_segment->getStyle()->isLink()) - { - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (over link, inactive)" << LL_ENDL; - getWindow()->setCursor(UI_CURSOR_HAND); - handled = TRUE; - } - else - if(cur_segment->getStyle()->getIsEmbeddedItem()) - { - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (over embedded item, inactive)" << LL_ENDL; - getWindow()->setCursor(UI_CURSOR_HAND); - //getWindow()->setCursor(UI_CURSOR_ARROW); - handled = TRUE; - } - mHoverSegment = cur_segment; - } - } - - if( !handled ) - { - LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL; - if (!mScrollbar->getVisible() || x < getRect().getWidth() - SCROLLBAR_SIZE) - { - getWindow()->setCursor(UI_CURSOR_IBEAM); - } - else - { - getWindow()->setCursor(UI_CURSOR_ARROW); - } - handled = TRUE; - } - } - - return handled; + return handled || LLTextEditor::handleHover(x, y, mask); } @@ -945,125 +846,36 @@ BOOL LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) BOOL LLViewerTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL; - if(!handled)handled = LLTextEditor::handleRightMouseDown(x, y, mask); - - // *TODO: Add right click menus for SLURLs -// if(! handled) -// { -// const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); -// if( cur_segment ) -// { -// if(cur_segment->getStyle()->isLink()) -// { -// handled = TRUE; -// mHTML = cur_segment->getStyle()->getLinkHREF(); -// } -// } -// } -// LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); -// if(handled && menu && mParseHTML && mHTML.length() > 0) -// { -// menu->setVisible(TRUE); -// menu->arrange(); -// menu->updateParent(LLMenuGL::sMenuContainer); -// LLMenuGL::showPopup(this, menu, x, y); -// mHTML = ""; -// } -// else -// { -// if(menu && menu->getVisible()) -// { -// menu->setVisible(FALSE); -// } -// } - return handled; + return childrenHandleRightMouseDown(x, y, mask) != NULL || LLTextEditor::handleRightMouseDown(x, y, mask); } BOOL LLViewerTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; - handled = childrenHandleMiddleMouseDown(x, y, mask) != NULL; - if (!handled) - { - handled = LLTextEditor::handleMiddleMouseDown(x, y, mask); - } - return handled; + return childrenHandleMiddleMouseDown(x, y, mask) != NULL || LLTextEditor::handleMiddleMouseDown(x, y, mask); } BOOL LLViewerTextEditor::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleMiddleMouseUp(x, y, mask) != NULL; - - return handled; + return childrenHandleMiddleMouseUp(x, y, mask) != NULL || LLTextEditor::handleMiddleMouseUp(x, y, mask); } BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { - BOOL handled = FALSE; - - // let scrollbar have first dibs - handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL; - - if( !handled && mTakesNonScrollClicks) + if (mTakesNonScrollClicks && allowsEmbeddedItems()) { - if( allowsEmbeddedItems() ) + const LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->getStyle()->getIsEmbeddedItem()) { - const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment && cur_segment->getStyle()->getIsEmbeddedItem() ) + if( openEmbeddedItemAtPos(cur_segment->getStart())) { - if( openEmbeddedItemAtPos( cur_segment->getStart() ) ) - { - deselect(); - setFocus( FALSE ); - return TRUE; - } + deselect(); + setFocus(FALSE); + return TRUE; } } - - - setCursorAtLocalPos( x, y, FALSE ); - deselect(); - - const LLWString &text = getWText(); - - if( isPartOfWord( text[mCursorPos] ) ) - { - // Select word the cursor is over - while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1])) - { - mCursorPos--; - } - startSelection(); - - while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) ) - { - mCursorPos++; - } - - mSelectionEnd = mCursorPos; - } - else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) ) - { - // Select the character the cursor is over - startSelection(); - mCursorPos++; - mSelectionEnd = mCursorPos; - } - - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection here. - mIsSelecting = FALSE; - - // delay cursor flashing - resetKeystrokeTimer(); - - // take selection to 'primary' clipboard - updatePrimary(); - - handled = TRUE; } - return handled; + + return LLTextEditor::handleDoubleClick(x, y, mask); } @@ -1243,7 +1055,6 @@ void LLViewerTextEditor::setEmbeddedText(const std::string& instr) std::string LLViewerTextEditor::getEmbeddedText() { -#if 1 // New version (Version 2) mEmbeddedItemList->copyUsedCharsToIndexed(); LLWString outtextw; @@ -1259,26 +1070,6 @@ std::string LLViewerTextEditor::getEmbeddedText() } std::string outtext = wstring_to_utf8str(outtextw); return outtext; -#else - // Old version (Version 1) - mEmbeddedItemList->copyUsedCharsToIndexed(); - std::string outtext; - for (S32 i=0; i<(S32)mWText.size(); i++) - { - llwchar wch = mWText[i]; - if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR ) - { - S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch); - wch = 0x80 | index % 128; - } - else if (wch >= 0x80) - { - wch = LL_UNKNOWN_CHAR; - } - outtext.push_back((U8)wch); - } - return outtext; -#endif } std::string LLViewerTextEditor::appendTime(bool prepend_newline) @@ -1679,7 +1470,7 @@ LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlF text_editor->initFromXML(node, parent); // add text after all parameters have been set - text_editor->appendStyledText(text, FALSE, FALSE); + text_editor->appendText(text, FALSE, FALSE); return text_editor; } diff --git a/indra/newview/llviewertexteditor.h b/indra/newview/llviewertexteditor.h index 702b97ca1..a7741502f 100644 --- a/indra/newview/llviewertexteditor.h +++ b/indra/newview/llviewertexteditor.h @@ -48,7 +48,8 @@ public: S32 max_length, const std::string& default_text = std::string(), const LLFontGL* glfont = NULL, - BOOL allow_embedded_items = FALSE); + BOOL allow_embedded_items = FALSE, + bool parse_html = false); virtual ~LLViewerTextEditor(); diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 352ac8857..85eb158f7 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -135,6 +135,7 @@ public: /*virtual*/ bool isActiveFetching(); /*virtual*/ const LLUUID& getID() const { return mID; } + void setID(const LLUUID& id) { mID = id; } // Edit for local assets to cut down on reloads, be sure to remove from wherever this has been added first. void setBoostLevel(S32 level); S32 getBoostLevel() { return mBoostLevel; } diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 08e2aa19b..da84e94ec 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -384,8 +384,16 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& return NULL ; } - std::string full_path = gDirUtilp->findSkinnedFilename("textures", filename); - if (full_path.empty()) + // Singu Note: Detect if we were given a full path already, require being over a certain size, so we have more than just a path + bool full = filename.size() > +#ifdef LL_WINDOWS + 3 && filename.substr(1, 2) == ":\\"; // Drive letter comes first +#else + 1 && filename.front() == '/'; // delim is root +#endif + + std::string full_path = full ? filename : gDirUtilp->findSkinnedFilename("textures", filename); + if (full_path.empty() || (full && !gDirUtilp->fileExists(full_path))) { LL_WARNS() << "Failed to find local image file: " << filename << LL_ENDL; return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_UI); diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index 6b9c37586..16cff6ba1 100644 --- a/indra/newview/llviewerwearable.cpp +++ b/indra/newview/llviewerwearable.cpp @@ -42,6 +42,7 @@ #include "llinventoryfunctions.h" #include "lllocaltextureobject.h" #include "llpaneleditwearable.h" +#include "aixmllindengenepool.h" using namespace LLAvatarAppearanceDefines; @@ -135,8 +136,8 @@ LLWearable::EImportResult LLViewerWearable::importStream( std::istream& input_st return result; } -#if 0 -AIArchetype LLViewerWearable::getArchetype(void) const + +AIArchetype LLViewerWearable::getArchetype() const { AIArchetype archetype(this); for (visual_param_index_map_t::const_iterator iter = mVisualParamIndexMap.begin(); iter != mVisualParamIndexMap.end(); ++iter) @@ -149,7 +150,7 @@ AIArchetype LLViewerWearable::getArchetype(void) const } return archetype; } -#endif + // Avatar parameter and texture definitions can change over time. // This function returns true if parameters or textures have been added or removed // since this wearable was created. @@ -332,11 +333,8 @@ const LLUUID LLViewerWearable::getDefaultTextureImageID(ETextureIndex index) con //virtual void LLViewerWearable::writeToAvatar(LLAvatarAppearance *avatarp) { - LLVOAvatarSelf* viewer_avatar = dynamic_cast(avatarp); - - if (!avatarp || !viewer_avatar) return; - - if (!viewer_avatar->isValid()) return; + if (!avatarp || !avatarp->isSelf() || !avatarp->isValid()) return; + LLVOAvatarSelf* viewer_avatar = static_cast(avatarp); #if 0 // FIXME DRANO - kludgy way to avoid overwriting avatar state from wearables. diff --git a/indra/newview/llviewerwearable.h b/indra/newview/llviewerwearable.h index 245753865..94f70fefa 100644 --- a/indra/newview/llviewerwearable.h +++ b/indra/newview/llviewerwearable.h @@ -68,9 +68,7 @@ public: /*virtual*/ EImportResult importStream( std::istream& input_stream, LLAvatarAppearance* avatarp ); // Singu extension. -#if 0 - AIArchetype getArchetype(void) const; -#endif + class AIArchetype getArchetype() const; void setParamsToDefaults(); void setTexturesToDefaults(); diff --git a/indra/newview/llvlmanager.cpp b/indra/newview/llvlmanager.cpp index d570428bd..b4ec788f7 100644 --- a/indra/newview/llvlmanager.cpp +++ b/indra/newview/llvlmanager.cpp @@ -42,6 +42,16 @@ #include "llframetimer.h" #include "llsurface.h" +const char LAND_LAYER_CODE = 'L'; +const char WATER_LAYER_CODE = 'W'; +const char WIND_LAYER_CODE = '7'; +const char CLOUD_LAYER_CODE = '8'; + +const char WHITECORE_LAND_LAYER_CODE = 'M'; +const char WHITECORE_WATER_LAYER_CODE = 'X'; +const char WHITECORE_WIND_LAYER_CODE = '9'; +const char WHITECORE_CLOUD_LAYER_CODE = ':'; + LLVLManager gVLManager; LLVLManager::~LLVLManager() @@ -56,33 +66,22 @@ LLVLManager::~LLVLManager() void LLVLManager::addLayerData(LLVLData *vl_datap, const S32 mesg_size) { -// Aurora Sim - //if (LAND_LAYER_CODE == vl_datap->mType) - if (LAND_LAYER_CODE == vl_datap->mType || AURORA_LAND_LAYER_CODE == vl_datap->mType) -// Aurora Sim + if (LAND_LAYER_CODE == vl_datap->mType || WHITECORE_LAND_LAYER_CODE == vl_datap->mType) { mLandBits += mesg_size * 8; } -// Aurora Sim - //else if (WIND_LAYER_CODE == vl_datap->mType) - else if (WIND_LAYER_CODE == vl_datap->mType || AURORA_WIND_LAYER_CODE == vl_datap->mType) -// Aurora Sim - { - mWindBits += mesg_size * 8; - } -// Aurora Sim - //else if (CLOUD_LAYER_CODE == vl_datap->mType) - else if (CLOUD_LAYER_CODE == vl_datap->mType || AURORA_CLOUD_LAYER_CODE == vl_datap->mType) -// Aurora Sim - { - mCloudBits += mesg_size * 8; - } -// Aurora Sim - else if (WATER_LAYER_CODE == vl_datap->mType || AURORA_CLOUD_LAYER_CODE == vl_datap->mType) + else if (WATER_LAYER_CODE == vl_datap->mType || WHITECORE_CLOUD_LAYER_CODE == vl_datap->mType) { mWaterBits += mesg_size * 8; } -// Aurora Sim + else if (WIND_LAYER_CODE == vl_datap->mType || WHITECORE_WIND_LAYER_CODE == vl_datap->mType) + { + mWindBits += mesg_size * 8; + } + else if (CLOUD_LAYER_CODE == vl_datap->mType || WHITECORE_CLOUD_LAYER_CODE == vl_datap->mType) + { + mCloudBits += mesg_size * 8; + } else { LL_ERRS() << "Unknown layer type!" << (S32)vl_datap->mType << LL_ENDL; @@ -108,19 +107,16 @@ void LLVLManager::unpackData(const S32 num_packets) { datap->mRegionp->getLand().decompressDCTPatch(bit_pack, &goph, FALSE); } -// Aurora Sim - else if (AURORA_LAND_LAYER_CODE == datap->mType) + else if (WHITECORE_LAND_LAYER_CODE == datap->mType) { datap->mRegionp->getLand().decompressDCTPatch(bit_pack, &goph, TRUE); } - //else if (WIND_LAYER_CODE == datap->mType) - else if (WIND_LAYER_CODE == datap->mType || AURORA_WIND_LAYER_CODE == datap->mType) -// Aurora Sim + else if (WIND_LAYER_CODE == datap->mType || WHITECORE_WIND_LAYER_CODE == datap->mType) { datap->mRegionp->mWind.decompress(bit_pack, &goph); } - else if (CLOUD_LAYER_CODE == datap->mType || AURORA_CLOUD_LAYER_CODE == datap->mType) + else if (CLOUD_LAYER_CODE == datap->mType || WHITECORE_CLOUD_LAYER_CODE == datap->mType) { #if ENABLE_CLASSIC_CLOUDS datap->mRegionp->mCloudLayer.decompress(bit_pack, &goph); @@ -156,6 +152,11 @@ S32 LLVLManager::getCloudBits() const return mCloudBits; } +S32 LLVLManager::getWaterBits() const +{ + return mWaterBits; +} + S32 LLVLManager::getTotalBytes() const { return mLandBits + mWindBits + mCloudBits; @@ -189,6 +190,6 @@ LLVLData::LLVLData(LLViewerRegion *regionp, const S8 type, U8 *data, const S32 s LLVLData::~LLVLData() { delete [] mData; - mData = NULL; - mRegionp = NULL; + mData = nullptr; + mRegionp = nullptr; } diff --git a/indra/newview/llvlmanager.h b/indra/newview/llvlmanager.h index 153abfa53..03d26d0db 100644 --- a/indra/newview/llvlmanager.h +++ b/indra/newview/llvlmanager.h @@ -54,6 +54,7 @@ public: S32 getLandBits() const; S32 getWindBits() const; S32 getCloudBits() const; + S32 getWaterBits() const; void resetBitCounts(); @@ -64,9 +65,7 @@ protected: U32 mLandBits; U32 mWindBits; U32 mCloudBits; -// Aurora Sim U32 mWaterBits; -// Aurora Sim }; class LLVLData @@ -76,10 +75,10 @@ public: const S8 type, U8 *data, const S32 size); ~LLVLData(); - S8 mType; - U8 *mData; - S32 mSize; LLViewerRegion *mRegionp; + U8 *mData; + S8 mType; + S32 mSize; }; extern LLVLManager gVLManager; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 41464fba0..3f24e1998 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -115,8 +115,7 @@ #include "llskinningutil.h" #include "llfloaterexploreanimations.h" -//#include "aixmllindengenepool.h" -#include "aifile.h" +#include "aixmllindengenepool.h" #include "llavatarname.h" #include "../lscript/lscript_byteformat.h" @@ -4265,7 +4264,10 @@ void LLVOAvatar::idleUpdateBelowWater() F32 water_height; water_height = getRegion()->getWaterHeight(); + BOOL old_below = mBelowWater; mBelowWater = avatar_height < water_height; + if (old_below != mBelowWater) + LLFloaterAO::toggleSwim(mBelowWater); } void LLVOAvatar::slamPosition() @@ -6329,7 +6331,7 @@ void LLVOAvatar::processAnimationStateChanges() { if (AOEnabled && isSelf()) // AO is only for ME { - LLFloaterAO::startMotion(anim_it->first, 0,FALSE); // AO overrides the anim if needed + LLFloaterAO::startMotion(anim_it->first, false); // AO overrides the anim if needed } mPlayingAnimations[anim_it->first] = anim_it->second; @@ -7687,17 +7689,6 @@ void LLVOAvatar::removeChild(LLViewerObject *childp) } } -namespace -{ - boost::signals2::connection sDetachBridgeConnection; - void detach_bridge(const LLViewerObject* obj, const LLViewerObject* bridge) - { - if (obj != bridge) return; - sDetachBridgeConnection.disconnect(); - LLVOAvatarSelf::detachAttachmentIntoInventory(obj->getAttachmentItemID()); - } -} - LLViewerJointAttachment* LLVOAvatar::getTargetAttachmentPoint(LLViewerObject* viewer_object) { S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getAttachmentState()); @@ -7718,11 +7709,6 @@ LLViewerJointAttachment* LLVOAvatar::getTargetAttachmentPoint(LLViewerObject* vi << " trying to use 1 (chest)" << LL_ENDL; - if (isSelf() && attachmentID == 127 && gSavedSettings.getBOOL("SGDetachBridge")) - { - LL_INFOS() << "Bridge detected! detaching" << LL_ENDL; - sDetachBridgeConnection = gAgentAvatarp->setAttachmentCallback(boost::bind(detach_bridge, _1, viewer_object)); - } attachment = get_if_there(mAttachmentPoints, 1, (LLViewerJointAttachment*)NULL); // Arbitrary using 1 (chest) if (attachment) { @@ -8281,7 +8267,7 @@ LLViewerObject * LLVOAvatar::findAttachmentByID( const LLUUID & target_id ) cons { #if SLOW_ATTACHMENT_LIST for(attachment_map_t::const_iterator attachment_points_iter = mAttachmentPoints.begin(); - attachment_points_iter != gAgentAvatarp->mAttachmentPoints.end(); + attachment_points_iter != mAttachmentPoints.end(); ++attachment_points_iter) { LLViewerJointAttachment* attachment = attachment_points_iter->second; @@ -10047,11 +10033,9 @@ void LLVOAvatar::dumpArchetypeXML(const std::string& prefix, bool group_by_weara void LLVOAvatar::dumpArchetypeXML_cont(std::string const& fullpath, bool group_by_wearables) { -#if 0 try { - AIFile outfile(fullpath, "wb"); - AIXMLLindenGenepool linden_genepool(outfile); + AIXMLLindenGenepool linden_genepool(fullpath); if (group_by_wearables) { @@ -10123,7 +10107,6 @@ void LLVOAvatar::dumpArchetypeXML_cont(std::string const& fullpath, bool group_b { AIAlert::add_modal("AIXMLdumpArchetypeXMLError", AIArgs("[FILE]", fullpath), error); } -#endif } diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 7147fa089..dc8826349 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -164,7 +164,7 @@ LLVOAvatarSelf::LLVOAvatarSelf(const LLUUID& id, mScreenp(NULL), mLastRegionHandle(0), mRegionCrossingCount(0), - mInitialBakesLoaded(false), + //mInitialBakesLoaded(false), // Value outside legal range, so will always be a mismatch the // first time through. mLastHoverOffsetSent(LLVector3(0.0f, 0.0f, -999.0f)) diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index b9c23e3e6..98e3a2802 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -120,7 +120,7 @@ private: private: LLUUID mInitialBakeIDs[6]; - bool mInitialBakesLoaded; + //bool mInitialBakesLoaded; /******************************************************************************** diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index 577dca897..92df639a6 100644 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -38,8 +38,7 @@ #include "llpanelprofile.h" // getProfileURL (this is the original location LL put it). #include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals -// third-party JSONCPP -#include // JSONCPP +#include "llsdjson.h" /* * Workflow: @@ -78,39 +77,25 @@ public: if (mStatus != HTTP_OK) { - LL_WARNS() << "Failed to get upload config (" << mStatus << ")" << LL_ENDL; + LL_WARNS() << "Failed to get upload config (" << mStatus << ')' << LL_ENDL; LLWebProfile::reportImageUploadStatus(false); return; } - Json::Value root; - Json::Reader reader; - if (!reader.parse(body, root)) - { - LL_WARNS() << "Failed to parse upload config: " << reader.getFormattedErrorMessages() << LL_ENDL; - LLWebProfile::reportImageUploadStatus(false); - return; - } + auto root = LlsdFromJson(nlohmann::json::parse(body)); // *TODO: 404 = not supported by the grid // *TODO: increase timeout or handle HTTP_INTERNAL_ERROR_* time errors. // Convert config to LLSD. - const Json::Value data = root["data"]; + const auto data = root["data"]; const std::string upload_url = root["url"].asString(); - LLSD config; - config["acl"] = data["acl"].asString(); - config["AWSAccessKeyId"] = data["AWSAccessKeyId"].asString(); - config["Content-Type"] = data["Content-Type"].asString(); - config["key"] = data["key"].asString(); - config["policy"] = data["policy"].asString(); - config["success_action_redirect"] = data["success_action_redirect"].asString(); - config["signature"] = data["signature"].asString(); - config["add_loc"] = data.get("add_loc", "0").asString(); - config["caption"] = data.get("caption", "").asString(); + LLSD config = data; + if (!data.has("add_loc")) config["add_loc"] = "0"; + if (!data.has("caption")) config["caption"] = LLStringUtil::null; // Do the actual image upload using the configuration. - LL_DEBUGS("Snapshots") << "Got upload config, POSTing image to " << upload_url << ", config=[" << config << "]" << LL_ENDL; + LL_DEBUGS("Snapshots") << "Got upload config, POSTing image to " << upload_url << ", config=[" << config << ']' << LL_ENDL; LLWebProfile::post(mImagep, config, upload_url); } @@ -133,7 +118,7 @@ public: { if (mStatus != HTTP_OK) { - LL_WARNS() << "Failed to upload image: " << mStatus << " " << mReason << LL_ENDL; + LL_WARNS() << "Failed to upload image: " << mStatus << ' ' << mReason << LL_ENDL; LLWebProfile::reportImageUploadStatus(false); return; } @@ -143,7 +128,7 @@ public: strstrm << istr.rdbuf(); const std::string body = strstrm.str(); LL_INFOS() << "Image uploaded." << LL_ENDL; - LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << LL_ENDL; + LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << ']' << LL_ENDL; LLWebProfile::reportImageUploadStatus(true); } @@ -184,8 +169,8 @@ public: } else { - LL_WARNS() << "Unexpected POST status: " << mStatus << " " << mReason << LL_ENDL; - LL_DEBUGS("Snapshots") << "received_headers: [" << mReceivedHeaders << "]" << LL_ENDL; + LL_WARNS() << "Unexpected POST status: " << mStatus << ' ' << mReason << LL_ENDL; + LL_DEBUGS("Snapshots") << "received_headers: [" << mReceivedHeaders << ']' << LL_ENDL; LLWebProfile::reportImageUploadStatus(false); } } diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index 8f33a56ac..ceb147443 100644 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -95,8 +95,8 @@ LLSimInfo::LLSimInfo(U64 handle) void LLSimInfo::setLandForSaleImage (LLUUID image_id) { - const bool is_aurora = gHippoGridManager->getConnectedGrid()->isAurora(); - if (is_aurora && mMapImageID[SIM_LAYER_OVERLAY].isNull() && image_id.notNull() && gTextureList.findImage(image_id)) + const bool is_whitecore = gHippoGridManager->getConnectedGrid()->isWhiteCore(); + if (is_whitecore && mMapImageID[SIM_LAYER_OVERLAY].isNull() && image_id.notNull() && gTextureList.findImage(image_id)) LLAppViewer::getTextureCache()->removeFromCache(image_id); mMapImageID[SIM_LAYER_OVERLAY] = image_id; @@ -105,7 +105,7 @@ void LLSimInfo::setLandForSaleImage (LLUUID image_id) if (mMapImageID[SIM_LAYER_OVERLAY].notNull()) { mLayerImage[SIM_LAYER_OVERLAY] = LLViewerTextureManager::getFetchedTexture(mMapImageID[SIM_LAYER_OVERLAY], FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_MAP, LLViewerTexture::LOD_TEXTURE); - if (is_aurora) mLayerImage[SIM_LAYER_OVERLAY]->forceImmediateUpdate(); + if (is_whitecore) mLayerImage[SIM_LAYER_OVERLAY]->forceImmediateUpdate(); mLayerImage[SIM_LAYER_OVERLAY]->setAddressMode(LLTexUnit::TAM_CLAMP); } else diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index 119b4332c..72ed7415f 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -1791,18 +1791,24 @@ ERlvCmdRet RlvHandler::onForceRemAttach(const RlvCommand& rlvCmd) const // @remattach=force - force detach all attachments points else if ( (rlvCmdOption.isAttachmentPointGroup()) || (rlvCmdOption.isEmpty()) ) { - for (LLVOAvatar::attachment_map_t::const_iterator itAttach = gAgentAvatarp->mAttachmentPoints.begin(); - itAttach != gAgentAvatarp->mAttachmentPoints.end(); ++itAttach) + for (const auto& entryAttachPt : gAgentAvatarp->mAttachmentPoints) { - const LLViewerJointAttachment* pAttachPt = itAttach->second; - if ( (pAttachPt) && (pAttachPt->getNumObjects()) && - ((rlvCmdOption.isEmpty()) || (rlvAttachGroupFromIndex(pAttachPt->getGroup()) == rlvCmdOption.getAttachmentPointGroup())) ) + const LLViewerJointAttachment* pAttachPt = entryAttachPt.second; + if ( (pAttachPt) && (pAttachPt->getNumObjects()) && ((rlvCmdOption.isEmpty()) || (rlvAttachGroupFromIndex(pAttachPt->getGroup()) == rlvCmdOption.getAttachmentPointGroup())) ) { RlvForceWear::instance().forceDetach(pAttachPt); } } return RLV_RET_SUCCESS; } + // @remattach:=force - force detach a specific attachment + else if (rlvCmdOption.isUUID()) + { + const LLViewerObject* pAttachObj = gObjectList.findObject(rlvCmdOption.getUUID()); + if ( (pAttachObj) && (pAttachObj->isAttachment()) && (pAttachObj->permYouOwner()) ) + RlvForceWear::instance().forceDetach(pAttachObj); + return RLV_RET_SUCCESS; + } return RLV_RET_FAILED_OPTION; } @@ -1830,16 +1836,30 @@ ERlvCmdRet RlvHandler::onForceGroup(const RlvCommand& rlvCmd) const } LLUUID idGroup; bool fValid = false; - if (idGroup.set(rlvCmd.getOption())) + if ("none" == rlvCmd.getOption()) + { + idGroup.setNull(); + fValid = true; + } + else if (idGroup.set(rlvCmd.getOption())) { fValid = (idGroup.isNull()) || (gAgent.isInGroup(idGroup, true)); } else { - for (S32 idxGroup = 0, cntGroup = gAgent.mGroups.size(); (idxGroup < cntGroup) && (idGroup.isNull()); idxGroup++) - if (boost::iequals(gAgent.mGroups[idxGroup].mName, rlvCmd.getOption())) - idGroup = gAgent.mGroups[idxGroup].mID; - fValid = (idGroup.notNull()) || ("none" == rlvCmd.getOption()); + bool fExactMatch = false; + for (const auto& groupData : gAgent.mGroups) + { + // NOTE: exact matches take precedence over partial matches; in case of partial matches the last match wins + if (boost::istarts_with(groupData.mName, rlvCmd.getOption())) + { + idGroup = groupData.mID; + fExactMatch = groupData.mName.length() == rlvCmd.getOption().length(); + if (fExactMatch) + break; + } + } + fValid = idGroup.notNull(); } if (fValid) @@ -1955,27 +1975,21 @@ ERlvCmdRet RlvHandler::processReplyCommand(const RlvCommand& rlvCmd) const case RLV_BHVR_GETATTACH: // @getattach[:]= eRet = onGetAttach(rlvCmd, strReply); break; -#ifdef RLV_EXTENSION_CMD_GETXXXNAMES case RLV_BHVR_GETATTACHNAMES: // @getattachnames[:]= case RLV_BHVR_GETADDATTACHNAMES:// @getaddattachnames[:]= case RLV_BHVR_GETREMATTACHNAMES:// @getremattachnames[:]= eRet = onGetAttachNames(rlvCmd, strReply); break; -#endif // RLV_EXTENSION_CMD_GETXXXNAMES case RLV_BHVR_GETOUTFIT: // @getoutfit[:]= eRet = onGetOutfit(rlvCmd, strReply); break; -#ifdef RLV_EXTENSION_CMD_GETXXXNAMES case RLV_BHVR_GETOUTFITNAMES: // @getoutfitnames= case RLV_BHVR_GETADDOUTFITNAMES:// @getaddoutfitnames= case RLV_BHVR_GETREMOUTFITNAMES:// @getremoutfitnames= eRet = onGetOutfitNames(rlvCmd, strReply); break; -#endif // RLV_EXTENSION_CMD_GETXXXNAMES case RLV_BHVR_FINDFOLDER: // @findfolder:= -#ifdef RLV_EXTENSION_CMD_FINDFOLDERS case RLV_BHVR_FINDFOLDERS: // @findfolders:= -#endif // RLV_EXTENSION_CMD_FINDFOLDERS eRet = onFindFolder(rlvCmd, strReply); break; case RLV_BHVR_GETPATH: // @getpath[: