diff options
author | Oz Linden <oz@lindenlab.com> | 2011-01-28 11:36:46 -0500 |
---|---|---|
committer | Oz Linden <oz@lindenlab.com> | 2011-01-28 11:36:46 -0500 |
commit | 98140b3cdcd505aabe31fcd23db7a2603c8deed3 (patch) | |
tree | b94c39ef7afbc4a091b1e4f2f7b4b2de599831a8 /indra | |
parent | 106c9124741afd50c3aaa5671743d5a939f1ad48 (diff) | |
parent | 4b354802c10e6e947a0ce0f023158e22fcd5bc4b (diff) |
merge changes for storm-634
Diffstat (limited to 'indra')
501 files changed, 23096 insertions, 7141 deletions
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 8d4969a49e..6d17a6f402 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -22,7 +22,10 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(Variables) if (DARWIN) - cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) + # 2.6.4 fixes a Mac bug in get_target_property(... "SLPlugin" LOCATION): + # before that version it returns "pathname/SLPlugin", whereas the correct + # answer is "pathname/SLPlugin.app/Contents/MacOS/SLPlugin". + cmake_minimum_required(VERSION 2.6.4 FATAL_ERROR) endif (DARWIN) if (NOT CMAKE_BUILD_TYPE) @@ -40,6 +43,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) add_subdirectory(${LIBS_OPEN_PREFIX}llimage) +add_subdirectory(${LIBS_OPEN_PREFIX}llkdu) add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj) add_subdirectory(${LIBS_OPEN_PREFIX}llinventory) add_subdirectory(${LIBS_OPEN_PREFIX}llmath) @@ -50,10 +54,6 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llvfs) add_subdirectory(${LIBS_OPEN_PREFIX}llwindow) add_subdirectory(${LIBS_OPEN_PREFIX}llxml) -if (EXISTS ${LIBS_CLOSED_DIR}llkdu) - add_subdirectory(${LIBS_CLOSED_PREFIX}llkdu) -endif (EXISTS ${LIBS_CLOSED_DIR}llkdu) - add_subdirectory(${LIBS_OPEN_PREFIX}lscript) if (WINDOWS AND EXISTS ${LIBS_CLOSED_DIR}copy_win_scripts) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index a114d6e778..dbe0cf5cd0 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -4,27 +4,28 @@ include(Variables) - # Portable compilation flags. - -if (EXISTS ${CMAKE_SOURCE_DIR}/llphysics) - # The release build should only offer to send crash reports if we're - # building from a Linden internal source tree. - set(release_crash_reports 1) -else (EXISTS ${CMAKE_SOURCE_DIR}/llphysics) - set(release_crash_reports 0) -endif (EXISTS ${CMAKE_SOURCE_DIR}/llphysics) - set(CMAKE_CXX_FLAGS_DEBUG "-D_DEBUG -DLL_DEBUG=1") set(CMAKE_CXX_FLAGS_RELEASE - "-DLL_RELEASE=1 -DLL_RELEASE_FOR_DOWNLOAD=1 -D_SECURE_SCL=0 -DLL_SEND_CRASH_REPORTS=${release_crash_reports} -DNDEBUG") + "-DLL_RELEASE=1 -DLL_RELEASE_FOR_DOWNLOAD=1 -D_SECURE_SCL=0 -DNDEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO - "-DLL_RELEASE=1 -D_SECURE_SCL=0 -DLL_SEND_CRASH_REPORTS=0 -DNDEBUG -DLL_RELEASE_WITH_DEBUG_INFO=1") + "-DLL_RELEASE=1 -D_SECURE_SCL=0 -DNDEBUG -DLL_RELEASE_WITH_DEBUG_INFO=1") +# Configure crash reporting +set(RELEASE_CRASH_REPORTING OFF CACHE BOOL "Enable use of crash reporting in release builds") +set(NON_RELEASE_CRASH_REPORTING OFF CACHE BOOL "Enable use of crash reporting in developer builds") -# Don't bother with a MinSizeRel build. +if(RELEASE_CRASH_REPORTING) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DLL_SEND_CRASH_REPORTS=1") +endif() + +if(NON_RELEASE_CRASH_REPORTING) + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DLL_SEND_CRASH_REPORTS=1") + 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) @@ -38,10 +39,10 @@ if (WINDOWS) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /MDd /MP" CACHE STRING "C++ compiler debug options" FORCE) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO - "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od /Zi /MD /MP" + "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od /Zi /MD /MP /Ob2" CACHE STRING "C++ compiler release-with-debug options" FORCE) set(CMAKE_CXX_FLAGS_RELEASE - "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP" + "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP /Ob2" CACHE STRING "C++ compiler release options" FORCE) set(CMAKE_CXX_STANDARD_LIBRARIES "") diff --git a/indra/cmake/BerkeleyDB.cmake b/indra/cmake/BerkeleyDB.cmake index d98e79179d..e3ca0fd77d 100644 --- a/indra/cmake/BerkeleyDB.cmake +++ b/indra/cmake/BerkeleyDB.cmake @@ -6,6 +6,11 @@ set(DB_FIND_REQUIRED ON) if (STANDALONE) include(FindBerkeleyDB) else (STANDALONE) - set(DB_LIBRARIES db-4.2) + if (LINUX) + # Need to add dependency pthread explicitely to support ld.gold. + set(DB_LIBRARIES db-4.2 pthread) + else (LINUX) + set(DB_LIBRARIES db-4.2) + endif (LINUX) set(DB_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) endif (STANDALONE) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 6470836286..3f421b270b 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -33,6 +33,7 @@ set(cmake_SOURCE_FILES FindMySQL.cmake FindOpenJPEG.cmake FindXmlRpcEpi.cmake + FindZLIB.cmake FMOD.cmake FreeType.cmake GStreamer010Plugin.cmake diff --git a/indra/cmake/CURL.cmake b/indra/cmake/CURL.cmake index 6e5fed4d52..9aba08e573 100644 --- a/indra/cmake/CURL.cmake +++ b/indra/cmake/CURL.cmake @@ -10,10 +10,10 @@ else (STANDALONE) use_prebuilt_binary(curl) if (WINDOWS) set(CURL_LIBRARIES - debug libcurld - optimized libcurl) + debug libcurld.lib + optimized libcurl.lib) else (WINDOWS) - set(CURL_LIBRARIES curl) + set(CURL_LIBRARIES libcurl.a) endif (WINDOWS) set(CURL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) endif (STANDALONE) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index e852cf463c..176ae9787e 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -60,22 +60,6 @@ if(WINDOWS) set(release_files ${release_files} fmod.dll) endif (FMOD) - #******************************* - # LLKDU - set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu") - if(NOT EXISTS ${internal_llkdu_path}) - if (EXISTS "${debug_src_dir}/llkdu.dll") - set(debug_llkdu_src "${debug_src_dir}/llkdu.dll") - set(debug_llkdu_dst "${SHARED_LIB_STAGING_DIR_DEBUG}/llkdu.dll") - endif (EXISTS "${debug_src_dir}/llkdu.dll") - - if (EXISTS "${release_src_dir}/llkdu.dll") - set(release_llkdu_src "${release_src_dir}/llkdu.dll") - set(release_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELEASE}/llkdu.dll") - set(relwithdebinfo_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}/llkdu.dll") - endif (EXISTS "${release_src_dir}/llkdu.dll") - endif (NOT EXISTS ${internal_llkdu_path}) - #******************************* # Copy MS C runtime dlls, required for packaging. # *TODO - Adapt this to support VC9 @@ -174,21 +158,6 @@ elseif(DARWIN) # fmod is statically linked on darwin set(fmod_files "") - #******************************* - # LLKDU - set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu") - if(NOT EXISTS ${internal_llkdu_path}) - if (EXISTS "${debug_src_dir}/libllkdu.dylib") - set(debug_llkdu_src "${debug_src_dir}/libllkdu.dylib") - set(debug_llkdu_dst "${SHARED_LIB_STAGING_DIR_DEBUG}/libllkdu.dylib") - endif (EXISTS "${debug_src_dir}/libllkdu.dylib") - - if (EXISTS "${release_src_dir}/libllkdu.dylib") - set(release_llkdu_src "${release_src_dir}/libllkdu.dylib") - set(release_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELEASE}/libllkdu.dylib") - set(relwithdebinfo_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}/libllkdu.dylib") - endif (EXISTS "${release_src_dir}/libllkdu.dylib") - endif (NOT EXISTS ${internal_llkdu_path}) elseif(LINUX) # linux is weird, multiple side by side configurations aren't supported # and we don't seem to have any debug shared libs built yet anyways... @@ -242,21 +211,6 @@ elseif(LINUX) set(release_files ${release_files} "libfmod-3.75.so") endif (FMOD) - #******************************* - # LLKDU - set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu") - if(NOT EXISTS ${internal_llkdu_path}) - if (EXISTS "${debug_src_dir}/libllkdu.so") - set(debug_llkdu_src "${debug_src_dir}/libllkdu.so") - set(debug_llkdu_dst "${SHARED_LIB_STAGING_DIR_DEBUG}/libllkdu.so") - endif (EXISTS "${debug_src_dir}/libllkdu.so") - - if (EXISTS "${release_src_dir}/libllkdu.so") - set(release_llkdu_src "${release_src_dir}/libllkdu.so") - set(release_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELEASE}/libllkdu.so") - set(relwithdebinfo_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}/libllkdu.so") - endif (EXISTS "${release_src_dir}/libllkdu.so") - endif(NOT EXISTS ${internal_llkdu_path}) else(WINDOWS) message(STATUS "WARNING: unrecognized platform for staging 3rd party libs, skipping...") set(vivox_src_dir "${CMAKE_SOURCE_DIR}/newview/vivox-runtime/i686-linux") @@ -334,40 +288,29 @@ copy_if_different( ) set(third_party_targets ${third_party_targets} ${out_targets}) -#******************************* -# LLKDU -set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu") -if(NOT EXISTS ${internal_llkdu_path}) - if (EXISTS "${debug_llkdu_src}") - ADD_CUSTOM_COMMAND( - OUTPUT ${debug_llkdu_dst} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${debug_llkdu_src} ${debug_llkdu_dst} - DEPENDS ${debug_llkdu_src} - COMMENT "Copying llkdu.dll ${SHARED_LIB_STAGING_DIR_DEBUG}" - ) - set(third_party_targets ${third_party_targets} $} ${debug_llkdu_dst}) - endif (EXISTS "${debug_llkdu_src}") - - if (EXISTS "${release_llkdu_src}") - ADD_CUSTOM_COMMAND( - OUTPUT ${release_llkdu_dst} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${release_llkdu_src} ${release_llkdu_dst} - DEPENDS ${release_llkdu_src} - COMMENT "Copying llkdu.dll ${SHARED_LIB_STAGING_DIR_RELEASE}" - ) - set(third_party_targets ${third_party_targets} ${release_llkdu_dst}) - - ADD_CUSTOM_COMMAND( - OUTPUT ${relwithdebinfo_llkdu_dst} - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${release_llkdu_src} ${relwithdebinfo_llkdu_dst} - DEPENDS ${release_llkdu_src} - COMMENT "Copying llkdu.dll ${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}" - ) - set(third_party_targets ${third_party_targets} ${relwithdebinfo_llkdu_dst}) - endif (EXISTS "${release_llkdu_src}") - -endif (NOT EXISTS ${internal_llkdu_path}) - +if (FMOD_SDK_DIR) + copy_if_different( + ${FMOD_SDK_DIR} + "${CMAKE_CURRENT_BINARY_DIR}/Debug" + out_targets + ${fmod_files} + ) + set(all_targets ${all_targets} ${out_targets}) + copy_if_different( + ${FMOD_SDK_DIR} + "${CMAKE_CURRENT_BINARY_DIR}/Release" + out_targets + ${fmod_files} + ) + set(all_targets ${all_targets} ${out_targets}) + copy_if_different( + ${FMOD_SDK_DIR} + "${CMAKE_CURRENT_BINARY_DIR}/RelWithDbgInfo" + out_targets + ${fmod_files} + ) + set(all_targets ${all_targets} ${out_targets}) +endif (FMOD_SDK_DIR) if(NOT STANDALONE) add_custom_target( diff --git a/indra/cmake/FMOD.cmake b/indra/cmake/FMOD.cmake index 96434e38fa..dcf44cd642 100644 --- a/indra/cmake/FMOD.cmake +++ b/indra/cmake/FMOD.cmake @@ -7,8 +7,10 @@ if (FMOD) set(FMOD_FIND_REQUIRED ON) include(FindFMOD) else (STANDALONE) - include(Prebuilt) - use_prebuilt_binary(fmod) + if (INSTALL_PROPRIETARY) + include(Prebuilt) + use_prebuilt_binary(fmod) + endif (INSTALL_PROPRIETARY) if (WINDOWS) set(FMOD_LIBRARY fmod) diff --git a/indra/cmake/FindJsonCpp.cmake b/indra/cmake/FindJsonCpp.cmake index 9d16f2aaab..cf84b309c1 100644 --- a/indra/cmake/FindJsonCpp.cmake +++ b/indra/cmake/FindJsonCpp.cmake @@ -21,7 +21,12 @@ EXEC_PROGRAM(${CMAKE_CXX_COMPILER} OUTPUT_STRIP_TRAILING_WHITESPACE ) +# Try to find a library that was compiled with the same compiler version as we currently use. SET(JSONCPP_NAMES ${JSONCPP_NAMES} libjson_linux-gcc-${_gcc_COMPILER_VERSION}_libmt.so) +IF (STANDALONE) + # On standalone, assume that the system installed library was compiled with the used compiler. + SET(JSONCPP_NAMES ${JSONCPP_NAMES} libjson.so) +ENDIF (STANDALONE) FIND_LIBRARY(JSONCPP_LIBRARY NAMES ${JSONCPP_NAMES} PATHS /usr/lib /usr/local/lib diff --git a/indra/cmake/FindLLQtWebkit.cmake b/indra/cmake/FindLLQtWebkit.cmake new file mode 100644 index 0000000000..c747ec32a2 --- /dev/null +++ b/indra/cmake/FindLLQtWebkit.cmake @@ -0,0 +1,62 @@ +# -*- cmake -*- + +# - Find llqtwebkit +# Find the llqtwebkit includes and library +# This module defines +# LLQTWEBKIT_INCLUDE_DIR, where to find llqtwebkit.h, etc. +# LLQTWEBKIT_LIBRARY, the llqtwebkit library with full path. +# LLQTWEBKIT_FOUND, If false, do not try to use llqtwebkit. +# also defined, but not for general use are +# LLQTWEBKIT_LIBRARIES, the libraries needed to use llqtwebkit. +# LLQTWEBKIT_LIBRARY_DIRS, where to find the llqtwebkit library. +# LLQTWEBKIT_DEFINITIONS - You should add_definitions(${LLQTWEBKIT_DEFINITIONS}) +# before compiling code that includes llqtwebkit library files. + +# Try to use pkg-config first. +# This allows to have two different libllqtwebkit packages installed: +# one for viewer 2.x and one for viewer 1.x. +include(FindPkgConfig) +if (PKG_CONFIG_FOUND) + if (LLQtWebkit_FIND_REQUIRED AND LLQtWebkit_FIND_VERSION) + set(_PACKAGE_ARGS libllqtwebkit>=${LLQtWebkit_FIND_VERSION} REQUIRED) + else (LLQtWebkit_FIND_REQUIRED AND LLQtWebkit_FIND_VERSION) + set(_PACKAGE_ARGS libllqtwebkit) + endif (LLQtWebkit_FIND_REQUIRED AND LLQtWebkit_FIND_VERSION) + if (NOT "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_LESS "2.8") + # As virtually nobody will have a pkg-config file for this, do this check always quiet. + # Unfortunately cmake 2.8 or higher is required for pkg_check_modules to have a 'QUIET'. + set(_PACKAGE_ARGS ${_PACKAGE_ARGS} QUIET) + endif () + pkg_check_modules(LLQTWEBKIT ${_PACKAGE_ARGS}) +endif (PKG_CONFIG_FOUND) +set(LLQTWEBKIT_DEFINITIONS ${LLQTWEBKIT_CFLAGS_OTHER}) + +find_path(LLQTWEBKIT_INCLUDE_DIR llqtwebkit.h NO_SYSTEM_ENVIRONMENT_PATH HINTS ${LLQTWEBKIT_INCLUDE_DIRS}) + +find_library(LLQTWEBKIT_LIBRARY NAMES llqtwebkit NO_SYSTEM_ENVIRONMENT_PATH HINTS ${LLQTWEBKIT_LIBRARY_DIRS}) + +if (NOT PKG_CONFIG_FOUND OR NOT LLQTWEBKIT_FOUND) # If pkg-config couldn't find it, pretend we don't have pkg-config. + set(LLQTWEBKIT_LIBRARIES llqtwebkit) + get_filename_component(LLQTWEBKIT_LIBRARY_DIRS ${LLQTWEBKIT_LIBRARY} PATH) +endif (NOT PKG_CONFIG_FOUND OR NOT LLQTWEBKIT_FOUND) + +# Handle the QUIETLY and REQUIRED arguments and set LLQTWEBKIT_FOUND +# to TRUE if all listed variables are TRUE. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + LLQTWEBKIT + DEFAULT_MSG + LLQTWEBKIT_LIBRARY + LLQTWEBKIT_INCLUDE_DIR + LLQTWEBKIT_LIBRARIES + LLQTWEBKIT_LIBRARY_DIRS + ) + +mark_as_advanced( + LLQTWEBKIT_LIBRARY + LLQTWEBKIT_INCLUDE_DIR + LLQTWEBKIT_LIBRARIES + LLQTWEBKIT_LIBRARY_DIRS + LLQTWEBKIT_DEFINITIONS + ) + diff --git a/indra/cmake/FindNDOF.cmake b/indra/cmake/FindNDOF.cmake new file mode 100644 index 0000000000..6dcf590a53 --- /dev/null +++ b/indra/cmake/FindNDOF.cmake @@ -0,0 +1,39 @@ +# -*- cmake -*- + +# - Find NDOF +# Find the NDOF includes and library +# This module defines +# NDOF_INCLUDE_DIR, where to find ndofdev_external.h, etc. +# NDOF_LIBRARY, the library needed to use NDOF. +# NDOF_FOUND, If false, do not try to use NDOF. + +find_path(NDOF_INCLUDE_DIR ndofdev_external.h + PATH_SUFFIXES ndofdev + ) + +set(NDOF_NAMES ${NDOF_NAMES} ndofdev libndofdev) +find_library(NDOF_LIBRARY + NAMES ${NDOF_NAMES} + ) + +if (NDOF_LIBRARY AND NDOF_INCLUDE_DIR) + set(NDOF_FOUND "YES") +else (NDOF_LIBRARY AND NDOF_INCLUDE_DIR) + set(NDOF_FOUND "NO") +endif (NDOF_LIBRARY AND NDOF_INCLUDE_DIR) + + +if (NDOF_FOUND) + if (NOT NDOF_FIND_QUIETLY) + message(STATUS "Found NDOF: Library in '${NDOF_LIBRARY}' and header in '${NDOF_INCLUDE_DIR}' ") + endif (NOT NDOF_FIND_QUIETLY) +else (NDOF_FOUND) + if (NDOF_FIND_REQUIRED) + message(FATAL_ERROR " * * *\nCould not find NDOF library!\nIf you don't need Space Navigator Joystick support you can skip this test by configuring with -DNDOF:BOOL=OFF\n * * *") + endif (NDOF_FIND_REQUIRED) +endif (NDOF_FOUND) + +mark_as_advanced( + NDOF_LIBRARY + NDOF_INCLUDE_DIR + ) diff --git a/indra/cmake/FindTut.cmake b/indra/cmake/FindTut.cmake index b5d58f6396..c2a9f43053 100644 --- a/indra/cmake/FindTut.cmake +++ b/indra/cmake/FindTut.cmake @@ -3,12 +3,11 @@ # - Find Tut # Find the Tut unit test framework includes and library # This module defines -# TUT_INCLUDE_DIR, where to find tut.h, etc. +# TUT_INCLUDE_DIR, where to find tut/tut.hpp. # TUT_FOUND, If false, do not try to use Tut. -find_path(TUT_INCLUDE_DIR tut.h - /usr/local/include/ - /usr/include +find_path(TUT_INCLUDE_DIR tut/tut.hpp + NO_SYSTEM_ENVIRONMENT_PATH ) if (TUT_INCLUDE_DIR) diff --git a/indra/cmake/FindZLIB.cmake b/indra/cmake/FindZLIB.cmake new file mode 100644 index 0000000000..6d630f1ba9 --- /dev/null +++ b/indra/cmake/FindZLIB.cmake @@ -0,0 +1,46 @@ +# -*- cmake -*- + +# - Find zlib +# Find the ZLIB includes and library +# This module defines +# ZLIB_INCLUDE_DIRS, where to find zlib.h, etc. +# ZLIB_LIBRARIES, the libraries needed to use zlib. +# ZLIB_FOUND, If false, do not try to use zlib. +# +# This FindZLIB is about 43 times as fast the one provided with cmake (2.8.x), +# because it doesn't look up the version of zlib, 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(ZLIB_INCLUDE_DIR zlib.h + NO_SYSTEM_ENVIRONMENT_PATH + ) + +FIND_LIBRARY(ZLIB_LIBRARY z) + +if (ZLIB_LIBRARY AND ZLIB_INCLUDE_DIR) + SET(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR}) + SET(ZLIB_LIBRARIES ${ZLIB_LIBRARY}) + SET(ZLIB_FOUND "YES") +else (ZLIB_LIBRARY AND ZLIB_INCLUDE_DIR) + SET(ZLIB_FOUND "NO") +endif (ZLIB_LIBRARY AND ZLIB_INCLUDE_DIR) + +if (ZLIB_FOUND) + if (NOT ZLIB_FIND_QUIETLY) + message(STATUS "Found ZLIB: ${ZLIB_LIBRARIES}") + SET(ZLIB_FIND_QUIETLY TRUE) + endif (NOT ZLIB_FIND_QUIETLY) +else (ZLIB_FOUND) + if (ZLIB_FIND_REQUIRED) + message(FATAL_ERROR "Could not find ZLIB library") + endif (ZLIB_FIND_REQUIRED) +endif (ZLIB_FOUND) + +mark_as_advanced( + ZLIB_LIBRARY + ZLIB_INCLUDE_DIR + ) + diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 79c3bb7da2..62b764bb30 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -1,265 +1,276 @@ -# -*- cmake -*- -include(LLTestCommand) -include(GoogleMock) - -MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) - # Given a project name and a list of sourcefiles (with optional properties on each), - # add targets to build and run the tests specified. - # ASSUMPTIONS: - # * this macro is being executed in the project file that is passed in - # * current working SOURCE dir is that project dir - # * there is a subfolder tests/ with test code corresponding to the filenames passed in - # * properties for each sourcefile passed in indicate what libs to link that file with (MAKE NO ASSUMPTIONS ASIDE FROM TUT) - # - # More info and examples at: https://wiki.secondlife.com/wiki/How_to_add_unit_tests_to_indra_code - # - # WARNING: do NOT modify this code without working with poppy - - # there is another branch that will conflict heavily with any changes here. -INCLUDE(GoogleMock) - - - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} sources: ${sources}") - ENDIF(LL_TEST_VERBOSE) - - # Start with the header and project-wide setup before making targets - #project(UNITTEST_PROJECT_${project}) - # Setup includes, paths, etc - SET(alltest_SOURCE_FILES - ${CMAKE_SOURCE_DIR}/test/test.cpp - ${CMAKE_SOURCE_DIR}/test/lltut.cpp - ) - SET(alltest_DEP_TARGETS - # needed by the test harness itself - ${APRUTIL_LIBRARIES} - ${APR_LIBRARIES} - llcommon - ) - IF(NOT "${project}" STREQUAL "llmath") - # add llmath as a dep unless the tested module *is* llmath! - LIST(APPEND alltest_DEP_TARGETS - llmath - ) - ENDIF(NOT "${project}" STREQUAL "llmath") - SET(alltest_INCLUDE_DIRS - ${LLMATH_INCLUDE_DIRS} - ${LLCOMMON_INCLUDE_DIRS} - ${LIBS_OPEN_DIR}/test - ${GOOGLEMOCK_INCLUDE_DIRS} - ) - SET(alltest_LIBRARIES - ${GOOGLEMOCK_LIBRARIES} - ${PTHREAD_LIBRARY} - ${WINDOWS_LIBRARIES} - ) - # Headers, for convenience in targets. - SET(alltest_HEADER_FILES - ${CMAKE_SOURCE_DIR}/test/test.h - ) - - # Use the default flags - if (LINUX) - SET(CMAKE_EXE_LINKER_FLAGS "") - endif (LINUX) - - # start the source test executable definitions - SET(${project}_TEST_OUTPUT "") - FOREACH (source ${sources}) - STRING( REGEX REPLACE "(.*)\\.[^.]+$" "\\1" name ${source} ) - STRING( REGEX REPLACE ".*\\.([^.]+)$" "\\1" extension ${source} ) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} individual source: ${source} (${name}.${extension})") - ENDIF(LL_TEST_VERBOSE) - - # - # Per-codefile additional / external source, header, and include dir property extraction - # - # Source - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_SOURCE_FILES ${source} LL_TEST_ADDITIONAL_SOURCE_FILES) - IF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND) - SET(${name}_test_additional_SOURCE_FILES "") - ENDIF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND) - SET(${name}_test_SOURCE_FILES ${source} tests/${name}_test.${extension} ${alltest_SOURCE_FILES} ${${name}_test_additional_SOURCE_FILES} ) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_SOURCE_FILES ${${name}_test_SOURCE_FILES}") - ENDIF(LL_TEST_VERBOSE) - # Headers - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_HEADER_FILES ${source} LL_TEST_ADDITIONAL_HEADER_FILES) - IF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND) - SET(${name}_test_additional_HEADER_FILES "") - ENDIF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND) - SET(${name}_test_HEADER_FILES ${name}.h ${${name}_test_additional_HEADER_FILES}) - set_source_files_properties(${${name}_test_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) - LIST(APPEND ${name}_test_SOURCE_FILES ${${name}_test_HEADER_FILES}) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_HEADER_FILES ${${name}_test_HEADER_FILES}") - ENDIF(LL_TEST_VERBOSE) - # Include dirs - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_INCLUDE_DIRS ${source} LL_TEST_ADDITIONAL_INCLUDE_DIRS) - IF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND) - SET(${name}_test_additional_INCLUDE_DIRS "") - ENDIF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND) - INCLUDE_DIRECTORIES(${alltest_INCLUDE_DIRS} ${name}_test_additional_INCLUDE_DIRS ) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_INCLUDE_DIRS ${${name}_test_additional_INCLUDE_DIRS}") - ENDIF(LL_TEST_VERBOSE) - - - # Setup target - ADD_EXECUTABLE(PROJECT_${project}_TEST_${name} ${${name}_test_SOURCE_FILES}) - SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") - - # - # Per-codefile additional / external project dep and lib dep property extraction - # - # WARNING: it's REALLY IMPORTANT to not mix these. I guarantee it will not work in the future. + poppy 2009-04-19 - # Projects - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_PROJECTS ${source} LL_TEST_ADDITIONAL_PROJECTS) - IF(${name}_test_additional_PROJECTS MATCHES NOTFOUND) - SET(${name}_test_additional_PROJECTS "") - ENDIF(${name}_test_additional_PROJECTS MATCHES NOTFOUND) - # Libraries - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_LIBRARIES ${source} LL_TEST_ADDITIONAL_LIBRARIES) - IF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND) - SET(${name}_test_additional_LIBRARIES "") - ENDIF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_PROJECTS ${${name}_test_additional_PROJECTS}") - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_LIBRARIES ${${name}_test_additional_LIBRARIES}") - ENDIF(LL_TEST_VERBOSE) - # Add to project - TARGET_LINK_LIBRARIES(PROJECT_${project}_TEST_${name} ${alltest_LIBRARIES} ${alltest_DEP_TARGETS} ${${name}_test_additional_PROJECTS} ${${name}_test_additional_LIBRARIES} ) - - # - # Setup test targets - # - GET_TARGET_PROPERTY(TEST_EXE PROJECT_${project}_TEST_${name} LOCATION) - SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PROJECT_${project}_TEST_${name}_ok.txt) - SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR}) - - # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19 - IF(LL_TEST_VERBOSE) - MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_cmd = ${TEST_CMD}") - ENDIF(LL_TEST_VERBOSE) - - SET_TEST_PATH(LD_LIBRARY_PATH) - LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${TEST_CMD}) - IF(LL_TEST_VERBOSE) - MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script = ${TEST_SCRIPT_CMD}") - ENDIF(LL_TEST_VERBOSE) - # Add test - ADD_CUSTOM_COMMAND( - OUTPUT ${TEST_OUTPUT} - COMMAND ${TEST_SCRIPT_CMD} - DEPENDS PROJECT_${project}_TEST_${name} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - # Why not add custom target and add POST_BUILD command? - # Slightly less uncertain behavior - # (OUTPUT commands run non-deterministically AFAIK) + poppy 2009-04-19 - # > I did not use a post build step as I could not make it notify of a - # > failure after the first time you build and fail a test. - daveh 2009-04-20 - LIST(APPEND ${project}_TEST_OUTPUT ${TEST_OUTPUT}) - ENDFOREACH (source) - - # Add the test runner target per-project - # (replaces old _test_ok targets all over the place) - ADD_CUSTOM_TARGET(${project}_tests ALL DEPENDS ${${project}_TEST_OUTPUT}) - ADD_DEPENDENCIES(${project} ${project}_tests) -ENDMACRO(LL_ADD_PROJECT_UNIT_TESTS) - -FUNCTION(LL_ADD_INTEGRATION_TEST - testname - additional_source_files - library_dependencies -# variable args - ) - if(TEST_DEBUG) - message(STATUS "Adding INTEGRATION_TEST_${testname} - debug output is on") - endif(TEST_DEBUG) - - SET(source_files - tests/${testname}_test.cpp - ${CMAKE_SOURCE_DIR}/test/test.cpp - ${CMAKE_SOURCE_DIR}/test/lltut.cpp - ${additional_source_files} - ) - - SET(libraries - ${library_dependencies} - ${GOOGLEMOCK_LIBRARIES} - ${PTHREAD_LIBRARY} - ) - - # Add test executable build target - if(TEST_DEBUG) - message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})") - endif(TEST_DEBUG) - ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files}) - SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") - - # Add link deps to the executable - if(TEST_DEBUG) - message(STATUS "TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})") - endif(TEST_DEBUG) - TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries}) - - # Create the test running command - SET(test_command ${ARGN}) - GET_TARGET_PROPERTY(TEST_EXE INTEGRATION_TEST_${testname} LOCATION) - LIST(FIND test_command "{}" test_exe_pos) - IF(test_exe_pos LESS 0) - # The {} marker means "the full pathname of the test executable." - # test_exe_pos -1 means we didn't find it -- so append the test executable - # name to $ARGN, the variable part of the arg list. This is convenient - # shorthand for both straightforward execution of the test program (empty - # $ARGN) and for running a "wrapper" program of some kind accepting the - # pathname of the test program as the last of its args. You need specify - # {} only if the test program's pathname isn't the last argument in the - # desired command line. - LIST(APPEND test_command "${TEST_EXE}") - ELSE (test_exe_pos LESS 0) - # Found {} marker at test_exe_pos. Remove the {}... - LIST(REMOVE_AT test_command test_exe_pos) - # ...and replace it with the actual name of the test executable. - LIST(INSERT test_command test_exe_pos "${TEST_EXE}") - ENDIF (test_exe_pos LESS 0) - - SET_TEST_PATH(LD_LIBRARY_PATH) - LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${test_command}) - - if(TEST_DEBUG) - message(STATUS "TEST_SCRIPT_CMD: ${TEST_SCRIPT_CMD}") - endif(TEST_DEBUG) - - ADD_CUSTOM_COMMAND( - TARGET INTEGRATION_TEST_${testname} - POST_BUILD - COMMAND ${TEST_SCRIPT_CMD} - ) - - # Use CTEST? Not sure how to yet... - # ADD_TEST(INTEGRATION_TEST_RUNNER_${testname} ${TEST_SCRIPT_CMD}) - -ENDFUNCTION(LL_ADD_INTEGRATION_TEST) - -MACRO(SET_TEST_PATH LISTVAR) - IF(WINDOWS) - # We typically build/package only Release variants of third-party - # libraries, so append the Release staging dir in case the library being - # sought doesn't have a debug variant. - set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR} ${SHARED_LIB_STAGING_DIR}/Release) - ELSEIF(DARWIN) - # We typically build/package only Release variants of third-party - # libraries, so append the Release staging dir in case the library being - # sought doesn't have a debug variant. - set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/Resources ${SHARED_LIB_STAGING_DIR}/Release/Resources /usr/lib) - ELSE(WINDOWS) - # Linux uses a single staging directory anyway. - IF (STANDALONE) - set(${LISTVAR} ${CMAKE_BINARY_DIR}/llcommon /usr/lib /usr/local/lib) - ELSE (STANDALONE) - set(${LISTVAR} ${SHARED_LIB_STAGING_DIR} /usr/lib) - ENDIF (STANDALONE) - ENDIF(WINDOWS) -ENDMACRO(SET_TEST_PATH) +# -*- cmake -*-
+include(LLTestCommand)
+include(GoogleMock)
+
+MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources)
+ # Given a project name and a list of sourcefiles (with optional properties on each),
+ # add targets to build and run the tests specified.
+ # ASSUMPTIONS:
+ # * this macro is being executed in the project file that is passed in
+ # * current working SOURCE dir is that project dir
+ # * there is a subfolder tests/ with test code corresponding to the filenames passed in
+ # * properties for each sourcefile passed in indicate what libs to link that file with (MAKE NO ASSUMPTIONS ASIDE FROM TUT)
+ #
+ # More info and examples at: https://wiki.secondlife.com/wiki/How_to_add_unit_tests_to_indra_code
+ #
+ # WARNING: do NOT modify this code without working with poppy -
+ # there is another branch that will conflict heavily with any changes here.
+INCLUDE(GoogleMock)
+
+
+ IF(LL_TEST_VERBOSE)
+ MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} sources: ${sources}")
+ ENDIF(LL_TEST_VERBOSE)
+
+ # Start with the header and project-wide setup before making targets
+ #project(UNITTEST_PROJECT_${project})
+ # Setup includes, paths, etc
+ SET(alltest_SOURCE_FILES
+ ${CMAKE_SOURCE_DIR}/test/test.cpp
+ ${CMAKE_SOURCE_DIR}/test/lltut.cpp
+ )
+ SET(alltest_DEP_TARGETS
+ # needed by the test harness itself
+ ${APRUTIL_LIBRARIES}
+ ${APR_LIBRARIES}
+ llcommon
+ )
+ IF(NOT "${project}" STREQUAL "llmath")
+ # add llmath as a dep unless the tested module *is* llmath!
+ LIST(APPEND alltest_DEP_TARGETS
+ llmath
+ )
+ ENDIF(NOT "${project}" STREQUAL "llmath")
+ SET(alltest_INCLUDE_DIRS
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LIBS_OPEN_DIR}/test
+ ${GOOGLEMOCK_INCLUDE_DIRS}
+ )
+ SET(alltest_LIBRARIES
+ ${GOOGLEMOCK_LIBRARIES}
+ ${PTHREAD_LIBRARY}
+ ${WINDOWS_LIBRARIES}
+ )
+ # Headers, for convenience in targets.
+ SET(alltest_HEADER_FILES
+ ${CMAKE_SOURCE_DIR}/test/test.h
+ )
+
+ # Use the default flags
+ if (LINUX)
+ SET(CMAKE_EXE_LINKER_FLAGS "")
+ endif (LINUX)
+
+ # start the source test executable definitions
+ SET(${project}_TEST_OUTPUT "")
+ FOREACH (source ${sources})
+ STRING( REGEX REPLACE "(.*)\\.[^.]+$" "\\1" name ${source} )
+ STRING( REGEX REPLACE ".*\\.([^.]+)$" "\\1" extension ${source} )
+ IF(LL_TEST_VERBOSE)
+ MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} individual source: ${source} (${name}.${extension})")
+ ENDIF(LL_TEST_VERBOSE)
+
+ #
+ # Per-codefile additional / external source, header, and include dir property extraction
+ #
+ # Source
+ GET_SOURCE_FILE_PROPERTY(${name}_test_additional_SOURCE_FILES ${source} LL_TEST_ADDITIONAL_SOURCE_FILES)
+ IF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND)
+ SET(${name}_test_additional_SOURCE_FILES "")
+ ENDIF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND)
+ SET(${name}_test_SOURCE_FILES ${source} tests/${name}_test.${extension} ${alltest_SOURCE_FILES} ${${name}_test_additional_SOURCE_FILES} )
+ IF(LL_TEST_VERBOSE)
+ MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_SOURCE_FILES ${${name}_test_SOURCE_FILES}")
+ ENDIF(LL_TEST_VERBOSE)
+ # Headers
+ GET_SOURCE_FILE_PROPERTY(${name}_test_additional_HEADER_FILES ${source} LL_TEST_ADDITIONAL_HEADER_FILES)
+ IF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND)
+ SET(${name}_test_additional_HEADER_FILES "")
+ ENDIF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND)
+ SET(${name}_test_HEADER_FILES ${name}.h ${${name}_test_additional_HEADER_FILES})
+ set_source_files_properties(${${name}_test_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)
+ LIST(APPEND ${name}_test_SOURCE_FILES ${${name}_test_HEADER_FILES})
+ IF(LL_TEST_VERBOSE)
+ MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_HEADER_FILES ${${name}_test_HEADER_FILES}")
+ ENDIF(LL_TEST_VERBOSE)
+ # Include dirs
+ GET_SOURCE_FILE_PROPERTY(${name}_test_additional_INCLUDE_DIRS ${source} LL_TEST_ADDITIONAL_INCLUDE_DIRS)
+ IF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND)
+ SET(${name}_test_additional_INCLUDE_DIRS "")
+ ENDIF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND)
+ INCLUDE_DIRECTORIES(${alltest_INCLUDE_DIRS} ${name}_test_additional_INCLUDE_DIRS )
+ IF(LL_TEST_VERBOSE)
+ MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_INCLUDE_DIRS ${${name}_test_additional_INCLUDE_DIRS}")
+ ENDIF(LL_TEST_VERBOSE)
+
+
+ # Setup target
+ ADD_EXECUTABLE(PROJECT_${project}_TEST_${name} ${${name}_test_SOURCE_FILES})
+ SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}")
+
+ #
+ # Per-codefile additional / external project dep and lib dep property extraction
+ #
+ # WARNING: it's REALLY IMPORTANT to not mix these. I guarantee it will not work in the future. + poppy 2009-04-19
+ # Projects
+ GET_SOURCE_FILE_PROPERTY(${name}_test_additional_PROJECTS ${source} LL_TEST_ADDITIONAL_PROJECTS)
+ IF(${name}_test_additional_PROJECTS MATCHES NOTFOUND)
+ SET(${name}_test_additional_PROJECTS "")
+ ENDIF(${name}_test_additional_PROJECTS MATCHES NOTFOUND)
+ # Libraries
+ GET_SOURCE_FILE_PROPERTY(${name}_test_additional_LIBRARIES ${source} LL_TEST_ADDITIONAL_LIBRARIES)
+ IF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND)
+ SET(${name}_test_additional_LIBRARIES "")
+ ENDIF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND)
+ IF(LL_TEST_VERBOSE)
+ MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_PROJECTS ${${name}_test_additional_PROJECTS}")
+ MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_LIBRARIES ${${name}_test_additional_LIBRARIES}")
+ ENDIF(LL_TEST_VERBOSE)
+ # Add to project
+ TARGET_LINK_LIBRARIES(PROJECT_${project}_TEST_${name} ${alltest_LIBRARIES} ${alltest_DEP_TARGETS} ${${name}_test_additional_PROJECTS} ${${name}_test_additional_LIBRARIES} )
+ # Compile-time Definitions
+ GET_SOURCE_FILE_PROPERTY(${name}_test_additional_CFLAGS ${source} LL_TEST_ADDITIONAL_CFLAGS)
+ IF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND)
+ SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES COMPILE_FLAGS ${${name}_test_additional_CFLAGS} )
+ IF(LL_TEST_VERBOSE)
+ MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_CFLAGS ${${name}_test_additional_CFLAGS}")
+ ENDIF(LL_TEST_VERBOSE)
+ ENDIF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND)
+
+ #
+ # Setup test targets
+ #
+ GET_TARGET_PROPERTY(TEST_EXE PROJECT_${project}_TEST_${name} LOCATION)
+ SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PROJECT_${project}_TEST_${name}_ok.txt)
+ SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR})
+
+ # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19
+ IF(LL_TEST_VERBOSE)
+ MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_cmd = ${TEST_CMD}")
+ ENDIF(LL_TEST_VERBOSE)
+
+ SET_TEST_PATH(LD_LIBRARY_PATH)
+ LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${TEST_CMD})
+ IF(LL_TEST_VERBOSE)
+ MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script = ${TEST_SCRIPT_CMD}")
+ ENDIF(LL_TEST_VERBOSE)
+ # Add test
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${TEST_OUTPUT}
+ COMMAND ${TEST_SCRIPT_CMD}
+ DEPENDS PROJECT_${project}_TEST_${name}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ )
+ # Why not add custom target and add POST_BUILD command?
+ # Slightly less uncertain behavior
+ # (OUTPUT commands run non-deterministically AFAIK) + poppy 2009-04-19
+ # > I did not use a post build step as I could not make it notify of a
+ # > failure after the first time you build and fail a test. - daveh 2009-04-20
+ LIST(APPEND ${project}_TEST_OUTPUT ${TEST_OUTPUT})
+ ENDFOREACH (source)
+
+ # Add the test runner target per-project
+ # (replaces old _test_ok targets all over the place)
+ ADD_CUSTOM_TARGET(${project}_tests ALL DEPENDS ${${project}_TEST_OUTPUT})
+ ADD_DEPENDENCIES(${project} ${project}_tests)
+ENDMACRO(LL_ADD_PROJECT_UNIT_TESTS)
+
+FUNCTION(LL_ADD_INTEGRATION_TEST
+ testname
+ additional_source_files
+ library_dependencies
+# variable args
+ )
+ if(TEST_DEBUG)
+ message(STATUS "Adding INTEGRATION_TEST_${testname} - debug output is on")
+ endif(TEST_DEBUG)
+
+ SET(source_files
+ tests/${testname}_test.cpp
+ ${CMAKE_SOURCE_DIR}/test/test.cpp
+ ${CMAKE_SOURCE_DIR}/test/lltut.cpp
+ ${additional_source_files}
+ )
+
+ SET(libraries
+ ${library_dependencies}
+ ${GOOGLEMOCK_LIBRARIES}
+ ${PTHREAD_LIBRARY}
+ )
+
+ # Add test executable build target
+ if(TEST_DEBUG)
+ message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})")
+ endif(TEST_DEBUG)
+ ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})
+ SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}")
+ if(STANDALONE)
+ SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES COMPILE_FLAGS -I"${TUT_INCLUDE_DIR}")
+ endif(STANDALONE)
+
+ # Add link deps to the executable
+ if(TEST_DEBUG)
+ message(STATUS "TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})")
+ endif(TEST_DEBUG)
+ TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})
+
+ # Create the test running command
+ SET(test_command ${ARGN})
+ GET_TARGET_PROPERTY(TEST_EXE INTEGRATION_TEST_${testname} LOCATION)
+ LIST(FIND test_command "{}" test_exe_pos)
+ IF(test_exe_pos LESS 0)
+ # The {} marker means "the full pathname of the test executable."
+ # test_exe_pos -1 means we didn't find it -- so append the test executable
+ # name to $ARGN, the variable part of the arg list. This is convenient
+ # shorthand for both straightforward execution of the test program (empty
+ # $ARGN) and for running a "wrapper" program of some kind accepting the
+ # pathname of the test program as the last of its args. You need specify
+ # {} only if the test program's pathname isn't the last argument in the
+ # desired command line.
+ LIST(APPEND test_command "${TEST_EXE}")
+ ELSE (test_exe_pos LESS 0)
+ # Found {} marker at test_exe_pos. Remove the {}...
+ LIST(REMOVE_AT test_command test_exe_pos)
+ # ...and replace it with the actual name of the test executable.
+ LIST(INSERT test_command test_exe_pos "${TEST_EXE}")
+ ENDIF (test_exe_pos LESS 0)
+
+ SET_TEST_PATH(LD_LIBRARY_PATH)
+ LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${test_command})
+
+ if(TEST_DEBUG)
+ message(STATUS "TEST_SCRIPT_CMD: ${TEST_SCRIPT_CMD}")
+ endif(TEST_DEBUG)
+
+ ADD_CUSTOM_COMMAND(
+ TARGET INTEGRATION_TEST_${testname}
+ POST_BUILD
+ COMMAND ${TEST_SCRIPT_CMD}
+ )
+
+ # Use CTEST? Not sure how to yet...
+ # ADD_TEST(INTEGRATION_TEST_RUNNER_${testname} ${TEST_SCRIPT_CMD})
+
+ENDFUNCTION(LL_ADD_INTEGRATION_TEST)
+
+MACRO(SET_TEST_PATH LISTVAR)
+ IF(WINDOWS)
+ # We typically build/package only Release variants of third-party
+ # libraries, so append the Release staging dir in case the library being
+ # sought doesn't have a debug variant.
+ set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR} ${SHARED_LIB_STAGING_DIR}/Release)
+ ELSEIF(DARWIN)
+ # We typically build/package only Release variants of third-party
+ # libraries, so append the Release staging dir in case the library being
+ # sought doesn't have a debug variant.
+ set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/Resources ${SHARED_LIB_STAGING_DIR}/Release/Resources /usr/lib)
+ ELSE(WINDOWS)
+ # Linux uses a single staging directory anyway.
+ IF (STANDALONE)
+ set(${LISTVAR} ${CMAKE_BINARY_DIR}/llcommon /usr/lib /usr/local/lib)
+ ELSE (STANDALONE)
+ set(${LISTVAR} ${SHARED_LIB_STAGING_DIR} /usr/lib)
+ ENDIF (STANDALONE)
+ ENDIF(WINDOWS)
+ENDMACRO(SET_TEST_PATH)
diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index d1ab264a41..17e211cb99 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -13,7 +13,14 @@ set(LLCOMMON_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ) -set(LLCOMMON_LIBRARIES llcommon) +if (LINUX) + # In order to support using ld.gold on linux, we need to explicitely + # specify all libraries that llcommon uses. + # llcommon uses `clock_gettime' which is provided by librt on linux. + set(LLCOMMON_LIBRARIES llcommon rt) +else (LINUX) + set(LLCOMMON_LIBRARIES llcommon) +endif (LINUX) add_definitions(${TCMALLOC_FLAG}) diff --git a/indra/cmake/LLKDU.cmake b/indra/cmake/LLKDU.cmake index 27c8ada686..f5cbad03a6 100644 --- a/indra/cmake/LLKDU.cmake +++ b/indra/cmake/LLKDU.cmake @@ -1,7 +1,20 @@ # -*- cmake -*- include(Prebuilt) +# USE_KDU can be set when launching cmake or develop.py as an option using the argument -DUSE_KDU:BOOL=ON +# When building using proprietary binaries though (i.e. having access to LL private servers), we always build with KDU if (INSTALL_PROPRIETARY AND NOT STANDALONE) - use_prebuilt_binary(kdu) - set(LLKDU_LIBRARY llkdu) + set(USE_KDU ON) endif (INSTALL_PROPRIETARY AND NOT STANDALONE) + +if (USE_KDU) + use_prebuilt_binary(kdu) + if (WINDOWS) + set(KDU_LIBRARY kdu.lib) + else (WINDOWS) + set(KDU_LIBRARY libkdu.a) + endif (WINDOWS) + set(KDU_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/kdu) + set(LLKDU_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llkdu) + set(LLKDU_LIBRARIES llkdu) +endif (USE_KDU) diff --git a/indra/cmake/LLPlugin.cmake b/indra/cmake/LLPlugin.cmake index 9722f16c3c..7ee404b9bd 100644 --- a/indra/cmake/LLPlugin.cmake +++ b/indra/cmake/LLPlugin.cmake @@ -5,4 +5,10 @@ set(LLPLUGIN_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llplugin ) -set(LLPLUGIN_LIBRARIES llplugin) +if (LINUX) + # In order to support using ld.gold on linux, we need to explicitely + # specify all libraries that llplugin uses. + set(LLPLUGIN_LIBRARIES llplugin pthread) +else (LINUX) + set(LLPLUGIN_LIBRARIES llplugin) +endif (LINUX) diff --git a/indra/cmake/NDOF.cmake b/indra/cmake/NDOF.cmake index dad74e99b1..7a463d1190 100644 --- a/indra/cmake/NDOF.cmake +++ b/indra/cmake/NDOF.cmake @@ -1,14 +1,32 @@ # -*- cmake -*- include(Prebuilt) -use_prebuilt_binary(ndofdev) +set(NDOF ON CACHE BOOL "Use NDOF space navigator joystick library.") -if (WINDOWS OR DARWIN OR LINUX) +if (NDOF) + if (STANDALONE) + set(NDOF_FIND_REQUIRED ON) + include(FindNDOF) + else (STANDALONE) + use_prebuilt_binary(ndofdev) + + if (WINDOWS) + set(NDOF_LIBRARY libndofdev) + elseif (DARWIN OR LINUX) + set(NDOF_LIBRARY ndofdev) + endif (WINDOWS) + + set(NDOF_INCLUDE_DIR ${ARCH_PREBUILT_DIRS}/include/ndofdev) + set(NDOF_FOUND 1) + endif (STANDALONE) +endif (NDOF) + +if (NDOF_FOUND) add_definitions(-DLIB_NDOF=1) -endif (WINDOWS OR DARWIN OR LINUX) + include_directories(${NDOF_INCLUDE_DIR}) +else (NDOF_FOUND) + message(STATUS "Building without N-DoF joystick support") + set(NDOF_INCLUDE_DIR "") + set(NDOF_LIBRARY "") +endif (NDOF_FOUND) -if (WINDOWS) - set(NDOF_LIBRARY libndofdev) -elseif (DARWIN OR LINUX) - set(NDOF_LIBRARY ndofdev) -endif (WINDOWS) diff --git a/indra/cmake/PulseAudio.cmake b/indra/cmake/PulseAudio.cmake index e918de0198..360a971058 100644 --- a/indra/cmake/PulseAudio.cmake +++ b/indra/cmake/PulseAudio.cmake @@ -1,7 +1,7 @@ # -*- cmake -*- include(Prebuilt) -set(PULSEAUDIO ON CACHE BOOL "Build with PulseAudio support, if available.") +set(PULSEAUDIO OFF CACHE BOOL "Build with PulseAudio support, if available.") if (PULSEAUDIO) if (STANDALONE) diff --git a/indra/cmake/Python.cmake b/indra/cmake/Python.cmake index 0901c1b7a2..748c8c2bec 100644 --- a/indra/cmake/Python.cmake +++ b/indra/cmake/Python.cmake @@ -9,10 +9,12 @@ if (WINDOWS) NAMES python25.exe python23.exe python.exe NO_DEFAULT_PATH # added so that cmake does not find cygwin python PATHS + [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.7\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.6\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.7\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.6\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath] diff --git a/indra/cmake/TemplateCheck.cmake b/indra/cmake/TemplateCheck.cmake index fa4e387dd5..90d58d93ad 100644 --- a/indra/cmake/TemplateCheck.cmake +++ b/indra/cmake/TemplateCheck.cmake @@ -7,8 +7,9 @@ macro (check_message_template _target) TARGET ${_target} POST_BUILD COMMAND ${PYTHON_EXECUTABLE} - ARGS ${SCRIPTS_DIR}/template_verifier.py - --mode=development --cache_master - COMMENT "Verifying message template" + ARGS ${SCRIPTS_DIR}/md5check.py + 3f19d130400c547de36278a6b6f9b028 + ${SCRIPTS_DIR}/messages/message_template.msg + COMMENT "Verifying message template - See http://wiki.secondlife.com/wiki/Template_verifier.py" ) endmacro (check_message_template) diff --git a/indra/cmake/Tut.cmake b/indra/cmake/Tut.cmake index 784560471d..738c08c42f 100644 --- a/indra/cmake/Tut.cmake +++ b/indra/cmake/Tut.cmake @@ -6,7 +6,6 @@ set(TUT_FIND_QUIETLY TRUE) if (STANDALONE) include(FindTut) - include_directories(${TUT_INCLUDE_DIR}) else (STANDALONE) use_prebuilt_binary(tut) endif (STANDALONE) diff --git a/indra/cmake/ViewerMiscLibs.cmake b/indra/cmake/ViewerMiscLibs.cmake index 32c4bc81df..df013b1665 100644 --- a/indra/cmake/ViewerMiscLibs.cmake +++ b/indra/cmake/ViewerMiscLibs.cmake @@ -3,7 +3,7 @@ include(Prebuilt) if (NOT STANDALONE) use_prebuilt_binary(libuuid) - use_prebuilt_binary(vivox) + use_prebuilt_binary(slvoice) use_prebuilt_binary(fontconfig) endif(NOT STANDALONE) diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake index 12ba1b1b35..1f5b0f5d84 100644 --- a/indra/cmake/WebKitLibPlugin.cmake +++ b/indra/cmake/WebKitLibPlugin.cmake @@ -3,6 +3,29 @@ include(Linking) include(Prebuilt) if (STANDALONE) + # The minimal version, 4.4.3, is rather arbitrary: it's the version in Debian/Lenny. + find_package(Qt4 4.4.3 COMPONENTS QtCore QtGui QtNetwork QtOpenGL QtWebKit REQUIRED) + include(${QT_USE_FILE}) + set(QTDIR $ENV{QTDIR}) + if (QTDIR AND NOT "${QT_BINARY_DIR}" STREQUAL "${QTDIR}/bin") + message(FATAL_ERROR "\"${QT_BINARY_DIR}\" is unequal \"${QTDIR}/bin\"; " + "Qt is found by looking for qmake in your PATH. " + "Please set your PATH such that 'qmake' is found in \$QTDIR/bin, " + "or unset QTDIR if the found Qt is correct.") + endif (QTDIR AND NOT "${QT_BINARY_DIR}" STREQUAL "${QTDIR}/bin") + find_package(LLQtWebkit REQUIRED QUIET) + # Add the plugins. + set(QT_PLUGIN_LIBRARIES) + foreach(qlibname qgif qjpeg) + find_library(QT_PLUGIN_${qlibname} ${qlibname} PATHS ${QT_PLUGINS_DIR}/imageformats NO_DEFAULT_PATH) + if (QT_PLUGIN_${qlibname}) + list(APPEND QT_PLUGIN_LIBRARIES ${QT_PLUGIN_${qlibname}}) + else (QT_PLUGIN_${qtlibname}) + message(FATAL_ERROR "Could not find the Qt plugin ${qlibname} in \"${QT_PLUGINS_DIR}/imageformats\"!") + endif (QT_PLUGIN_${qlibname}) + endforeach(qlibname) + # qjpeg depends on libjpeg + list(APPEND QT_PLUGIN_LIBRARIES jpeg) set(WEBKITLIBPLUGIN OFF CACHE BOOL "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.") else (STANDALONE) @@ -35,7 +58,7 @@ elseif (DARWIN) ) elseif (LINUX) if (STANDALONE) - set(WEBKIT_PLUGIN_LIBRARIES llqtwebkit) + set(WEBKIT_PLUGIN_LIBRARIES ${LLQTWEBKIT_LIBRARY} ${QT_LIBRARIES} ${QT_PLUGIN_LIBRARIES}) else (STANDALONE) set(WEBKIT_PLUGIN_LIBRARIES llqtwebkit diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt index 452d37d3be..e0772e55ca 100644 --- a/indra/integration_tests/llui_libtest/CMakeLists.txt +++ b/indra/integration_tests/llui_libtest/CMakeLists.txt @@ -10,6 +10,7 @@ include(00-Common) include(LLCommon) include(LLImage) include(LLImageJ2COJ) # ugh, needed for images +include(LLKDU) include(LLMath) include(LLMessage) include(LLRender) @@ -71,6 +72,11 @@ endif (DARWIN) target_link_libraries(llui_libtest llui llmessage + ${LLRENDER_LIBRARIES} + ${LLIMAGE_LIBRARIES} + ${LLKDU_LIBRARIES} + ${KDU_LIBRARY} + ${LLIMAGEJ2COJ_LIBRARIES} ${OS_LIBRARIES} ${GOOGLE_PERFTOOLS_LIBRARIES} ) diff --git a/indra/integration_tests/llui_libtest/llui_libtest.cpp b/indra/integration_tests/llui_libtest/llui_libtest.cpp index 1f15b73182..c34115ee80 100644 --- a/indra/integration_tests/llui_libtest/llui_libtest.cpp +++ b/indra/integration_tests/llui_libtest/llui_libtest.cpp @@ -174,7 +174,7 @@ void export_test_floaters() std::string delim = gDirUtilp->getDirDelimiter(); std::string xui_dir = get_xui_dir() + "en" + delim; std::string filename; - while (gDirUtilp->getNextFileInDir(xui_dir, "floater_test_*.xml", filename, false)) + while (gDirUtilp->getNextFileInDir(xui_dir, "floater_test_*.xml", filename)) { if (filename.find("_new.xml") != std::string::npos) { diff --git a/indra/lib/python/indra/util/test_win32_manifest.py b/indra/lib/python/indra/util/test_win32_manifest.py index 786521c068..da8ee6c545 100644 --- a/indra/lib/python/indra/util/test_win32_manifest.py +++ b/indra/lib/python/indra/util/test_win32_manifest.py @@ -52,20 +52,22 @@ def get_HKLM_registry_value(key_str, value_str): def find_vc_dir(): supported_versions = (r'8.0', r'9.0') + supported_products = (r'VisualStudio', r'VCExpress') value_str = (r'ProductDir') - for version in supported_versions: - key_str = (r'SOFTWARE\Microsoft\VisualStudio\%s\Setup\VC' % - version) - try: - return get_HKLM_registry_value(key_str, value_str) - except WindowsError, err: - x64_key_str = (r'SOFTWARE\Wow6432Node\Microsoft\VisualStudio\%s\Setup\VS' % - version) + for product in supported_products: + for version in supported_versions: + key_str = (r'SOFTWARE\Microsoft\%s\%s\Setup\VC' % + (product, version)) try: - return get_HKLM_registry_value(x64_key_str, value_str) - except: - print >> sys.stderr, "Didn't find MS VC version %s " % version + return get_HKLM_registry_value(key_str, value_str) + except WindowsError, err: + x64_key_str = (r'SOFTWARE\Wow6432Node\Microsoft\VisualStudio\%s\Setup\VS' % + version) + try: + return get_HKLM_registry_value(x64_key_str, value_str) + except: + print >> sys.stderr, "Didn't find MS %s version %s " % (product,version) raise diff --git a/indra/linux_updater/linux_updater.cpp b/indra/linux_updater/linux_updater.cpp index be4d810860..d909516bf8 100644 --- a/indra/linux_updater/linux_updater.cpp +++ b/indra/linux_updater/linux_updater.cpp @@ -49,6 +49,7 @@ const guint ROTATE_IMAGE_TIMEOUT = 8000; typedef struct _updater_app_state { std::string app_name; std::string url; + std::string file; std::string image_dir; std::string dest_dir; std::string strings_dirs; @@ -113,7 +114,7 @@ BOOL install_package(std::string package_file, std::string destination); BOOL spawn_viewer(UpdaterAppState *app_state); extern "C" { - void on_window_closed(GtkWidget *sender, gpointer state); + void on_window_closed(GtkWidget *sender, GdkEvent *event, gpointer state); gpointer worker_thread_cb(gpointer *data); int download_progress_cb(gpointer data, double t, double d, double utotal, double ulnow); gboolean rotate_image_cb(gpointer data); @@ -216,11 +217,11 @@ gboolean rotate_image_cb(gpointer data) std::string next_image_filename(std::string& image_path) { std::string image_filename; - gDirUtilp->getNextFileInDir(image_path, "/*.jpg", image_filename, true); + gDirUtilp->getNextFileInDir(image_path, "/*.jpg", image_filename); return image_path + "/" + image_filename; } -void on_window_closed(GtkWidget *sender, gpointer data) +void on_window_closed(GtkWidget *sender, GdkEvent* event, gpointer data) { UpdaterAppState *app_state; @@ -266,85 +267,95 @@ gpointer worker_thread_cb(gpointer data) CURLcode result; FILE *package_file; GError *error = NULL; - char *tmp_filename = NULL; int fd; //g_return_val_if_fail (data != NULL, NULL); app_state = (UpdaterAppState *) data; try { - // create temporary file to store the package. - fd = g_file_open_tmp - ("secondlife-update-XXXXXX", &tmp_filename, &error); - if (error != NULL) - { - llerrs << "Unable to create temporary file: " - << error->message - << llendl; - - g_error_free(error); - throw 0; - } - package_file = fdopen(fd, "wb"); - if (package_file == NULL) + if(!app_state->url.empty()) { - llerrs << "Failed to create temporary file: " - << tmp_filename - << llendl; + char* tmp_local_filename = NULL; + // create temporary file to store the package. + fd = g_file_open_tmp + ("secondlife-update-XXXXXX", &tmp_local_filename, &error); + if (error != NULL) + { + llerrs << "Unable to create temporary file: " + << error->message + << llendl; - gdk_threads_enter(); - display_error(app_state->window, - LLTrans::getString("UpdaterFailDownloadTitle"), - LLTrans::getString("UpdaterFailUpdateDescriptive")); - gdk_threads_leave(); - throw 0; - } + g_error_free(error); + throw 0; + } + + if(tmp_local_filename != NULL) + { + app_state->file = tmp_local_filename; + g_free(tmp_local_filename); + } - // initialize curl and start downloading the package - llinfos << "Downloading package: " << app_state->url << llendl; + package_file = fdopen(fd, "wb"); + if (package_file == NULL) + { + llerrs << "Failed to create temporary file: " + << app_state->file.c_str() + << llendl; + + gdk_threads_enter(); + display_error(app_state->window, + LLTrans::getString("UpdaterFailDownloadTitle"), + LLTrans::getString("UpdaterFailUpdateDescriptive")); + gdk_threads_leave(); + throw 0; + } - curl = curl_easy_init(); - if (curl == NULL) - { - llerrs << "Failed to initialize libcurl" << llendl; + // initialize curl and start downloading the package + llinfos << "Downloading package: " << app_state->url << llendl; - gdk_threads_enter(); - display_error(app_state->window, - LLTrans::getString("UpdaterFailDownloadTitle"), - LLTrans::getString("UpdaterFailUpdateDescriptive")); - gdk_threads_leave(); - throw 0; - } + curl = curl_easy_init(); + if (curl == NULL) + { + llerrs << "Failed to initialize libcurl" << llendl; + + gdk_threads_enter(); + display_error(app_state->window, + LLTrans::getString("UpdaterFailDownloadTitle"), + LLTrans::getString("UpdaterFailUpdateDescriptive")); + gdk_threads_leave(); + throw 0; + } - curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str()); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE); - curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, - &download_progress_cb); - curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state); + curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str()); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE); + curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, + &download_progress_cb); + curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state); - result = curl_easy_perform(curl); - fclose(package_file); - curl_easy_cleanup(curl); + result = curl_easy_perform(curl); + fclose(package_file); + curl_easy_cleanup(curl); - if (result) - { - llerrs << "Failed to download update: " - << app_state->url - << llendl; + if (result) + { + llerrs << "Failed to download update: " + << app_state->url + << llendl; - gdk_threads_enter(); - display_error(app_state->window, - LLTrans::getString("UpdaterFailDownloadTitle"), - LLTrans::getString("UpdaterFailUpdateDescriptive")); - gdk_threads_leave(); + gdk_threads_enter(); + display_error(app_state->window, + LLTrans::getString("UpdaterFailDownloadTitle"), + LLTrans::getString("UpdaterFailUpdateDescriptive")); + gdk_threads_leave(); - throw 0; + throw 0; + } } - + // now pulse the progres bar back and forth while the package is // being unpacked gdk_threads_enter(); @@ -357,7 +368,7 @@ gpointer worker_thread_cb(gpointer data) // *TODO: if the destination is not writable, terminate this // thread and show file chooser? - if (!install_package(tmp_filename, app_state->dest_dir)) + if (!install_package(app_state->file.c_str(), app_state->dest_dir)) { llwarns << "Failed to install package to destination: " << app_state->dest_dir @@ -392,15 +403,6 @@ gpointer worker_thread_cb(gpointer data) app_state->failure = TRUE; } - // FIXME: delete package file also if delete-event is raised on window - if (tmp_filename != NULL) - { - if (gDirUtilp->fileExists(tmp_filename)) - { - LLFile::remove(tmp_filename); - } - } - gdk_threads_enter(); updater_app_quit(app_state); gdk_threads_leave(); @@ -712,7 +714,7 @@ BOOL spawn_viewer(UpdaterAppState *app_state) void show_usage_and_exit() { - std::cout << "Usage: linux-updater --url URL --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE" + std::cout << "Usage: linux-updater <--url URL | --file FILE> --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE" << "[--image-dir PATH]" << std::endl; exit(1); @@ -728,6 +730,10 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state) { app_state->url = argv[i]; } + else if ((!strcmp(argv[i], "--file")) && (++i < argc)) + { + app_state->file = argv[i]; + } else if ((!strcmp(argv[i], "--name")) && (++i < argc)) { app_state->app_name = argv[i]; @@ -756,7 +762,7 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state) } if (app_state->app_name.empty() - || app_state->url.empty() + || (app_state->url.empty() && app_state->file.empty()) || app_state->dest_dir.empty()) { show_usage_and_exit(); @@ -771,10 +777,10 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state) int main(int argc, char **argv) { - UpdaterAppState app_state; + UpdaterAppState* app_state = new UpdaterAppState; GThread *worker_thread; - parse_args_and_init(argc, argv, &app_state); + parse_args_and_init(argc, argv, app_state); // Initialize logger, and rename old log file gDirUtilp->initAppDirs("SecondLife"); @@ -797,17 +803,29 @@ int main(int argc, char **argv) gtk_init(&argc, &argv); // create UI - updater_app_ui_init(&app_state); + updater_app_ui_init(app_state); //llinfos << "SAMPLE TRANSLATION IS: " << LLTrans::getString("LoginInProgress") << llendl; // create download thread worker_thread = g_thread_create - (GThreadFunc(worker_thread_cb), &app_state, FALSE, NULL); + (GThreadFunc(worker_thread_cb), app_state, FALSE, NULL); gdk_threads_enter(); gtk_main(); gdk_threads_leave(); - return (app_state.failure == FALSE) ? 0 : 1; + // Delete the file only if created from url download. + if(!app_state->url.empty() && !app_state->file.empty()) + { + if (gDirUtilp->fileExists(app_state->file)) + { + LLFile::remove(app_state->file); + } + } + + bool success = !app_state->failure; + delete app_state; + return success ? 0 : 1; } + diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index e869b9717c..21ec622819 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -14,7 +14,6 @@ include(LLVFS) include_directories( ${LLAUDIO_INCLUDE_DIRS} - ${FMOD_INCLUDE_DIR} ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} @@ -45,6 +44,10 @@ set(llaudio_HEADER_FILES ) if (FMOD) + include_directories( + ${FMOD_INCLUDE_DIR} + ) + list(APPEND llaudio_SOURCE_FILES llaudioengine_fmod.cpp lllistener_fmod.cpp diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 1cc03bddb8..c9cb1cd6e7 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -1557,6 +1557,10 @@ bool LLAudioSource::hasPendingPreloads() const LLAudioData *adp = iter->second; // note: a bad UUID will forever be !hasDecodedData() // but also !hasValidData(), hence the check for hasValidData() + if (!adp) + { + continue; + } if (!adp->hasDecodedData() && adp->hasValidData()) { // This source is still waiting for a preload diff --git a/indra/llaudio/llvorbisencode.cpp b/indra/llaudio/llvorbisencode.cpp index 9f479189d7..0e0c80a456 100644 --- a/indra/llaudio/llvorbisencode.cpp +++ b/indra/llaudio/llvorbisencode.cpp @@ -120,6 +120,13 @@ S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& erro + ((U32) wav_header[5] << 8) + wav_header[4]; + if (chunk_length > physical_file_size - file_pos - 4) + { + infile.close(); + error_msg = "SoundFileInvalidChunkSize"; + return(LLVORBISENC_CHUNK_SIZE_ERR); + } + // llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl; if (!(strncmp((char *)&(wav_header[0]),"fmt ",4))) diff --git a/indra/llaudio/llvorbisencode.h b/indra/llaudio/llvorbisencode.h index d33aacf1ea..6b22a2cb59 100644 --- a/indra/llaudio/llvorbisencode.h +++ b/indra/llaudio/llvorbisencode.h @@ -38,6 +38,7 @@ const S32 LLVORBISENC_MULTICHANNEL_ERR = 7; // can't do stereo const S32 LLVORBISENC_UNSUPPORTED_SAMPLE_RATE = 8; // unsupported sample rate const S32 LLVORBISENC_UNSUPPORTED_WORD_SIZE = 9; // unsupported word size const S32 LLVORBISENC_CLIP_TOO_LONG = 10; // source file is too long +const S32 LLVORBISENC_CHUNK_SIZE_ERR = 11; // chunk size is wrong const F32 LLVORBIS_CLIP_MAX_TIME = 10.0f; const U8 LLVORBIS_CLIP_MAX_CHANNELS = 2; diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 7bad780dd8..9342a22d46 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -70,6 +70,7 @@ set(llcommon_SOURCE_FILES llmemorystream.cpp llmemtype.cpp llmetrics.cpp + llmetricperformancetester.cpp llmortician.cpp lloptioninterface.cpp llptrto.cpp @@ -92,6 +93,7 @@ set(llcommon_SOURCE_FILES llstringtable.cpp llsys.cpp llthread.cpp + llthreadsafequeue.cpp lltimer.cpp lluri.cpp lluuid.cpp @@ -186,6 +188,7 @@ set(llcommon_HEADER_FILES llmemorystream.h llmemtype.h llmetrics.h + llmetricperformancetester.h llmortician.h llnametable.h lloptioninterface.h @@ -223,6 +226,7 @@ set(llcommon_HEADER_FILES llstringtable.h llsys.h llthread.h + llthreadsafequeue.h lltimer.h lltreeiterators.h lluri.h diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index b0618bfe59..95cb606240 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -245,9 +245,6 @@ const U8 SIM_ACCESS_ADULT = 42; // Seriously Adult Only const U8 SIM_ACCESS_DOWN = 254; const U8 SIM_ACCESS_MAX = SIM_ACCESS_ADULT; -// group constants -const S32 MAX_AGENT_GROUPS = 25; - // attachment constants const S32 MAX_AGENT_ATTACHMENTS = 38; const U8 ATTACHMENT_ADD = 0x80; @@ -300,6 +297,14 @@ const U32 START_LOCATION_ID_COUNT = 6; // group constants const U32 GROUP_MIN_SIZE = 2; +// gMaxAgentGroups is now sent by login.cgi, which +// looks it up from globals.xml. +// +// For now we need an old default value however, +// so the viewer can be deployed ahead of login.cgi. +// +const S32 DEFAULT_MAX_AGENT_GROUPS = 25; + // radius within which a chat message is fully audible const F32 CHAT_WHISPER_RADIUS = 10.f; const F32 CHAT_NORMAL_RADIUS = 20.f; diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index eebd5ed0a6..39daefd1ad 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -90,6 +90,10 @@ S32 LL_HEARTBEAT_SIGNAL = (SIGRTMAX >= 0) ? (SIGRTMAX-0) : SIGUSR2; // the static application instance LLApp* LLApp::sApplication = NULL; +// Allows the generation of core files for post mortem under gdb +// and disables crashlogger +BOOL LLApp::sDisableCrashlogger = FALSE; + // Local flag for whether or not to do logging in signal handlers. //static BOOL LLApp::sLogInSignal = FALSE; @@ -461,11 +465,30 @@ bool LLApp::isQuitting() return (APP_STATUS_QUITTING == sStatus); } +// static bool LLApp::isExiting() { return isQuitting() || isError(); } +void LLApp::disableCrashlogger() +{ + // Disable Breakpad exception handler. + if (mExceptionHandler != 0) + { + delete mExceptionHandler; + mExceptionHandler = 0; + } + + sDisableCrashlogger = TRUE; +} + +// static +bool LLApp::isCrashloggerDisabled() +{ + return (sDisableCrashlogger == TRUE); +} + #if !LL_WINDOWS // static U32 LLApp::getSigChildCount() @@ -799,6 +822,15 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *) { llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl; } + + if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem + { + clear_signals(); + llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl; + raise(signum); + return; + } + // Flag status to ERROR, so thread_error does its work. LLApp::setError(); // Block in the signal handler until somebody says that we're done. diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index ee1d696829..a536a06ea5 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -189,6 +189,11 @@ public: // virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit. + // + // Crash logging + // + void disableCrashlogger(); // Let the OS handle the crashes + static bool isCrashloggerDisabled(); // Get the here above set value // // Application status @@ -280,6 +285,7 @@ protected: static void setStatus(EAppStatus status); // Use this to change the application status. static EAppStatus sStatus; // Reflects current application status static BOOL sErrorThreadRunning; // Set while the error thread is running + static BOOL sDisableCrashlogger; // Let the OS handle crashes for us. #if !LL_WINDOWS static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received. diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index 66ec5bad2c..d1c44c9403 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -28,6 +28,7 @@ #include "linden_common.h" #include "llapr.h" +#include "apr_dso.h" apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool. @@ -279,14 +280,31 @@ bool ll_apr_warn_status(apr_status_t status) { if(APR_SUCCESS == status) return false; char buf[MAX_STRING]; /* Flawfinder: ignore */ - apr_strerror(status, buf, MAX_STRING); + apr_strerror(status, buf, sizeof(buf)); LL_WARNS("APR") << "APR: " << buf << LL_ENDL; return true; } +bool ll_apr_warn_status(apr_status_t status, apr_dso_handle_t *handle) +{ + bool result = ll_apr_warn_status(status); + // Despite observed truncation of actual Mac dylib load errors, increasing + // this buffer to more than MAX_STRING doesn't help: it appears that APR + // stores the output in a fixed 255-character internal buffer. (*sigh*) + char buf[MAX_STRING]; /* Flawfinder: ignore */ + apr_dso_error(handle, buf, sizeof(buf)); + LL_WARNS("APR") << "APR: " << buf << LL_ENDL; + return result; +} + void ll_apr_assert_status(apr_status_t status) { - llassert(ll_apr_warn_status(status) == false); + llassert(! ll_apr_warn_status(status)); +} + +void ll_apr_assert_status(apr_status_t status, apr_dso_handle_t *handle) +{ + llassert(! ll_apr_warn_status(status, handle)); } //--------------------------------------------------------------------- diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 4930270af8..af33ce666f 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -53,6 +53,8 @@ extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp; extern apr_thread_mutex_t* gCallStacksLogMutexp; +struct apr_dso_handle_t; + /** * @brief initialize the common apr constructs -- apr itself, the * global pool, and a mutex. @@ -259,8 +261,11 @@ public: * @return Returns <code>true</code> if status is an error condition. */ bool LL_COMMON_API ll_apr_warn_status(apr_status_t status); +/// There's a whole other APR error-message function if you pass a DSO handle. +bool LL_COMMON_API ll_apr_warn_status(apr_status_t status, apr_dso_handle_t* handle); void LL_COMMON_API ll_apr_assert_status(apr_status_t status); +void LL_COMMON_API ll_apr_assert_status(apr_status_t status, apr_dso_handle_t* handle); extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool diff --git a/indra/llcommon/llavatarname.cpp b/indra/llcommon/llavatarname.cpp index b1ec9e9875..ad1845d387 100644 --- a/indra/llcommon/llavatarname.cpp +++ b/indra/llcommon/llavatarname.cpp @@ -48,7 +48,7 @@ LLAvatarName::LLAvatarName() mLegacyFirstName(), mLegacyLastName(), mIsDisplayNameDefault(false), - mIsDummy(false), + mIsTemporaryName(false), mExpires(F64_MAX), mNextUpdate(0.0) { } diff --git a/indra/llcommon/llavatarname.h b/indra/llcommon/llavatarname.h index 145aeccd35..ba258d6d52 100644 --- a/indra/llcommon/llavatarname.h +++ b/indra/llcommon/llavatarname.h @@ -79,7 +79,7 @@ public: // Under error conditions, we may insert "dummy" records with // names like "???" into caches as placeholders. These can be // shown in UI, but are not serialized. - bool mIsDummy; + bool mIsTemporaryName; // Names can change, so need to keep track of when name was // last checked. diff --git a/indra/llcommon/lldarray.h b/indra/llcommon/lldarray.h index a8cd03b42a..131b819c99 100644 --- a/indra/llcommon/lldarray.h +++ b/indra/llcommon/lldarray.h @@ -51,7 +51,7 @@ public: LLDynamicArray(S32 size=0) : std::vector<Type>(size) { if (size < BlockSize) std::vector<Type>::reserve(BlockSize); } - void reset() { std::vector<Type>::resize(0); } + void reset() { std::vector<Type>::clear(); } // ACCESSORS const Type& get(S32 index) const { return std::vector<Type>::operator[](index); } diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 84a6620a77..97e2bdeb57 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -475,7 +475,7 @@ void LLEventPump::stopListening(const std::string& name) *****************************************************************************/ bool LLEventStream::post(const LLSD& event) { - if (! mEnabled) + if (! mEnabled || !mSignal) { return false; } @@ -515,6 +515,8 @@ bool LLEventQueue::post(const LLSD& event) void LLEventQueue::flush() { + if(!mSignal) return; + // Consider the case when a given listener on this LLEventQueue posts yet // another event on the same queue. If we loop over mEventQueue directly, // we'll end up processing all those events during the same flush() call diff --git a/indra/llcommon/llfasttimer_class.cpp b/indra/llcommon/llfasttimer_class.cpp index c45921cdec..bce87ada96 100644 --- a/indra/llcommon/llfasttimer_class.cpp +++ b/indra/llcommon/llfasttimer_class.cpp @@ -56,6 +56,7 @@ bool LLFastTimer::sPauseHistory = 0; bool LLFastTimer::sResetHistory = 0; LLFastTimer::CurTimerData LLFastTimer::sCurTimerData; BOOL LLFastTimer::sLog = FALSE; +std::string LLFastTimer::sLogName = ""; BOOL LLFastTimer::sMetricLog = FALSE; LLMutex* LLFastTimer::sLogLock = NULL; std::queue<LLSD> LLFastTimer::sLogQueue; diff --git a/indra/llcommon/llfasttimer_class.h b/indra/llcommon/llfasttimer_class.h index 1158ac5140..eb9789682b 100644 --- a/indra/llcommon/llfasttimer_class.h +++ b/indra/llcommon/llfasttimer_class.h @@ -211,6 +211,7 @@ public: static std::queue<LLSD> sLogQueue; static BOOL sLog; static BOOL sMetricLog; + static std::string sLogName; static bool sPauseHistory; static bool sResetHistory; static U64 sTimerCycles; diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 289ce0bc2c..8f02391e75 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -318,7 +318,12 @@ void llofstream::close() if(is_open()) { if (_Filebuffer->close() == 0) + { _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/ + } + delete _Filebuffer; + _Filebuffer = NULL; + _ShouldClose = false; } } diff --git a/indra/llcommon/llmetricperformancetester.cpp b/indra/llcommon/llmetricperformancetester.cpp new file mode 100644 index 0000000000..5fa3a5ea07 --- /dev/null +++ b/indra/llcommon/llmetricperformancetester.cpp @@ -0,0 +1,254 @@ +/** + * @file llmetricperformancetester.cpp + * @brief LLMetricPerformanceTesterBasic and LLMetricPerformanceTesterWithSession classes implementation + * + * $LicenseInfo:firstyear=2004&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 "indra_constants.h" +#include "llerror.h" +#include "llsdserialize.h" +#include "llstat.h" +#include "lltreeiterators.h" +#include "llmetricperformancetester.h" + +//---------------------------------------------------------------------------------------------- +// LLMetricPerformanceTesterBasic : static methods and testers management +//---------------------------------------------------------------------------------------------- + +LLMetricPerformanceTesterBasic::name_tester_map_t LLMetricPerformanceTesterBasic::sTesterMap ; + +/*static*/ +void LLMetricPerformanceTesterBasic::cleanClass() +{ + for (name_tester_map_t::iterator iter = sTesterMap.begin() ; iter != sTesterMap.end() ; ++iter) + { + delete iter->second ; + } + sTesterMap.clear() ; +} + +/*static*/ +BOOL LLMetricPerformanceTesterBasic::addTester(LLMetricPerformanceTesterBasic* tester) +{ + llassert_always(tester != NULL); + std::string name = tester->getTesterName() ; + if (getTester(name)) + { + llerrs << "Tester name is already used by some other tester : " << name << llendl ; + return FALSE; + } + + sTesterMap.insert(std::make_pair(name, tester)); + return TRUE; +} + +/*static*/ +LLMetricPerformanceTesterBasic* LLMetricPerformanceTesterBasic::getTester(std::string name) +{ + // Check for the requested metric name + name_tester_map_t::iterator found_it = sTesterMap.find(name) ; + if (found_it != sTesterMap.end()) + { + return found_it->second ; + } + return NULL ; +} + +/*static*/ +// Return TRUE if this metric is requested or if the general default "catch all" metric is requested +BOOL LLMetricPerformanceTesterBasic::isMetricLogRequested(std::string name) +{ + return (LLFastTimer::sMetricLog && ((LLFastTimer::sLogName == name) || (LLFastTimer::sLogName == DEFAULT_METRIC_NAME))); +} + + +//---------------------------------------------------------------------------------------------- +// LLMetricPerformanceTesterBasic : Tester instance methods +//---------------------------------------------------------------------------------------------- + +LLMetricPerformanceTesterBasic::LLMetricPerformanceTesterBasic(std::string name) : + mName(name), + mCount(0) +{ + if (mName == std::string()) + { + llerrs << "LLMetricPerformanceTesterBasic construction invalid : Empty name passed to constructor" << llendl ; + } + + mValidInstance = LLMetricPerformanceTesterBasic::addTester(this) ; +} + +LLMetricPerformanceTesterBasic::~LLMetricPerformanceTesterBasic() +{ +} + +void LLMetricPerformanceTesterBasic::preOutputTestResults(LLSD* sd) +{ + incrementCurrentCount() ; + (*sd)[getCurrentLabelName()]["Name"] = mName ; +} + +void LLMetricPerformanceTesterBasic::postOutputTestResults(LLSD* sd) +{ + LLMutexLock lock(LLFastTimer::sLogLock); + LLFastTimer::sLogQueue.push((*sd)); +} + +void LLMetricPerformanceTesterBasic::outputTestResults() +{ + LLSD sd; + + preOutputTestResults(&sd) ; + outputTestRecord(&sd) ; + postOutputTestResults(&sd) ; +} + +void LLMetricPerformanceTesterBasic::addMetric(std::string str) +{ + mMetricStrings.push_back(str) ; +} + +/*virtual*/ +void LLMetricPerformanceTesterBasic::analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current) +{ + resetCurrentCount() ; + + std::string current_label = getCurrentLabelName(); + BOOL in_base = (*base).has(current_label) ; + BOOL in_current = (*current).has(current_label) ; + + while(in_base || in_current) + { + LLSD::String label = current_label ; + + if(in_base && in_current) + { + *os << llformat("%s\n", label.c_str()) ; + + for(U32 index = 0 ; index < mMetricStrings.size() ; index++) + { + switch((*current)[label][ mMetricStrings[index] ].type()) + { + case LLSD::TypeInteger: + compareTestResults(os, mMetricStrings[index], + (S32)((*base)[label][ mMetricStrings[index] ].asInteger()), (S32)((*current)[label][ mMetricStrings[index] ].asInteger())) ; + break ; + case LLSD::TypeReal: + compareTestResults(os, mMetricStrings[index], + (F32)((*base)[label][ mMetricStrings[index] ].asReal()), (F32)((*current)[label][ mMetricStrings[index] ].asReal())) ; + break; + default: + llerrs << "unsupported metric " << mMetricStrings[index] << " LLSD type: " << (S32)(*current)[label][ mMetricStrings[index] ].type() << llendl ; + } + } + } + + incrementCurrentCount(); + current_label = getCurrentLabelName(); + in_base = (*base).has(current_label) ; + in_current = (*current).has(current_label) ; + } +} + +/*virtual*/ +void LLMetricPerformanceTesterBasic::compareTestResults(std::ofstream* os, std::string metric_string, S32 v_base, S32 v_current) +{ + *os << llformat(" ,%s, %d, %d, %d, %.4f\n", metric_string.c_str(), v_base, v_current, + v_current - v_base, (v_base != 0) ? 100.f * v_current / v_base : 0) ; +} + +/*virtual*/ +void LLMetricPerformanceTesterBasic::compareTestResults(std::ofstream* os, std::string metric_string, F32 v_base, F32 v_current) +{ + *os << llformat(" ,%s, %.4f, %.4f, %.4f, %.4f\n", metric_string.c_str(), v_base, v_current, + v_current - v_base, (fabs(v_base) > 0.0001f) ? 100.f * v_current / v_base : 0.f ) ; +} + +//---------------------------------------------------------------------------------------------- +// LLMetricPerformanceTesterWithSession +//---------------------------------------------------------------------------------------------- + +LLMetricPerformanceTesterWithSession::LLMetricPerformanceTesterWithSession(std::string name) : + LLMetricPerformanceTesterBasic(name), + mBaseSessionp(NULL), + mCurrentSessionp(NULL) +{ +} + +LLMetricPerformanceTesterWithSession::~LLMetricPerformanceTesterWithSession() +{ + if (mBaseSessionp) + { + delete mBaseSessionp ; + mBaseSessionp = NULL ; + } + if (mCurrentSessionp) + { + delete mCurrentSessionp ; + mCurrentSessionp = NULL ; + } +} + +/*virtual*/ +void LLMetricPerformanceTesterWithSession::analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current) +{ + // Load the base session + resetCurrentCount() ; + mBaseSessionp = loadTestSession(base) ; + + // Load the current session + resetCurrentCount() ; + mCurrentSessionp = loadTestSession(current) ; + + if (!mBaseSessionp || !mCurrentSessionp) + { + llerrs << "Error loading test sessions." << llendl ; + } + + // Compare + compareTestSessions(os) ; + + // Release memory + if (mBaseSessionp) + { + delete mBaseSessionp ; + mBaseSessionp = NULL ; + } + if (mCurrentSessionp) + { + delete mCurrentSessionp ; + mCurrentSessionp = NULL ; + } +} + + +//---------------------------------------------------------------------------------------------- +// LLTestSession +//---------------------------------------------------------------------------------------------- + +LLMetricPerformanceTesterWithSession::LLTestSession::~LLTestSession() +{ +} + diff --git a/indra/llcommon/llmetricperformancetester.h b/indra/llcommon/llmetricperformancetester.h new file mode 100644 index 0000000000..925010ac96 --- /dev/null +++ b/indra/llcommon/llmetricperformancetester.h @@ -0,0 +1,206 @@ +/** + * @file llmetricperformancetester.h + * @brief LLMetricPerformanceTesterBasic and LLMetricPerformanceTesterWithSession classes definition + * + * $LicenseInfo:firstyear=2004&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_METRICPERFORMANCETESTER_H +#define LL_METRICPERFORMANCETESTER_H + +const std::string DEFAULT_METRIC_NAME("metric"); + +/** + * @class LLMetricPerformanceTesterBasic + * @brief Performance Metric Base Class + */ +class LL_COMMON_API LLMetricPerformanceTesterBasic +{ +public: + /** + * @brief Creates a basic tester instance. + * @param[in] name - Unique string identifying this tester instance. + */ + LLMetricPerformanceTesterBasic(std::string name); + virtual ~LLMetricPerformanceTesterBasic(); + + /** + * @return Returns true if the instance has been added to the tester map. + * Need to be tested after creation of a tester instance so to know if the tester is correctly handled. + * A tester might not be added to the map if another tester with the same name already exists. + */ + BOOL isValid() const { return mValidInstance; } + + /** + * @brief Write a set of test results to the log LLSD. + */ + void outputTestResults() ; + + /** + * @brief Compare the test results. + * By default, compares the test results against the baseline one by one, item by item, + * in the increasing order of the LLSD record counter, starting from the first one. + */ + virtual void analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current) ; + + /** + * @return Returns the number of the test metrics in this tester instance. + */ + S32 getNumberOfMetrics() const { return mMetricStrings.size() ;} + /** + * @return Returns the metric name at index + * @param[in] index - Index on the list of metrics managed by this tester instance. + */ + std::string getMetricName(S32 index) const { return mMetricStrings[index] ;} + +protected: + /** + * @return Returns the name of this tester instance. + */ + std::string getTesterName() const { return mName ;} + + /** + * @brief Insert a new metric to be managed by this tester instance. + * @param[in] str - Unique string identifying the new metric. + */ + void addMetric(std::string str) ; + + /** + * @brief Compare test results, provided in 2 flavors: compare integers and compare floats. + * @param[out] os - Formatted output string holding the compared values. + * @param[in] metric_string - Name of the metric. + * @param[in] v_base - Base value of the metric. + * @param[in] v_current - Current value of the metric. + */ + virtual void compareTestResults(std::ofstream* os, std::string metric_string, S32 v_base, S32 v_current) ; + virtual void compareTestResults(std::ofstream* os, std::string metric_string, F32 v_base, F32 v_current) ; + + /** + * @brief Reset internal record count. Count starts with 1. + */ + void resetCurrentCount() { mCount = 1; } + /** + * @brief Increment internal record count. + */ + void incrementCurrentCount() { mCount++; } + /** + * @return Returns the label to be used for the current count. It's "TesterName"-"Count". + */ + std::string getCurrentLabelName() const { return llformat("%s-%d", mName.c_str(), mCount) ;} + + /** + * @brief Write a test record to the LLSD. Implementers need to overload this method. + * @param[out] sd - The LLSD record to store metric data into. + */ + virtual void outputTestRecord(LLSD* sd) = 0 ; + +private: + void preOutputTestResults(LLSD* sd) ; + void postOutputTestResults(LLSD* sd) ; + + std::string mName ; // Name of this tester instance + S32 mCount ; // Current record count + BOOL mValidInstance; // TRUE if the instance is managed by the map + std::vector< std::string > mMetricStrings ; // Metrics strings + +// Static members managing the collection of testers +public: + // Map of all the tester instances in use + typedef std::map< std::string, LLMetricPerformanceTesterBasic* > name_tester_map_t; + static name_tester_map_t sTesterMap ; + + /** + * @return Returns a pointer to the tester + * @param[in] name - Name of the tester instance queried. + */ + static LLMetricPerformanceTesterBasic* getTester(std::string name) ; + + /** + * @return Returns TRUE if that metric *or* the default catch all metric has been requested to be logged + * @param[in] name - Name of the tester queried. + */ + static BOOL isMetricLogRequested(std::string name); + + /** + * @return Returns TRUE if there's a tester defined, FALSE otherwise. + */ + static BOOL hasMetricPerformanceTesters() { return !sTesterMap.empty() ;} + /** + * @brief Delete all testers and reset the tester map + */ + static void cleanClass() ; + +private: + // Add a tester to the map. Returns false if adding fails. + static BOOL addTester(LLMetricPerformanceTesterBasic* tester) ; +}; + +/** + * @class LLMetricPerformanceTesterWithSession + * @brief Performance Metric Class with custom session + */ +class LL_COMMON_API LLMetricPerformanceTesterWithSession : public LLMetricPerformanceTesterBasic +{ +public: + /** + * @param[in] name - Unique string identifying this tester instance. + */ + LLMetricPerformanceTesterWithSession(std::string name); + virtual ~LLMetricPerformanceTesterWithSession(); + + /** + * @brief Compare the test results. + * This will be loading the base and current sessions and compare them using the virtual + * abstract methods loadTestSession() and compareTestSessions() + */ + virtual void analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current) ; + +protected: + /** + * @class LLMetricPerformanceTesterWithSession::LLTestSession + * @brief Defines an interface for the two abstract virtual functions loadTestSession() and compareTestSessions() + */ + class LL_COMMON_API LLTestSession + { + public: + virtual ~LLTestSession() ; + }; + + /** + * @brief Convert an LLSD log into a test session. + * @param[in] log - The LLSD record + * @return Returns the record as a test session + */ + virtual LLMetricPerformanceTesterWithSession::LLTestSession* loadTestSession(LLSD* log) = 0; + + /** + * @brief Compare the base session and the target session. Assumes base and current sessions have been loaded. + * @param[out] os - The comparison result as a standard stream + */ + virtual void compareTestSessions(std::ofstream* os) = 0; + + LLTestSession* mBaseSessionp; + LLTestSession* mCurrentSessionp; +}; + +#endif + diff --git a/indra/llcommon/llprocesslauncher.cpp b/indra/llcommon/llprocesslauncher.cpp index 99308c94e7..4b0f6b0251 100644 --- a/indra/llcommon/llprocesslauncher.cpp +++ b/indra/llcommon/llprocesslauncher.cpp @@ -58,6 +58,11 @@ void LLProcessLauncher::setWorkingDirectory(const std::string &dir) mWorkingDir = dir; } +const std::string& LLProcessLauncher::getExecutable() const +{ + return mExecutable; +} + void LLProcessLauncher::clearArguments() { mLaunchArguments.clear(); @@ -260,14 +265,7 @@ int LLProcessLauncher::launch(void) delete[] fake_argv; mProcessID = id; - - // At this point, the child process will have been created (since that's how vfork works -- the child borrowed our execution context until it forked) - // If the process doesn't exist at this point, the exec failed. - if(!isRunning()) - { - result = -1; - } - + return result; } diff --git a/indra/llcommon/llprocesslauncher.h b/indra/llcommon/llprocesslauncher.h index 479aeb664a..954c249147 100644 --- a/indra/llcommon/llprocesslauncher.h +++ b/indra/llcommon/llprocesslauncher.h @@ -47,6 +47,8 @@ public: void setExecutable(const std::string &executable); void setWorkingDirectory(const std::string &dir); + const std::string& getExecutable() const; + void clearArguments(); void addArgument(const std::string &arg); void addArgument(const char *arg); diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h index c75e0e2bbf..a53b22f6fc 100644 --- a/indra/llcommon/llqueuedthread.h +++ b/indra/llcommon/llqueuedthread.h @@ -179,7 +179,7 @@ public: void waitOnPending(); void printQueueStats(); - S32 getPending(); + virtual S32 getPending(); bool getThreaded() { return mThreaded ? true : false; } // Request accessors diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 00c94404d4..10cdc7087b 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -635,6 +635,26 @@ U32 LLMemoryInfo::getPhysicalMemoryClamped() const } } +//static +void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb) +{ +#if LL_WINDOWS + MEMORYSTATUSEX state; + state.dwLength = sizeof(state); + GlobalMemoryStatusEx(&state); + + avail_physical_mem_kb = (U32)(state.ullAvailPhys/1024) ; + avail_virtual_mem_kb = (U32)(state.ullAvailVirtual/1024) ; + +#else + //do not know how to collect available memory info for other systems. + //leave it blank here for now. + + avail_physical_mem_kb = -1 ; + avail_virtual_mem_kb = -1 ; +#endif +} + void LLMemoryInfo::stream(std::ostream& s) const { #if LL_WINDOWS diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 39af74e5c8..41a4f25000 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -114,6 +114,9 @@ public: ** be returned. */ U32 getPhysicalMemoryClamped() const; ///< Memory size in clamped bytes + + //get the available memory infomation in KiloBytes. + static void getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb); }; diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index d7b7c3699c..49d05ef411 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -63,9 +63,6 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap { LLThread *threadp = (LLThread *)datap; - // Set thread state to running - threadp->mStatus = RUNNING; - // Run the user supplied function threadp->run(); @@ -147,26 +144,45 @@ void LLThread::shutdown() { // This thread just wouldn't stop, even though we gave it time llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl; + // Put a stake in its heart. + apr_thread_exit(mAPRThreadp, -1); return; } mAPRThreadp = NULL; } delete mRunCondition; + mRunCondition = 0; - if (mIsLocalPool) + if (mIsLocalPool && mAPRPoolp) { apr_pool_destroy(mAPRPoolp); + mAPRPoolp = 0; } } void LLThread::start() { - apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp); + llassert(isStopped()); + + // Set thread state to running + mStatus = RUNNING; - // We won't bother joining - apr_thread_detach(mAPRThreadp); + apr_status_t status = + apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp); + + if(status == APR_SUCCESS) + { + // We won't bother joining + apr_thread_detach(mAPRThreadp); + } + else + { + mStatus = STOPPED; + llwarns << "failed to start thread " << mName << llendl; + ll_apr_warn_status(status); + } } //============================================================================ diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp new file mode 100644 index 0000000000..8a73e632a9 --- /dev/null +++ b/indra/llcommon/llthreadsafequeue.cpp @@ -0,0 +1,109 @@ +/** + * @file llthread.cpp + * + * $LicenseInfo:firstyear=2004&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 <apr_pools.h> +#include <apr_queue.h> +#include "llthreadsafequeue.h" + + + +// LLThreadSafeQueueImplementation +//----------------------------------------------------------------------------- + + +LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity): + mOwnsPool(pool == 0), + mPool(pool), + mQueue(0) +{ + if(mOwnsPool) { + apr_status_t status = apr_pool_create(&mPool, 0); + if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate pool"); + } else { + ; // No op. + } + + apr_status_t status = apr_queue_create(&mQueue, capacity, mPool); + if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue"); +} + + +LLThreadSafeQueueImplementation::~LLThreadSafeQueueImplementation() +{ + if(mQueue != 0) { + if(apr_queue_size(mQueue) != 0) llwarns << + "terminating queue which still contains " << apr_queue_size(mQueue) << + " elements;" << "memory will be leaked" << LL_ENDL; + apr_queue_term(mQueue); + } + if(mOwnsPool && (mPool != 0)) apr_pool_destroy(mPool); +} + + +void LLThreadSafeQueueImplementation::pushFront(void * element) +{ + apr_status_t status = apr_queue_push(mQueue, element); + + if(status == APR_EINTR) { + throw LLThreadSafeQueueInterrupt(); + } else if(status != APR_SUCCESS) { + throw LLThreadSafeQueueError("push failed"); + } else { + ; // Success. + } +} + + +bool LLThreadSafeQueueImplementation::tryPushFront(void * element){ + return apr_queue_trypush(mQueue, element) == APR_SUCCESS; +} + + +void * LLThreadSafeQueueImplementation::popBack(void) +{ + void * element; + apr_status_t status = apr_queue_pop(mQueue, &element); + + if(status == APR_EINTR) { + throw LLThreadSafeQueueInterrupt(); + } else if(status != APR_SUCCESS) { + throw LLThreadSafeQueueError("pop failed"); + } else { + return element; + } +} + + +bool LLThreadSafeQueueImplementation::tryPopBack(void *& element) +{ + return apr_queue_trypop(mQueue, &element) == APR_SUCCESS; +} + + +size_t LLThreadSafeQueueImplementation::size() +{ + return apr_queue_size(mQueue); +} diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h new file mode 100644 index 0000000000..58cac38769 --- /dev/null +++ b/indra/llcommon/llthreadsafequeue.h @@ -0,0 +1,205 @@ +/** + * @file llthreadsafequeue.h + * @brief Base classes for thread, mutex and condition handling. + * + * $LicenseInfo:firstyear=2004&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_LLTHREADSAFEQUEUE_H +#define LL_LLTHREADSAFEQUEUE_H + + +#include <string> +#include <stdexcept> + + +struct apr_pool_t; // From apr_pools.h +class LLThreadSafeQueueImplementation; // See below. + + +// +// A general queue exception. +// +class LL_COMMON_API LLThreadSafeQueueError: +public std::runtime_error +{ +public: + LLThreadSafeQueueError(std::string const & message): + std::runtime_error(message) + { + ; // No op. + } +}; + + +// +// An exception raised when blocking operations are interrupted. +// +class LL_COMMON_API LLThreadSafeQueueInterrupt: + public LLThreadSafeQueueError +{ +public: + LLThreadSafeQueueInterrupt(void): + LLThreadSafeQueueError("queue operation interrupted") + { + ; // No op. + } +}; + + +struct apr_queue_t; // From apr_queue.h + + +// +// Implementation details. +// +class LL_COMMON_API LLThreadSafeQueueImplementation +{ +public: + LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity); + ~LLThreadSafeQueueImplementation(); + void pushFront(void * element); + bool tryPushFront(void * element); + void * popBack(void); + bool tryPopBack(void *& element); + size_t size(); + +private: + bool mOwnsPool; + apr_pool_t * mPool; + apr_queue_t * mQueue; +}; + + +// +// Implements a thread safe FIFO. +// +template<typename ElementT> +class LLThreadSafeQueue +{ +public: + typedef ElementT value_type; + + // If the pool is set to NULL one will be allocated and managed by this + // queue. + LLThreadSafeQueue(apr_pool_t * pool = 0, unsigned int capacity = 1024); + + // Add an element to the front of queue (will block if the queue has + // reached capacity). + // + // This call will raise an interrupt error if the queue is deleted while + // the caller is blocked. + void pushFront(ElementT const & element); + + // Try to add an element to the front ofqueue without blocking. Returns + // true only if the element was actually added. + bool tryPushFront(ElementT const & element); + + // Pop the element at the end of the queue (will block if the queue is + // empty). + // + // This call will raise an interrupt error if the queue is deleted while + // the caller is blocked. + ElementT popBack(void); + + // Pop an element from the end of the queue if there is one available. + // Returns true only if an element was popped. + bool tryPopBack(ElementT & element); + + // Returns the size of the queue. + size_t size(); + +private: + LLThreadSafeQueueImplementation mImplementation; +}; + + + +// LLThreadSafeQueue +//----------------------------------------------------------------------------- + + +template<typename ElementT> +LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(apr_pool_t * pool, unsigned int capacity): + mImplementation(pool, capacity) +{ + ; // No op. +} + + +template<typename ElementT> +void LLThreadSafeQueue<ElementT>::pushFront(ElementT const & element) +{ + ElementT * elementCopy = new ElementT(element); + try { + mImplementation.pushFront(elementCopy); + } catch (LLThreadSafeQueueInterrupt) { + delete elementCopy; + throw; + } +} + + +template<typename ElementT> +bool LLThreadSafeQueue<ElementT>::tryPushFront(ElementT const & element) +{ + ElementT * elementCopy = new ElementT(element); + bool result = mImplementation.tryPushFront(elementCopy); + if(!result) delete elementCopy; + return result; +} + + +template<typename ElementT> +ElementT LLThreadSafeQueue<ElementT>::popBack(void) +{ + ElementT * element = reinterpret_cast<ElementT *> (mImplementation.popBack()); + ElementT result(*element); + delete element; + return result; +} + + +template<typename ElementT> +bool LLThreadSafeQueue<ElementT>::tryPopBack(ElementT & element) +{ + void * storedElement; + bool result = mImplementation.tryPopBack(storedElement); + if(result) { + ElementT * elementPtr = reinterpret_cast<ElementT *>(storedElement); + element = *elementPtr; + delete elementPtr; + } else { + ; // No op. + } + return result; +} + + +template<typename ElementT> +size_t LLThreadSafeQueue<ElementT>::size(void) +{ + return mImplementation.size(); +} + + +#endif diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index 12bddbfeed..356e0f4c0f 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -28,10 +28,14 @@ #define LL_LLVERSIONVIEWER_H const S32 LL_VERSION_MAJOR = 2; -const S32 LL_VERSION_MINOR = 3; +const S32 LL_VERSION_MINOR = 5; const S32 LL_VERSION_PATCH = 0; const S32 LL_VERSION_BUILD = 0; const char * const LL_CHANNEL = "Second Life Developer"; +#if LL_DARWIN +const char * const LL_VERSION_BUNDLE_ID = "com.secondlife.indra.viewer"; +#endif + #endif diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt index a69621a57b..ea8c1a1107 100644 --- a/indra/llimage/CMakeLists.txt +++ b/indra/llimage/CMakeLists.txt @@ -3,12 +3,13 @@ project(llimage) include(00-Common) -include(LLAddBuildTest) include(LLCommon) include(LLImage) include(LLMath) include(LLVFS) include(ZLIB) +include(LLAddBuildTest) +include(Tut) include_directories( ${LLCOMMON_INCLUDE_DIRS} @@ -57,11 +58,18 @@ add_library (llimage ${llimage_SOURCE_FILES}) # Sort by high-level to low-level target_link_libraries(llimage llcommon - llimagej2coj # *HACK: In theory a noop for KDU builds? ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ) # Add tests -#ADD_BUILD_TEST(llimageworker llimage) +if (LL_TESTS) + SET(llimage_TEST_SOURCE_FILES + llimageworker.cpp + ) + LL_ADD_PROJECT_UNIT_TESTS(llimage "${llimage_TEST_SOURCE_FILES}") +endif (LL_TESTS) + + + diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 5c33b675ca..b46a99e030 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -52,13 +52,11 @@ LLMutex* LLImage::sMutex = NULL; void LLImage::initClass() { sMutex = new LLMutex(NULL); - LLImageJ2C::openDSO(); } //static void LLImage::cleanupClass() { - LLImageJ2C::closeDSO(); delete sMutex; sMutex = NULL; } diff --git a/indra/llimage/llimagedimensionsinfo.cpp b/indra/llimage/llimagedimensionsinfo.cpp index 5ea4a236b5..835664c60f 100644 --- a/indra/llimage/llimagedimensionsinfo.cpp +++ b/indra/llimage/llimagedimensionsinfo.cpp @@ -30,6 +30,9 @@ #include "llimagedimensionsinfo.h" +// Value is true if one of Libjpeg's functions has encountered an error while working. +static bool sJpegErrorEncountered = false; + bool LLImageDimensionsInfo::load(const std::string& src_filename,U32 codec) { clean(); @@ -101,9 +104,17 @@ bool LLImageDimensionsInfo::getImageDimensionsPng() return true; } +// Called instead of exit() if Libjpeg encounters an error. +void on_jpeg_error(j_common_ptr cinfo) +{ + (void) cinfo; + sJpegErrorEncountered = true; + llwarns << "Libjpeg has encountered an error!" << llendl; +} bool LLImageDimensionsInfo::getImageDimensionsJpeg() { + sJpegErrorEncountered = false; clean(); FILE *fp = fopen (mSrcFilename.c_str(), "rb"); if (fp == NULL) @@ -115,6 +126,9 @@ bool LLImageDimensionsInfo::getImageDimensionsJpeg() jpeg_error_mgr jerr; jpeg_decompress_struct cinfo; cinfo.err = jpeg_std_error(&jerr); + // Call our function instead of exit() if Libjpeg encounters an error. + // This is done to avoid crash in this case (STORM-472). + cinfo.err->error_exit = on_jpeg_error; jpeg_create_decompress (&cinfo); jpeg_stdio_src (&cinfo, fp); @@ -128,6 +142,6 @@ bool LLImageDimensionsInfo::getImageDimensionsJpeg() jpeg_destroy_decompress(&cinfo); fclose(fp); - return true; + return !sJpegErrorEncountered; } diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index c8c866b7f2..cb2a85fa91 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -24,148 +24,32 @@ */ #include "linden_common.h" -#include "apr_pools.h" -#include "apr_dso.h" - #include "lldir.h" #include "llimagej2c.h" #include "llmemtype.h" +#include "lltimer.h" +#include "llmath.h" typedef LLImageJ2CImpl* (*CreateLLImageJ2CFunction)(); typedef void (*DestroyLLImageJ2CFunction)(LLImageJ2CImpl*); typedef const char* (*EngineInfoLLImageJ2CFunction)(); -//some "private static" variables so we only attempt to load -//dynamic libaries once -CreateLLImageJ2CFunction j2cimpl_create_func; -DestroyLLImageJ2CFunction j2cimpl_destroy_func; -EngineInfoLLImageJ2CFunction j2cimpl_engineinfo_func; -apr_pool_t *j2cimpl_dso_memory_pool; -apr_dso_handle_t *j2cimpl_dso_handle; - -//Declare the prototype for theses functions here, their functionality -//will be implemented in other files which define a derived LLImageJ2CImpl -//but only ONE static library which has the implementation for this -//function should ever be included +// Declare the prototype for theses functions here. Their functionality +// will be implemented in other files which define a derived LLImageJ2CImpl +// but only ONE static library which has the implementation for these +// functions should ever be included. LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl(); void fallbackDestroyLLImageJ2CImpl(LLImageJ2CImpl* impl); const char* fallbackEngineInfoLLImageJ2CImpl(); -//static -//Loads the required "create", "destroy" and "engineinfo" functions needed -void LLImageJ2C::openDSO() -{ - //attempt to load a DSO and get some functions from it - std::string dso_name; - std::string dso_path; - - bool all_functions_loaded = false; - apr_status_t rv; - -#if LL_WINDOWS - dso_name = "llkdu.dll"; -#elif LL_DARWIN - dso_name = "libllkdu.dylib"; -#else - dso_name = "libllkdu.so"; -#endif - - dso_path = gDirUtilp->findFile(dso_name, - gDirUtilp->getAppRODataDir(), - gDirUtilp->getExecutableDir()); - - j2cimpl_dso_handle = NULL; - j2cimpl_dso_memory_pool = NULL; - - //attempt to load the shared library - apr_pool_create(&j2cimpl_dso_memory_pool, NULL); - rv = apr_dso_load(&j2cimpl_dso_handle, - dso_path.c_str(), - j2cimpl_dso_memory_pool); - - //now, check for success - if ( rv == APR_SUCCESS ) - { - //found the dynamic library - //now we want to load the functions we're interested in - CreateLLImageJ2CFunction create_func = NULL; - DestroyLLImageJ2CFunction dest_func = NULL; - EngineInfoLLImageJ2CFunction engineinfo_func = NULL; - - rv = apr_dso_sym((apr_dso_handle_sym_t*)&create_func, - j2cimpl_dso_handle, - "createLLImageJ2CKDU"); - if ( rv == APR_SUCCESS ) - { - //we've loaded the create function ok - //we need to delete via the DSO too - //so lets check for a destruction function - rv = apr_dso_sym((apr_dso_handle_sym_t*)&dest_func, - j2cimpl_dso_handle, - "destroyLLImageJ2CKDU"); - if ( rv == APR_SUCCESS ) - { - //we've loaded the destroy function ok - rv = apr_dso_sym((apr_dso_handle_sym_t*)&engineinfo_func, - j2cimpl_dso_handle, - "engineInfoLLImageJ2CKDU"); - if ( rv == APR_SUCCESS ) - { - //ok, everything is loaded alright - j2cimpl_create_func = create_func; - j2cimpl_destroy_func = dest_func; - j2cimpl_engineinfo_func = engineinfo_func; - all_functions_loaded = true; - } - } - } - } - - if ( !all_functions_loaded ) - { - //something went wrong with the DSO or function loading.. - //fall back onto our satefy impl creation function - -#if 0 - // precious verbose debugging, sadly we can't use our - // 'llinfos' stream etc. this early in the initialisation seq. - char errbuf[256]; - fprintf(stderr, "failed to load syms from DSO %s (%s)\n", - dso_name.c_str(), dso_path.c_str()); - apr_strerror(rv, errbuf, sizeof(errbuf)); - fprintf(stderr, "error: %d, %s\n", rv, errbuf); - apr_dso_error(j2cimpl_dso_handle, errbuf, sizeof(errbuf)); - fprintf(stderr, "dso-error: %d, %s\n", rv, errbuf); -#endif - - if ( j2cimpl_dso_handle ) - { - apr_dso_unload(j2cimpl_dso_handle); - j2cimpl_dso_handle = NULL; - } - - if ( j2cimpl_dso_memory_pool ) - { - apr_pool_destroy(j2cimpl_dso_memory_pool); - j2cimpl_dso_memory_pool = NULL; - } - } -} - -//static -void LLImageJ2C::closeDSO() -{ - if ( j2cimpl_dso_handle ) apr_dso_unload(j2cimpl_dso_handle); - if (j2cimpl_dso_memory_pool) apr_pool_destroy(j2cimpl_dso_memory_pool); -} +// Test data gathering handle +LLImageCompressionTester* LLImageJ2C::sTesterp = NULL ; +const std::string sTesterName("ImageCompressionTester"); //static std::string LLImageJ2C::getEngineInfo() { - if (!j2cimpl_engineinfo_func) - j2cimpl_engineinfo_func = fallbackEngineInfoLLImageJ2CImpl; - - return j2cimpl_engineinfo_func(); + return fallbackEngineInfoLLImageJ2CImpl(); } LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), @@ -175,47 +59,32 @@ LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), mReversible(FALSE), mAreaUsedForDataSizeCalcs(0) { - //We assume here that if we wanted to create via - //a dynamic library that the approriate open calls were made - //before any calls to this constructor. - - //Therefore, a NULL creation function pointer here means - //we either did not want to create using functions from the dynamic - //library or there were issues loading it, either way - //use our fall back - if ( !j2cimpl_create_func ) - { - j2cimpl_create_func = fallbackCreateLLImageJ2CImpl; - } - - mImpl = j2cimpl_create_func(); + mImpl = fallbackCreateLLImageJ2CImpl(); // Clear data size table for( S32 i = 0; i <= MAX_DISCARD_LEVEL; i++) { // Array size is MAX_DISCARD_LEVEL+1 mDataSizes[i] = 0; } + + // If that test log has ben requested but not yet created, create it + if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName)) + { + sTesterp = new LLImageCompressionTester() ; + if (!sTesterp->isValid()) + { + delete sTesterp; + sTesterp = NULL; + } + } } // virtual LLImageJ2C::~LLImageJ2C() { - //We assume here that if we wanted to destroy via - //a dynamic library that the approriate open calls were made - //before any calls to this destructor. - - //Therefore, a NULL creation function pointer here means - //we either did not want to destroy using functions from the dynamic - //library or there were issues loading it, either way - //use our fall back - if ( !j2cimpl_destroy_func ) - { - j2cimpl_destroy_func = fallbackDestroyLLImageJ2CImpl; - } - if ( mImpl ) { - j2cimpl_destroy_func(mImpl); + fallbackDestroyLLImageJ2CImpl(mImpl); } } @@ -280,6 +149,7 @@ BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time) // Returns TRUE to mean done, whether successful or not. BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count ) { + LLTimer elapsed; LLMemType mt1(mMemType); BOOL res = TRUE; @@ -318,6 +188,21 @@ BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 fir LLImage::setLastError(mLastError); } + LLImageCompressionTester* tester = (LLImageCompressionTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) + { + // Decompression stat gathering + // Note that we *do not* take into account the decompression failures data so we might overestimate the time spent processing + + // Always add the decompression time to the stat + tester->updateDecompressionStats(elapsed.getElapsedTimeF32()) ; + if (res) + { + // The whole data stream is finally decompressed when res is returned as TRUE + tester->updateDecompressionStats(this->getDataSize(), raw_imagep->getDataSize()) ; + } + } + return res; } @@ -330,6 +215,7 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 encode_time) BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time) { + LLTimer elapsed; LLMemType mt1(mMemType); resetLastError(); BOOL res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible); @@ -337,6 +223,22 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, { LLImage::setLastError(mLastError); } + + LLImageCompressionTester* tester = (LLImageCompressionTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) + { + // Compression stat gathering + // Note that we *do not* take into account the compression failures cases so we night overestimate the time spent processing + + // Always add the compression time to the stat + tester->updateCompressionStats(elapsed.getElapsedTimeF32()) ; + if (res) + { + // The whole data stream is finally compressed when res is returned as TRUE + tester->updateCompressionStats(this->getDataSize(), raw_imagep->getDataSize()) ; + } + } + return res; } @@ -540,3 +442,125 @@ void LLImageJ2C::updateRawDiscardLevel() LLImageJ2CImpl::~LLImageJ2CImpl() { } + +//---------------------------------------------------------------------------------------------- +// Start of LLImageCompressionTester +//---------------------------------------------------------------------------------------------- +LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTesterBasic(sTesterName) +{ + addMetric("Time Decompression (s)"); + addMetric("Volume In Decompression (kB)"); + addMetric("Volume Out Decompression (kB)"); + addMetric("Decompression Ratio (x:1)"); + addMetric("Perf Decompression (kB/s)"); + + addMetric("Time Compression (s)"); + addMetric("Volume In Compression (kB)"); + addMetric("Volume Out Compression (kB)"); + addMetric("Compression Ratio (x:1)"); + addMetric("Perf Compression (kB/s)"); + + mRunBytesInDecompression = 0; + mRunBytesInCompression = 0; + + mTotalBytesInDecompression = 0; + mTotalBytesOutDecompression = 0; + mTotalBytesInCompression = 0; + mTotalBytesOutCompression = 0; + + mTotalTimeDecompression = 0.0f; + mTotalTimeCompression = 0.0f; +} + +LLImageCompressionTester::~LLImageCompressionTester() +{ + LLImageJ2C::sTesterp = NULL; +} + +//virtual +void LLImageCompressionTester::outputTestRecord(LLSD *sd) +{ + std::string currentLabel = getCurrentLabelName(); + + F32 decompressionPerf = 0.0f; + F32 compressionPerf = 0.0f; + F32 decompressionRate = 0.0f; + F32 compressionRate = 0.0f; + + F32 totalkBInDecompression = (F32)(mTotalBytesInDecompression) / 1000.0; + F32 totalkBOutDecompression = (F32)(mTotalBytesOutDecompression) / 1000.0; + F32 totalkBInCompression = (F32)(mTotalBytesInCompression) / 1000.0; + F32 totalkBOutCompression = (F32)(mTotalBytesOutCompression) / 1000.0; + + if (!is_approx_zero(mTotalTimeDecompression)) + { + decompressionPerf = totalkBInDecompression / mTotalTimeDecompression; + } + if (!is_approx_zero(totalkBInDecompression)) + { + decompressionRate = totalkBOutDecompression / totalkBInDecompression; + } + if (!is_approx_zero(mTotalTimeCompression)) + { + compressionPerf = totalkBInCompression / mTotalTimeCompression; + } + if (!is_approx_zero(totalkBOutCompression)) + { + compressionRate = totalkBInCompression / totalkBOutCompression; + } + + (*sd)[currentLabel]["Time Decompression (s)"] = (LLSD::Real)mTotalTimeDecompression; + (*sd)[currentLabel]["Volume In Decompression (kB)"] = (LLSD::Real)totalkBInDecompression; + (*sd)[currentLabel]["Volume Out Decompression (kB)"]= (LLSD::Real)totalkBOutDecompression; + (*sd)[currentLabel]["Decompression Ratio (x:1)"] = (LLSD::Real)decompressionRate; + (*sd)[currentLabel]["Perf Decompression (kB/s)"] = (LLSD::Real)decompressionPerf; + + (*sd)[currentLabel]["Time Compression (s)"] = (LLSD::Real)mTotalTimeCompression; + (*sd)[currentLabel]["Volume In Compression (kB)"] = (LLSD::Real)totalkBInCompression; + (*sd)[currentLabel]["Volume Out Compression (kB)"] = (LLSD::Real)totalkBOutCompression; + (*sd)[currentLabel]["Compression Ratio (x:1)"] = (LLSD::Real)compressionRate; + (*sd)[currentLabel]["Perf Compression (kB/s)"] = (LLSD::Real)compressionPerf; +} + +void LLImageCompressionTester::updateCompressionStats(const F32 deltaTime) +{ + mTotalTimeCompression += deltaTime; +} + +void LLImageCompressionTester::updateCompressionStats(const S32 bytesCompress, const S32 bytesRaw) +{ + mTotalBytesInCompression += bytesRaw; + mRunBytesInCompression += bytesRaw; + mTotalBytesOutCompression += bytesCompress; + if (mRunBytesInCompression > (1000000)) + { + // Output everything + outputTestResults(); + // Reset the compression data of the run + mRunBytesInCompression = 0; + } +} + +void LLImageCompressionTester::updateDecompressionStats(const F32 deltaTime) +{ + mTotalTimeDecompression += deltaTime; +} + +void LLImageCompressionTester::updateDecompressionStats(const S32 bytesIn, const S32 bytesOut) +{ + mTotalBytesInDecompression += bytesIn; + mRunBytesInDecompression += bytesIn; + mTotalBytesOutDecompression += bytesOut; + if (mRunBytesInDecompression > (1000000)) + { + // Output everything + outputTestResults(); + // Reset the decompression data of the run + mRunBytesInDecompression = 0; + } +} + +//---------------------------------------------------------------------------------------------- +// End of LLTexturePipelineTester +//---------------------------------------------------------------------------------------------- + diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h index cdb3faa207..dd5bec8b2e 100644 --- a/indra/llimage/llimagej2c.h +++ b/indra/llimage/llimagej2c.h @@ -29,8 +29,11 @@ #include "llimage.h" #include "llassettype.h" +#include "llmetricperformancetester.h" class LLImageJ2CImpl; +class LLImageCompressionTester ; + class LLImageJ2C : public LLImageFormatted { protected: @@ -69,14 +72,13 @@ public: static S32 calcHeaderSizeJ2C(); static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = 0.f); - static void openDSO(); - static void closeDSO(); static std::string getEngineInfo(); - + protected: friend class LLImageJ2CImpl; friend class LLImageJ2COJ; friend class LLImageJ2CKDU; + friend class LLImageCompressionTester; void decodeFailed(); void updateRawDiscardLevel(); @@ -90,6 +92,9 @@ protected: BOOL mReversible; LLImageJ2CImpl *mImpl; std::string mLastError; + + // Image compression/decompression tester + static LLImageCompressionTester* sTesterp; }; // Derive from this class to implement JPEG2000 decoding @@ -118,4 +123,40 @@ protected: #define LINDEN_J2C_COMMENT_PREFIX "LL_" +// +// This class is used for performance data gathering only. +// Tracks the image compression / decompression data, +// records and outputs them to the log file. +// +class LLImageCompressionTester : public LLMetricPerformanceTesterBasic +{ + public: + LLImageCompressionTester(); + ~LLImageCompressionTester(); + + void updateDecompressionStats(const F32 deltaTime) ; + void updateDecompressionStats(const S32 bytesIn, const S32 bytesOut) ; + void updateCompressionStats(const F32 deltaTime) ; + void updateCompressionStats(const S32 bytesIn, const S32 bytesOut) ; + + protected: + /*virtual*/ void outputTestRecord(LLSD* sd); + + private: + // + // Data size + // + U32 mTotalBytesInDecompression; // Total bytes fed to decompressor + U32 mTotalBytesOutDecompression; // Total bytes produced by decompressor + U32 mTotalBytesInCompression; // Total bytes fed to compressor + U32 mTotalBytesOutCompression; // Total bytes produced by compressor + U32 mRunBytesInDecompression; // Bytes fed to decompressor in this run + U32 mRunBytesInCompression; // Bytes fed to compressor in this run + // + // Time + // + F32 mTotalTimeDecompression; // Total time spent in computing decompression + F32 mTotalTimeCompression; // Total time spent in computing compression + }; + #endif diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index a109276709..08476fb72c 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -26,10 +26,8 @@ */ // Precompiled header: almost always required for newview cpp files -#include <list> -#include <map> -#include <algorithm> -// Class to test +#include "linden_common.h" +// Class to test #include "../llimageworker.h" // For timer class #include "../llcommon/lltimer.h" @@ -44,7 +42,17 @@ // * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) // * A simulator for a class can be implemented here. Please comment and document thoroughly. -LLImageBase::LLImageBase() {} +LLImageBase::LLImageBase() +: mData(NULL), +mDataSize(0), +mWidth(0), +mHeight(0), +mComponents(0), +mBadBufferAllocation(false), +mAllowOverSize(false), +mMemType(LLMemType::MTYPE_IMAGEBASE) +{ +} LLImageBase::~LLImageBase() {} void LLImageBase::dump() { } void LLImageBase::sanityCheck() { } diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 95e0997d5b..13b12c0928 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -90,6 +90,12 @@ void info_callback(const char* msg, void*) lldebugs << "LLImageJ2COJ: " << chomp(msg) << llendl; } +// Divide a by 2 to the power of b and round upwards +int ceildivpow2(int a, int b) +{ + return (a + (1 << b) - 1) >> b; +} + LLImageJ2COJ::LLImageJ2COJ() : LLImageJ2CImpl() diff --git a/indra/llimagej2coj/llimagej2coj.h b/indra/llimagej2coj/llimagej2coj.h index 7edacbe97c..9476665ccb 100644 --- a/indra/llimagej2coj/llimagej2coj.h +++ b/indra/llimagej2coj/llimagej2coj.h @@ -34,17 +34,11 @@ class LLImageJ2COJ : public LLImageJ2CImpl public: LLImageJ2COJ(); virtual ~LLImageJ2COJ(); - protected: /*virtual*/ BOOL getMetadata(LLImageJ2C &base); /*virtual*/ BOOL decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count); /*virtual*/ BOOL encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0, BOOL reversible = FALSE); - int ceildivpow2(int a, int b) - { - // Divide a by b to the power of 2 and round upwards. - return (a + (1 << b) - 1) >> b; - } }; #endif diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index bda76eac80..a3caf79519 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -61,8 +61,6 @@ static const std::string INV_FOLDER_ID_LABEL_WS("category_id"); ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- -const U8 TASK_INVENTORY_ITEM_KEY = 0; -const U8 TASK_INVENTORY_ASSET_KEY = 1; const LLUUID MAGIC_ID("3c115e51-04f4-523c-9fa6-98aff1034730"); diff --git a/indra/llinventory/llnotecard.cpp b/indra/llinventory/llnotecard.cpp index 62829c284f..69152cefe0 100644 --- a/indra/llinventory/llnotecard.cpp +++ b/indra/llinventory/llnotecard.cpp @@ -199,7 +199,7 @@ bool LLNotecard::importStream(std::istream& str) return FALSE; } - if(text_len > mMaxText) + if(text_len > mMaxText || text_len < 0) { llwarns << "Invalid Linden text length: " << text_len << llendl; return FALSE; diff --git a/indra/llkdu/CMakeLists.txt b/indra/llkdu/CMakeLists.txt new file mode 100644 index 0000000000..7ed1c6c694 --- /dev/null +++ b/indra/llkdu/CMakeLists.txt @@ -0,0 +1,55 @@ +# -*- cmake -*- + +project(llkdu) + +# Visual Studio 2005 has a dumb bug that causes it to fail compilation +# of KDU if building with both optimisation and /WS (treat warnings as +# errors), even when the specific warnings that make it croak are +# disabled. + +#set(VS_DISABLE_FATAL_WARNINGS ON) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLKDU) +include(LLMath) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${KDU_INCLUDE_DIR} + ${LLMATH_INCLUDE_DIRS} + ) + +set(llkdu_SOURCE_FILES + llimagej2ckdu.cpp + llkdumem.cpp + ) + +set(llkdu_HEADER_FILES + CMakeLists.txt + + llimagej2ckdu.h + llkdumem.h + ) + +set_source_files_properties(${llkdu_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llkdu_SOURCE_FILES ${llkdu_HEADER_FILES}) + +if (USE_KDU) + add_library (${LLKDU_LIBRARIES} ${llkdu_SOURCE_FILES}) + + # Add tests + if (LL_TESTS) + include(LLAddBuildTest) + include(Tut) + SET(llkdu_TEST_SOURCE_FILES + llimagej2ckdu.cpp + ) + LL_ADD_PROJECT_UNIT_TESTS(llkdu "${llkdu_TEST_SOURCE_FILES}") + endif (LL_TESTS) + +endif (USE_KDU) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp new file mode 100644 index 0000000000..10ea5685e8 --- /dev/null +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -0,0 +1,1085 @@ + /** + * @file llimagej2ckdu.cpp + * @brief This is an implementation of JPEG2000 encode/decode using Kakadu + * + * $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 "llimagej2ckdu.h" + +#include "lltimer.h" +#include "llpointer.h" +#include "llkdumem.h" + + +class kdc_flow_control { + +public: // Member functions + kdc_flow_control(kdu_image_in_base *img_in, kdu_codestream codestream); + ~kdc_flow_control(); + bool advance_components(); + void process_components(); + +private: // Data + + struct kdc_component_flow_control { + public: // Data + kdu_image_in_base *reader; + int vert_subsampling; + int ratio_counter; /* Initialized to 0, decremented by `count_delta'; + when < 0, a new line must be processed, after + which it is incremented by `vert_subsampling'. */ + int initial_lines; + int remaining_lines; + kdu_line_buf *line; + }; + + kdu_codestream codestream; + kdu_dims valid_tile_indices; + kdu_coords tile_idx; + kdu_tile tile; + int num_components; + kdc_component_flow_control *components; + int count_delta; // Holds the minimum of the `vert_subsampling' fields + kdu_multi_analysis engine; + kdu_long max_buffer_memory; +}; + +// +// Kakadu specific implementation +// +void set_default_colour_weights(kdu_params *siz); + +const char* engineInfoLLImageJ2CKDU() +{ + return "KDU v6.4.1"; +} + +LLImageJ2CKDU* createLLImageJ2CKDU() +{ + return new LLImageJ2CKDU(); +} + +void destroyLLImageJ2CKDU(LLImageJ2CKDU* kdu) +{ + delete kdu; + kdu = NULL; +} + +LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl() +{ + return new LLImageJ2CKDU(); +} + +void fallbackDestroyLLImageJ2CImpl(LLImageJ2CImpl* impl) +{ + delete impl; + impl = NULL; +} + +const char* fallbackEngineInfoLLImageJ2CImpl() +{ + return engineInfoLLImageJ2CKDU(); +} + +class LLKDUDecodeState +{ +public: + + S32 mNumComponents; + BOOL mUseYCC; + kdu_dims mDims; + kdu_sample_allocator mAllocator; + kdu_tile_comp mComps[4]; + kdu_line_buf mLines[4]; + kdu_pull_ifc mEngines[4]; + bool mReversible[4]; // Some components may be reversible and others not. + int mBitDepths[4]; // Original bit-depth may be quite different from 8. + + kdu_tile mTile; + kdu_byte *mBuf; + S32 mRowGap; + + LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap); + ~LLKDUDecodeState(); + BOOL processTileDecode(F32 decode_time, BOOL limit_time = TRUE); + +public: + int *AssignLayerBytes(siz_params *siz, int &num_specs); + + void setupCodeStream(BOOL keep_codestream, LLImageJ2CKDU::ECodeStreamMode mode); + BOOL initDecode(LLImageRaw &raw_image, F32 decode_time, LLImageJ2CKDU::ECodeStreamMode mode, S32 first_channel, S32 max_channel_count ); +}; + +void ll_kdu_error( void ) +{ + // *FIX: This exception is bad, bad, bad. It gets thrown from a + // destructor which can lead to immediate program termination! + throw "ll_kdu_error() throwing an exception"; +} + +// Stuff for new kdu error handling +class LLKDUMessageWarning : public kdu_message +{ +public: + /*virtual*/ void put_text(const char *s); + /*virtual*/ void put_text(const kdu_uint16 *s); + + static LLKDUMessageWarning sDefaultMessage; +}; + +class LLKDUMessageError : public kdu_message +{ +public: + /*virtual*/ void put_text(const char *s); + /*virtual*/ void put_text(const kdu_uint16 *s); + /*virtual*/ void flush(bool end_of_message=false); + static LLKDUMessageError sDefaultMessage; +}; + +void LLKDUMessageWarning::put_text(const char *s) +{ + llinfos << "KDU Warning: " << s << llendl; +} + +void LLKDUMessageWarning::put_text(const kdu_uint16 *s) +{ + llinfos << "KDU Warning: " << s << llendl; +} + +void LLKDUMessageError::put_text(const char *s) +{ + llinfos << "KDU Error: " << s << llendl; +} + +void LLKDUMessageError::put_text(const kdu_uint16 *s) +{ + llinfos << "KDU Error: " << s << llendl; +} + +void LLKDUMessageError::flush(bool end_of_message) +{ + if( end_of_message ) + { + throw "KDU throwing an exception"; + } +} + +LLKDUMessageWarning LLKDUMessageWarning::sDefaultMessage; +LLKDUMessageError LLKDUMessageError::sDefaultMessage; +static bool kdu_message_initialized = false; + +LLImageJ2CKDU::LLImageJ2CKDU() : LLImageJ2CImpl(), +mInputp(NULL), +mCodeStreamp(NULL), +mTPosp(NULL), +mTileIndicesp(NULL), +mRawImagep(NULL), +mDecodeState(NULL) +{ +} + +LLImageJ2CKDU::~LLImageJ2CKDU() +{ + cleanupCodeStream(); // in case destroyed before decode completed +} + +// Stuff for new simple decode +void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision); + +void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECodeStreamMode mode) +{ + S32 data_size = base.getDataSize(); + S32 max_bytes = base.getMaxBytes() ? base.getMaxBytes() : data_size; + + // + // Initialization + // + if (!kdu_message_initialized) + { + kdu_message_initialized = true; + kdu_customize_errors(&LLKDUMessageError::sDefaultMessage); + kdu_customize_warnings(&LLKDUMessageWarning::sDefaultMessage); + } + + if (mCodeStreamp) + { + mCodeStreamp->destroy(); + delete mCodeStreamp; + mCodeStreamp = NULL; + } + + if (!mInputp && base.getData()) + { + // The compressed data has been loaded + // Setup the source for the codestream + mInputp = new LLKDUMemSource(base.getData(), data_size); + } + + if (mInputp) + { + mInputp->reset(); + } + mCodeStreamp = new kdu_codestream; + + mCodeStreamp->create(mInputp); + + // Set the maximum number of bytes to use from the codestream + mCodeStreamp->set_max_bytes(max_bytes); + + // If you want to flip or rotate the image for some reason, change + // the resolution, or identify a restricted region of interest, this is + // the place to do it. You may use "kdu_codestream::change_appearance" + // and "kdu_codestream::apply_input_restrictions" for this purpose. + // If you wish to truncate the code-stream prior to decompression, you + // may use "kdu_codestream::set_max_bytes". + // If you wish to retain all compressed data so that the material + // can be decompressed multiple times, possibly with different appearance + // parameters, you should call "kdu_codestream::set_persistent" here. + // There are a variety of other features which must be enabled at + // this point if you want to take advantage of them. See the + // descriptions appearing with the "kdu_codestream" interface functions + // in "kdu_compressed.h" for an itemized account of these capabilities. + + switch( mode ) + { + case MODE_FAST: + mCodeStreamp->set_fast(); + break; + case MODE_RESILIENT: + mCodeStreamp->set_resilient(); + break; + case MODE_FUSSY: + mCodeStreamp->set_fussy(); + break; + default: + llassert(0); + mCodeStreamp->set_fast(); + } + + kdu_dims dims; + mCodeStreamp->get_dims(0,dims); + + S32 components = mCodeStreamp->get_num_components(); + + if (components >= 3) + { // Check that components have consistent dimensions (for PPM file) + kdu_dims dims1; mCodeStreamp->get_dims(1,dims1); + kdu_dims dims2; mCodeStreamp->get_dims(2,dims2); + if ((dims1 != dims) || (dims2 != dims)) + { + llerrs << "Components don't have matching dimensions!" << llendl; + } + } + + base.setSize(dims.size.x, dims.size.y, components); + + if (!keep_codestream) + { + mCodeStreamp->destroy(); + delete mCodeStreamp; + mCodeStreamp = NULL; + delete mInputp; + mInputp = NULL; + } +} + +void LLImageJ2CKDU::cleanupCodeStream() +{ + delete mInputp; + mInputp = NULL; + + delete mDecodeState; + mDecodeState = NULL; + + if (mCodeStreamp) + { + mCodeStreamp->destroy(); + delete mCodeStreamp; + mCodeStreamp = NULL; + } + + delete mTPosp; + mTPosp = NULL; + + delete mTileIndicesp; + mTileIndicesp = NULL; +} + +BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count ) +{ + base.resetLastError(); + + // *FIX: kdu calls our callback function if there's an error, and then bombs. + // To regain control, we throw an exception, and catch it here. + try + { + base.updateRawDiscardLevel(); + setupCodeStream(base, TRUE, mode); + + mRawImagep = &raw_image; + mCodeStreamp->change_appearance(false, true, false); + mCodeStreamp->apply_input_restrictions(first_channel,max_channel_count,base.getRawDiscardLevel(),0,NULL); + + kdu_dims dims; mCodeStreamp->get_dims(0,dims); + S32 channels = base.getComponents() - first_channel; + if( channels > max_channel_count ) + { + channels = max_channel_count; + } + raw_image.resize(dims.size.x, dims.size.y, channels); + + // llinfos << "Resizing to " << dims.size.x << ":" << dims.size.y << llendl; + if (!mTileIndicesp) + { + mTileIndicesp = new kdu_dims; + } + mCodeStreamp->get_valid_tiles(*mTileIndicesp); + if (!mTPosp) + { + mTPosp = new kdu_coords; + mTPosp->y = 0; + mTPosp->x = 0; + } + } + catch (const char* msg) + { + base.setLastError(ll_safe_string(msg)); + return FALSE; + } + catch (...) + { + base.setLastError("Unknown J2C error"); + return FALSE; + } + + return TRUE; +} + + +// Returns TRUE to mean done, whether successful or not. +BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) +{ + ECodeStreamMode mode = MODE_FAST; + + LLTimer decode_timer; + + if (!mCodeStreamp) + { + if (!initDecode(base, raw_image, decode_time, mode, first_channel, max_channel_count)) + { + // Initializing the J2C decode failed, bail out. + cleanupCodeStream(); + return TRUE; // done + } + } + + // These can probably be grabbed from what's saved in the class. + kdu_dims dims; + mCodeStreamp->get_dims(0,dims); + + // Now we are ready to walk through the tiles processing them one-by-one. + kdu_byte *buffer = raw_image.getData(); + + while (mTPosp->y < mTileIndicesp->size.y) + { + while (mTPosp->x < mTileIndicesp->size.x) + { + try + { + if (!mDecodeState) + { + kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos); + + // Find the region of the buffer occupied by this + // tile. Note that we have no control over + // sub-sampling factors which might have been used + // during compression and so it can happen that tiles + // (at the image component level) actually have + // different dimensions. For this reason, we cannot + // figure out the buffer region occupied by a tile + // directly from the tile indices. Instead, we query + // the highest resolution of the first tile-component + // concerning its location and size on the canvas -- + // the `dims' object already holds the location and + // size of the entire image component on the same + // canvas coordinate system. Comparing the two tells + // us where the current tile is in the buffer. + S32 channels = base.getComponents() - first_channel; + if( channels > max_channel_count ) + { + channels = max_channel_count; + } + kdu_resolution res = tile.access_component(0).access_resolution(); + kdu_dims tile_dims; res.get_dims(tile_dims); + kdu_coords offset = tile_dims.pos - dims.pos; + int row_gap = channels*dims.size.x; // inter-row separation + kdu_byte *buf = buffer + offset.y*row_gap + offset.x*channels; + mDecodeState = new LLKDUDecodeState(tile, buf, row_gap); + } + // Do the actual processing + F32 remaining_time = decode_time - decode_timer.getElapsedTimeF32(); + // This is where we do the actual decode. If we run out of time, return false. + if (mDecodeState->processTileDecode(remaining_time, (decode_time > 0.0f))) + { + delete mDecodeState; + mDecodeState = NULL; + } + else + { + // Not finished decoding yet. + // setLastError("Ran out of time while decoding"); + return FALSE; + } + } + catch( const char* msg ) + { + base.setLastError(ll_safe_string(msg)); + base.decodeFailed(); + cleanupCodeStream(); + return TRUE; // done + } + catch( ... ) + { + base.setLastError( "Unknown J2C error" ); + base.decodeFailed(); + cleanupCodeStream(); + return TRUE; // done + } + + + mTPosp->x++; + } + mTPosp->y++; + mTPosp->x = 0; + } + + cleanupCodeStream(); + + return TRUE; +} + + +BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, BOOL reversible) +{ + // Collect simple arguments. + bool transpose, vflip, hflip; + bool allow_rate_prediction, mem, quiet, no_weights; + int cpu_iterations; + std::ostream *record_stream; + + transpose = false; + record_stream = NULL; + allow_rate_prediction = true; + no_weights = false; + cpu_iterations = -1; + mem = false; + quiet = false; + vflip = true; + hflip = false; + + try + { + // Set up input image files. + siz_params siz; + + // Should set rate someplace here. + LLKDUMemIn mem_in(raw_image.getData(), + raw_image.getDataSize(), + raw_image.getWidth(), + raw_image.getHeight(), + raw_image.getComponents(), + &siz); + + base.setSize(raw_image.getWidth(), raw_image.getHeight(), raw_image.getComponents()); + + int num_components = raw_image.getComponents(); + + siz.set(Scomponents,0,0,num_components); + siz.set(Sdims,0,0,base.getHeight()); // Height of first image component + siz.set(Sdims,0,1,base.getWidth()); // Width of first image component + siz.set(Sprecision,0,0,8); // Image samples have original bit-depth of 8 + siz.set(Ssigned,0,0,false); // Image samples are originally unsigned + + kdu_params *siz_ref = &siz; siz_ref->finalize(); + siz_params transformed_siz; // Use this one to construct code-strea + transformed_siz.copy_from(&siz,-1,-1,-1,0,transpose,false,false); + + // Construct the `kdu_codestream' object and parse all remaining arguments. + + U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents(); + if (max_output_size < 1000) + { + max_output_size = 1000; + } + U8 *output_buffer = new U8[max_output_size]; + + U32 output_size = max_output_size; // gets modified + LLKDUMemTarget output(output_buffer, output_size, base.getWidth()*base.getHeight()*base.getComponents()); + if (output_size > max_output_size) + { + llerrs << llformat("LLImageJ2C::encode output_size(%d) > max_output_size(%d)", + output_size,max_output_size) << llendl; + } + + kdu_codestream codestream; + codestream.create(&transformed_siz,&output); + + if (comment_text) + { + // Set the comments for the codestream + kdu_codestream_comment comment = codestream.add_comment(); + comment.put_text(comment_text); + } + + // Set codestream options + int num_layer_specs = 0; + + kdu_long layer_bytes[64]; + U32 max_bytes = 0; + + if ((num_components >= 3) && !no_weights) + { + set_default_colour_weights(codestream.access_siz()); + } + + if (reversible) + { + // If we're doing reversible, assume we're not using quality layers. + // Yes, I know this is incorrect! + codestream.access_siz()->parse_string("Creversible=yes"); + codestream.access_siz()->parse_string("Clayers=1"); + num_layer_specs = 1; + layer_bytes[0] = 0; + } + else + { + // Rate is the argument passed into the LLImageJ2C which + // specifies the target compression rate. The default is 8:1. + // Possibly if max_bytes < 500, we should just use the default setting? + if (base.mRate != 0.f) + { + max_bytes = (U32)(base.mRate*base.getWidth()*base.getHeight()*base.getComponents()); + } + else + { + max_bytes = (U32)(base.getWidth()*base.getHeight()*base.getComponents()*0.125); + } + + const U32 min_bytes = FIRST_PACKET_SIZE; + if (max_bytes > min_bytes) + { + U32 i; + // This code is where we specify the target number of bytes for + // each layer. Not sure if we should do this for small images + // or not. The goal is to have this roughly align with + // different quality levels that we decode at. + for (i = min_bytes; i < max_bytes; i*=4) + { + if (i == min_bytes * 4) + { + i = 2000; + } + layer_bytes[num_layer_specs] = i; + num_layer_specs++; + } + layer_bytes[num_layer_specs] = max_bytes; + num_layer_specs++; + + std::string layer_string = llformat("Clayers=%d",num_layer_specs); + codestream.access_siz()->parse_string(layer_string.c_str()); + } + else + { + layer_bytes[0] = min_bytes; + num_layer_specs = 1; + std::string layer_string = llformat("Clayers=%d",num_layer_specs); + codestream.access_siz()->parse_string(layer_string.c_str()); + } + } + codestream.access_siz()->finalize_all(); + if (cpu_iterations >= 0) + { + codestream.collect_timing_stats(cpu_iterations); + } + codestream.change_appearance(transpose,vflip,hflip); + + // Now we are ready for sample data processing. + kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream); + bool done = false; + while (!done) + { + // Process line by line + done = true; + if (tile->advance_components()) + { + done = false; + tile->process_components(); + } + } + + // Produce the compressed output + codestream.flush(layer_bytes,num_layer_specs); + + // Cleanup + delete tile; + + codestream.destroy(); + if (record_stream != NULL) + { + delete record_stream; + } + + // Now that we're done encoding, create the new data buffer for the compressed + // image and stick it there. + + base.copyData(output_buffer, output_size); + base.updateData(); // set width, height + delete[] output_buffer; + } + catch(const char* msg) + { + base.setLastError(ll_safe_string(msg)); + return FALSE; + } + catch( ... ) + { + base.setLastError( "Unknown J2C error" ); + return FALSE; + } + + return TRUE; +} + +BOOL LLImageJ2CKDU::getMetadata(LLImageJ2C &base) +{ + // *FIX: kdu calls our callback function if there's an error, and + // then bombs. To regain control, we throw an exception, and + // catch it here. + try + { + setupCodeStream(base, FALSE, MODE_FAST); + return TRUE; + } + catch( const char* msg ) + { + base.setLastError(ll_safe_string(msg)); + return FALSE; + } + catch( ... ) + { + base.setLastError( "Unknown J2C error" ); + return FALSE; + } +} + +void set_default_colour_weights(kdu_params *siz) +{ + kdu_params *cod = siz->access_cluster(COD_params); + assert(cod != NULL); + + bool can_use_ycc = true; + bool rev0=false; + int depth0=0, sub_x0=1, sub_y0=1; + for (int c=0; c < 3; c++) + { + int depth=0; siz->get(Sprecision,c,0,depth); + int sub_y=1; siz->get(Ssampling,c,0,sub_y); + int sub_x=1; siz->get(Ssampling,c,1,sub_x); + kdu_params *coc = cod->access_relation(-1,c); + bool rev=false; coc->get(Creversible,0,0,rev); + if (c == 0) + { rev0=rev; depth0=depth; sub_x0=sub_x; sub_y0=sub_y; } + else if ((rev != rev0) || (depth != depth0) || + (sub_x != sub_x0) || (sub_y != sub_y0)) + can_use_ycc = false; + } + if (!can_use_ycc) + return; + + bool use_ycc; + if (!cod->get(Cycc,0,0,use_ycc)) + cod->set(Cycc,0,0,use_ycc=true); + if (!use_ycc) + return; + float weight; + if (cod->get(Clev_weights,0,0,weight) || + cod->get(Cband_weights,0,0,weight)) + return; // Weights already specified explicitly. + + /* These example weights are adapted from numbers generated by Marcus Nadenau + at EPFL, for a viewing distance of 15 cm and a display resolution of + 300 DPI. */ + + cod->parse_string("Cband_weights:C0=" + "{0.0901},{0.2758},{0.2758}," + "{0.7018},{0.8378},{0.8378},{1}"); + cod->parse_string("Cband_weights:C1=" + "{0.0263},{0.0863},{0.0863}," + "{0.1362},{0.2564},{0.2564}," + "{0.3346},{0.4691},{0.4691}," + "{0.5444},{0.6523},{0.6523}," + "{0.7078},{0.7797},{0.7797},{1}"); + cod->parse_string("Cband_weights:C2=" + "{0.0773},{0.1835},{0.1835}," + "{0.2598},{0.4130},{0.4130}," + "{0.5040},{0.6464},{0.6464}," + "{0.7220},{0.8254},{0.8254}," + "{0.8769},{0.9424},{0.9424},{1}"); +} + +/******************************************************************************/ +/* transfer_bytes */ +/******************************************************************************/ + +void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision) +/* Transfers source samples from the supplied line buffer into the output +byte buffer, spacing successive output samples apart by `gap' bytes +(to allow for interleaving of colour components). The function performs +all necessary level shifting, type conversion, rounding and truncation. */ +{ + int width = src.get_width(); + if (src.get_buf32() != NULL) + { // Decompressed samples have a 32-bit representation (integer or float) + assert(precision >= 8); // Else would have used 16 bit representation + kdu_sample32 *sp = src.get_buf32(); + if (!src.is_absolute()) + { // Transferring normalized floating point data. + float scale16 = (float)(1<<16); + kdu_int32 val; + + for (; width > 0; width--, sp++, dest+=gap) + { + val = (kdu_int32)(sp->fval*scale16); + val = (val+128)>>8; // May be faster than true rounding + val += 128; + if (val & ((-1)<<8)) + { + val = (val<0)?0:255; + } + *dest = (kdu_byte) val; + } + } + else + { // Transferring 32-bit absolute integers. + kdu_int32 val; + kdu_int32 downshift = precision-8; + kdu_int32 offset = (1<<downshift)>>1; + + for (; width > 0; width--, sp++, dest+=gap) + { + val = sp->ival; + val = (val+offset)>>downshift; + val += 128; + if (val & ((-1)<<8)) + { + val = (val<0)?0:255; + } + *dest = (kdu_byte) val; + } + } + } + else + { // Source data is 16 bits. + kdu_sample16 *sp = src.get_buf16(); + if (!src.is_absolute()) + { // Transferring 16-bit fixed point quantities + kdu_int16 val; + + if (precision >= 8) + { // Can essentially ignore the bit-depth. + for (; width > 0; width--, sp++, dest+=gap) + { + val = sp->ival; + val += (1<<(KDU_FIX_POINT-8))>>1; + val >>= (KDU_FIX_POINT-8); + val += 128; + if (val & ((-1)<<8)) + { + val = (val<0)?0:255; + } + *dest = (kdu_byte) val; + } + } + else + { // Need to force zeros into one or more least significant bits. + kdu_int16 downshift = KDU_FIX_POINT-precision; + kdu_int16 upshift = 8-precision; + kdu_int16 offset = 1<<(downshift-1); + + for (; width > 0; width--, sp++, dest+=gap) + { + val = sp->ival; + val = (val+offset)>>downshift; + val <<= upshift; + val += 128; + if (val & ((-1)<<8)) + { + val = (val<0)?0:(256-(1<<upshift)); + } + *dest = (kdu_byte) val; + } + } + } + else + { // Transferring 16-bit absolute integers. + kdu_int16 val; + + if (precision >= 8) + { + kdu_int16 downshift = precision-8; + kdu_int16 offset = (1<<downshift)>>1; + + for (; width > 0; width--, sp++, dest+=gap) + { + val = sp->ival; + val = (val+offset)>>downshift; + val += 128; + if (val & ((-1)<<8)) + { + val = (val<0)?0:255; + } + *dest = (kdu_byte) val; + } + } + else + { + kdu_int16 upshift = 8-precision; + + for (; width > 0; width--, sp++, dest+=gap) + { + val = sp->ival; + val <<= upshift; + val += 128; + if (val & ((-1)<<8)) + { + val = (val<0)?0:(256-(1<<upshift)); + } + *dest = (kdu_byte) val; + } + } + } + } +} + +LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap) +{ + S32 c; + + mTile = tile; + mBuf = buf; + mRowGap = row_gap; + + mNumComponents = tile.get_num_components(); + + llassert(mNumComponents<=4); + mUseYCC = tile.get_ycc(); + + for (c=0; c<4; ++c) + { + mReversible[c] = false; + mBitDepths[c] = 0; + } + + // Open tile-components and create processing engines and resources + for (c=0; c < mNumComponents; c++) + { + mComps[c] = mTile.access_component(c); + mReversible[c] = mComps[c].get_reversible(); + mBitDepths[c] = mComps[c].get_bit_depth(); + kdu_resolution res = mComps[c].access_resolution(); // Get top resolution + kdu_dims comp_dims; res.get_dims(comp_dims); + if (c == 0) + { + mDims = comp_dims; + } + else + { + llassert(mDims == comp_dims); // Safety check; the caller has ensured this + } + bool use_shorts = (mComps[c].get_bit_depth(true) <= 16); + mLines[c].pre_create(&mAllocator,mDims.size.x,mReversible[c],use_shorts); + if (res.which() == 0) // No DWT levels used + { + mEngines[c] = kdu_decoder(res.access_subband(LL_BAND),&mAllocator,use_shorts); + } + else + { + mEngines[c] = kdu_synthesis(res,&mAllocator,use_shorts); + } + } + mAllocator.finalize(); // Actually creates buffering resources + for (c=0; c < mNumComponents; c++) + { + mLines[c].create(); // Grabs resources from the allocator. + } +} + +LLKDUDecodeState::~LLKDUDecodeState() +{ + S32 c; + // Cleanup + for (c=0; c < mNumComponents; c++) + { + mEngines[c].destroy(); // engines are interfaces; no default destructors + } + + mTile.close(); +} + +BOOL LLKDUDecodeState::processTileDecode(F32 decode_time, BOOL limit_time) +/* Decompresses a tile, writing the data into the supplied byte buffer. +The buffer contains interleaved image components, if there are any. +Although you may think of the buffer as belonging entirely to this tile, +the `buf' pointer may actually point into a larger buffer representing +multiple tiles. For this reason, `row_gap' is needed to identify the +separation between consecutive rows in the real buffer. */ +{ + S32 c; + // Now walk through the lines of the buffer, recovering them from the + // relevant tile-component processing engines. + + LLTimer decode_timer; + while (mDims.size.y--) + { + for (c=0; c < mNumComponents; c++) + { + mEngines[c].pull(mLines[c],true); + } + if ((mNumComponents >= 3) && mUseYCC) + { + kdu_convert_ycc_to_rgb(mLines[0],mLines[1],mLines[2]); + } + for (c=0; c < mNumComponents; c++) + { + transfer_bytes(mBuf+c,mLines[c],mNumComponents,mBitDepths[c]); + } + mBuf += mRowGap; + if (mDims.size.y % 10) + { + if (limit_time && decode_timer.getElapsedTimeF32() > decode_time) + { + return FALSE; + } + } + } + return TRUE; +} + +// kdc_flow_control + +kdc_flow_control::kdc_flow_control (kdu_image_in_base *img_in, kdu_codestream codestream) +{ + int n; + + this->codestream = codestream; + codestream.get_valid_tiles(valid_tile_indices); + tile_idx = valid_tile_indices.pos; + tile = codestream.open_tile(tile_idx,NULL); + + // Set up the individual components + num_components = codestream.get_num_components(true); + components = new kdc_component_flow_control[num_components]; + count_delta = 0; + kdc_component_flow_control *comp = components; + for (n = 0; n < num_components; n++, comp++) + { + comp->line = NULL; + comp->reader = img_in; + kdu_coords subsampling; + codestream.get_subsampling(n,subsampling,true); + kdu_dims dims; + codestream.get_tile_dims(tile_idx,n,dims,true); + comp->vert_subsampling = subsampling.y; + if ((n == 0) || (comp->vert_subsampling < count_delta)) + { + count_delta = comp->vert_subsampling; + } + comp->ratio_counter = 0; + comp->remaining_lines = comp->initial_lines = dims.size.y; + } + assert(num_components >= 0); + + tile.set_components_of_interest(num_components); + max_buffer_memory = engine.create(codestream,tile,false,NULL,false,1,NULL,NULL,false); +} + +kdc_flow_control::~kdc_flow_control() +{ + if (components != NULL) + delete[] components; + if (engine.exists()) + engine.destroy(); +} + +bool kdc_flow_control::advance_components() +{ + bool found_line = false; + while (!found_line) + { + bool all_done = true; + kdc_component_flow_control *comp = components; + for (int n = 0; n < num_components; n++, comp++) + { + assert(comp->ratio_counter >= 0); + if (comp->remaining_lines > 0) + { + all_done = false; + comp->ratio_counter -= count_delta; + if (comp->ratio_counter < 0) + { + found_line = true; + comp->line = engine.exchange_line(n,NULL,NULL); + assert(comp->line != NULL); + if (comp->line->get_width()) + { + comp->reader->get(n,*(comp->line),0); + } + } + } + } + if (all_done) + { + return false; + } + } + return true; +} + +void kdc_flow_control::process_components() +{ + kdc_component_flow_control *comp = components; + for (int n = 0; n < num_components; n++, comp++) + { + if (comp->ratio_counter < 0) + { + comp->ratio_counter += comp->vert_subsampling; + assert(comp->ratio_counter >= 0); + assert(comp->remaining_lines > 0); + comp->remaining_lines--; + assert(comp->line != NULL); + engine.exchange_line(n,comp->line,NULL); + comp->line = NULL; + } + } +} diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h new file mode 100644 index 0000000000..5628f69eeb --- /dev/null +++ b/indra/llkdu/llimagej2ckdu.h @@ -0,0 +1,90 @@ +/** + * @file llimagej2ckdu.h + * @brief This is an implementation of JPEG2000 encode/decode using Kakadu + * + * $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_LLIMAGEJ2CKDU_H +#define LL_LLIMAGEJ2CKDU_H + +#include "llimagej2c.h" + +// +// KDU core header files +// +#include "kdu_elementary.h" +#include "kdu_messaging.h" +#include "kdu_params.h" +#include "kdu_compressed.h" +#include "kdu_sample_processing.h" + +class LLKDUDecodeState; +class LLKDUMemSource; + +class LLImageJ2CKDU : public LLImageJ2CImpl +{ +public: + enum ECodeStreamMode + { + MODE_FAST = 0, + MODE_RESILIENT = 1, + MODE_FUSSY = 2 + }; + LLImageJ2CKDU(); + virtual ~LLImageJ2CKDU(); + +protected: + /*virtual*/ BOOL getMetadata(LLImageJ2C &base); + /*virtual*/ BOOL decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count); + /*virtual*/ BOOL encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0, + BOOL reversible=FALSE); + +private: + void setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECodeStreamMode mode); + void cleanupCodeStream(); + BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count ); + + // Encode variable + LLKDUMemSource *mInputp; + kdu_codestream *mCodeStreamp; + kdu_coords *mTPosp; // tile position + kdu_dims *mTileIndicesp; + + // Temporary variables for in-progress decodes... + LLImageRaw *mRawImagep; + LLKDUDecodeState *mDecodeState; +}; + +#if LL_WINDOWS +# define LLSYMEXPORT __declspec(dllexport) +#elif LL_LINUX +# define LLSYMEXPORT __attribute__ ((visibility("default"))) +#else +# define LLSYMEXPORT +#endif + +extern "C" LLSYMEXPORT const char* engineInfoLLImageJ2CKDU(); +extern "C" LLSYMEXPORT LLImageJ2CKDU* createLLImageJ2CKDU(); +extern "C" LLSYMEXPORT void destroyLLImageJ2CKDU(LLImageJ2CKDU* kdu); + +#endif diff --git a/indra/llkdu/llkdumem.cpp b/indra/llkdu/llkdumem.cpp new file mode 100644 index 0000000000..1f549cbbe0 --- /dev/null +++ b/indra/llkdu/llkdumem.cpp @@ -0,0 +1,196 @@ + /** + * @file llkdumem.cpp + * @brief Helper class for kdu memory management + * + * $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 "llkdumem.h" +#include "llerror.h" + +#if defined(LL_WINDOWS) +# pragma warning(disable: 4702) // unreachable code +#endif + +LLKDUMemIn::LLKDUMemIn(const U8 *data, + const U32 size, + const U16 width, + const U16 height, + const U8 in_num_components, + siz_params *siz) +{ + U8 n; + + first_comp_idx = 0; + rows = height; + cols = width; + num_components = in_num_components; + alignment_bytes = 0; + + for (n=0; n<3; ++n) + { + precision[n] = 0; + } + + for (n=0; n < num_components; ++n) + { + siz->set(Sdims,n,0,rows); + siz->set(Sdims,n,1,cols); + siz->set(Ssigned,n,0,false); + siz->set(Sprecision,n,0,8); + } + incomplete_lines = NULL; + free_lines = NULL; + num_unread_rows = rows; + + mData = data; + mDataSize = size; + mCurPos = 0; +} + +LLKDUMemIn::~LLKDUMemIn() +{ + if ((num_unread_rows > 0) || (incomplete_lines != NULL)) + { + kdu_warning w; + w << "Not all rows of image components " + << first_comp_idx << " through " + << first_comp_idx+num_components-1 + << " were consumed!"; + } + image_line_buf *tmp; + while ((tmp=incomplete_lines) != NULL) + { + incomplete_lines = tmp->next; + delete tmp; + } + while ((tmp=free_lines) != NULL) + { + free_lines = tmp->next; + delete tmp; + } +} + + +bool LLKDUMemIn::get(int comp_idx, kdu_line_buf &line, int x_tnum) +{ + int idx = comp_idx - this->first_comp_idx; + assert((idx >= 0) && (idx < num_components)); + x_tnum = x_tnum*num_components+idx; + image_line_buf *scan, *prev=NULL; + for (scan=incomplete_lines; scan != NULL; prev=scan, scan=scan->next) + { + assert(scan->next_x_tnum >= x_tnum); + if (scan->next_x_tnum == x_tnum) + { + break; + } + } + if (scan == NULL) + { // Need to read a new image line. + assert(x_tnum == 0); // Must consume in very specific order. + if (num_unread_rows == 0) + { + return false; + } + if ((scan = free_lines) == NULL) + { + scan = new image_line_buf(cols+3,num_components); + } + free_lines = scan->next; + if (prev == NULL) + { + incomplete_lines = scan; + } + else + { + prev->next = scan; + } + + // Copy from image buffer into scan. + memcpy(scan->buf, mData+mCurPos, cols*num_components); + mCurPos += cols*num_components; + + num_unread_rows--; + scan->accessed_samples = 0; + scan->next_x_tnum = 0; + } + + assert((cols-scan->accessed_samples) >= line.get_width()); + + int comp_offset = idx; + kdu_byte *sp = scan->buf+num_components*scan->accessed_samples + comp_offset; + int n=line.get_width(); + + if (line.get_buf32() != NULL) + { + kdu_sample32 *dp = line.get_buf32(); + if (line.is_absolute()) + { // 32-bit absolute integers + for (; n > 0; n--, sp+=num_components, dp++) + { + dp->ival = ((kdu_int32)(*sp)) - 128; + } + } + else + { // true 32-bit floats + for (; n > 0; n--, sp+=num_components, dp++) + { + dp->fval = (((float)(*sp)) / 256.0F) - 0.5F; + } + } + } + else + { + kdu_sample16 *dp = line.get_buf16(); + if (line.is_absolute()) + { // 16-bit absolute integers + for (; n > 0; n--, sp+=num_components, dp++) + { + dp->ival = ((kdu_int16)(*sp)) - 128; + } + } + else + { // 16-bit normalized representation. + for (; n > 0; n--, sp+=num_components, dp++) + { + dp->ival = (((kdu_int16)(*sp)) - 128) << (KDU_FIX_POINT-8); + } + } + } + + scan->next_x_tnum++; + if (idx == (num_components-1)) + { + scan->accessed_samples += line.get_width(); + } + if (scan->accessed_samples == cols) + { // Send empty line to free list. + assert(scan == incomplete_lines); + incomplete_lines = scan->next; + scan->next = free_lines; + free_lines = scan; + } + + return true; +} diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h new file mode 100644 index 0000000000..7064de4408 --- /dev/null +++ b/indra/llkdu/llkdumem.h @@ -0,0 +1,145 @@ +/** + * @file llkdumem.h + * @brief Helper class for kdu memory management + * + * $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_LLKDUMEM_H +#define LL_LLKDUMEM_H + +// Support classes for reading and writing from memory buffers in KDU +#include "kdu_image.h" +#include "kdu_elementary.h" +#include "kdu_messaging.h" +#include "kdu_params.h" +#include "kdu_compressed.h" +#include "kdu_sample_processing.h" +#include "image_local.h" +#include "stdtypes.h" + +class LLKDUMemSource: public kdu_compressed_source +{ +public: // Member functions + LLKDUMemSource(U8 *input_buffer, U32 size) + { + mData = input_buffer; + mSize = size; + mCurPos = 0; + } + + ~LLKDUMemSource() + { + } + + int read(kdu_byte *buf, int num_bytes) + { + U32 num_out; + num_out = num_bytes; + + if ((mSize - mCurPos) < (U32)num_bytes) + { + num_out = mSize -mCurPos; + } + memcpy(buf, mData + mCurPos, num_out); + mCurPos += num_out; + return num_out; + } + + void reset() + { + mCurPos = 0; + } + +private: // Data + U8 *mData; + U32 mSize; + U32 mCurPos; +}; + +class LLKDUMemTarget: public kdu_compressed_target +{ +public: // Member functions + LLKDUMemTarget(U8 *output_buffer, U32 &output_size, const U32 buffer_size) + { + mData = output_buffer; + mSize = buffer_size; + mCurPos = 0; + mOutputSize = &output_size; + } + + ~LLKDUMemTarget() + { + } + + bool write(const kdu_byte *buf, int num_bytes) + { + U32 num_out; + num_out = num_bytes; + + if ((mSize - mCurPos) < (U32)num_bytes) + { + num_out = mSize - mCurPos; + memcpy(mData + mCurPos, buf, num_out); + return false; + } + memcpy(mData + mCurPos, buf, num_out); + mCurPos += num_out; + *mOutputSize = mCurPos; + return true; + } + +private: // Data + U8 *mData; + U32 mSize; + U32 mCurPos; + U32 *mOutputSize; +}; + +class LLKDUMemIn : public kdu_image_in_base +{ +public: // Member functions + LLKDUMemIn(const U8 *data, + const U32 size, + const U16 rows, + const U16 cols, + U8 in_num_components, + siz_params *siz); + ~LLKDUMemIn(); + + bool get(int comp_idx, kdu_line_buf &line, int x_tnum); + +private: // Data + const U8 *mData; + int first_comp_idx; + int num_components; + int rows, cols; + int alignment_bytes; // Number of 0's at end of each line. + int precision[3]; + image_line_buf *incomplete_lines; // Each "sample" represents a full pixel + image_line_buf *free_lines; + int num_unread_rows; + + U32 mCurPos; + U32 mDataSize; +}; +#endif diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp new file mode 100644 index 0000000000..1ccee4bb64 --- /dev/null +++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp @@ -0,0 +1,248 @@ +/** + * @file llimagej2ckdu_test.cpp + * @author Merov Linden + * @date 2010-12-17 + * + * $LicenseInfo:firstyear=2006&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" +// Class to test +#include "../llimagej2ckdu.h" +#include "../llkdumem.h" +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + +// End Stubbing +// ------------------------------------------------------------------------------------------- +// Stubb the LL Image Classes +LLImageRaw::LLImageRaw() { } +LLImageRaw::~LLImageRaw() { } +U8* LLImageRaw::allocateData(S32 ) { return NULL; } +void LLImageRaw::deleteData() { } +U8* LLImageRaw::reallocateData(S32 ) { return NULL; } +BOOL LLImageRaw::resize(U16, U16, S8) { return TRUE; } // this method always returns TRUE... + +LLImageBase::LLImageBase() +: mData(NULL), +mDataSize(0), +mWidth(0), +mHeight(0), +mComponents(0), +mBadBufferAllocation(false), +mAllowOverSize(false), +mMemType(LLMemType::MTYPE_IMAGEBASE) +{ } +LLImageBase::~LLImageBase() { } +U8* LLImageBase::allocateData(S32 ) { return NULL; } +void LLImageBase::deleteData() { } +void LLImageBase::dump() { } +const U8* LLImageBase::getData() const { return NULL; } +U8* LLImageBase::getData() { return NULL; } +U8* LLImageBase::reallocateData(S32 ) { return NULL; } +void LLImageBase::sanityCheck() { } +void LLImageBase::setSize(S32 , S32 , S32 ) { } + +LLImageJ2CImpl::~LLImageJ2CImpl() { } + +LLImageFormatted::LLImageFormatted(S8 ) { } +LLImageFormatted::~LLImageFormatted() { } +U8* LLImageFormatted::allocateData(S32 ) { return NULL; } +S32 LLImageFormatted::calcDataSize(S32 ) { return 0; } +S32 LLImageFormatted::calcDiscardLevelBytes(S32 ) { return 0; } +BOOL LLImageFormatted::decodeChannels(LLImageRaw*, F32, S32, S32) { return FALSE; } +BOOL LLImageFormatted::copyData(U8 *, S32) { return TRUE; } // this method always returns TRUE... +void LLImageFormatted::deleteData() { } +void LLImageFormatted::dump() { } +U8* LLImageFormatted::reallocateData(S32 ) { return NULL; } +void LLImageFormatted::resetLastError() { } +void LLImageFormatted::sanityCheck() { } +void LLImageFormatted::setLastError(const std::string& , const std::string& ) { } + +LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C) { } +LLImageJ2C::~LLImageJ2C() { } +S32 LLImageJ2C::calcDataSize(S32 ) { return 0; } +S32 LLImageJ2C::calcDiscardLevelBytes(S32 ) { return 0; } +S32 LLImageJ2C::calcHeaderSize() { return 0; } +BOOL LLImageJ2C::decode(LLImageRaw*, F32) { return FALSE; } +BOOL LLImageJ2C::decodeChannels(LLImageRaw*, F32, S32, S32 ) { return FALSE; } +void LLImageJ2C::decodeFailed() { } +BOOL LLImageJ2C::encode(const LLImageRaw*, F32) { return FALSE; } +S8 LLImageJ2C::getRawDiscardLevel() { return 0; } +void LLImageJ2C::resetLastError() { } +void LLImageJ2C::setLastError(const std::string&, const std::string&) { } +BOOL LLImageJ2C::updateData() { return FALSE; } +void LLImageJ2C::updateRawDiscardLevel() { } + +LLKDUMemIn::LLKDUMemIn(const U8*, const U32, const U16, const U16, const U8, siz_params*) { } +LLKDUMemIn::~LLKDUMemIn() { } +bool LLKDUMemIn::get(int, kdu_line_buf&, int) { return false; } + +// Stub Kakadu Library calls +kdu_tile_comp kdu_tile::access_component(int ) { kdu_tile_comp a; return a; } +void kdu_tile::close(kdu_thread_env* ) { } +int kdu_tile::get_num_components() { return 0; } +bool kdu_tile::get_ycc() { return false; } +void kdu_tile::set_components_of_interest(int , const int* ) { } +kdu_resolution kdu_tile_comp::access_resolution() { kdu_resolution a; return a; } +int kdu_tile_comp::get_bit_depth(bool ) { return 8; } +bool kdu_tile_comp::get_reversible() { return false; } +kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; } +void kdu_resolution::get_dims(kdu_dims& ) { } +int kdu_resolution::which() { return 0; } +kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { } +kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { } +kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { } +kdu_params::~kdu_params() { } +void kdu_params::set(const char* , int , int , bool ) { } +void kdu_params::set(const char* , int , int , int ) { } +void kdu_params::finalize_all(bool ) { } +void kdu_params::copy_from(kdu_params*, int, int, int, int, int, bool, bool, bool) { } +bool kdu_params::parse_string(const char*) { return false; } +bool kdu_params::get(const char*, int, int, bool&, bool, bool, bool) { return false; } +bool kdu_params::get(const char*, int, int, float&, bool, bool, bool) { return false; } +bool kdu_params::get(const char*, int, int, int&, bool, bool, bool) { return false; } +kdu_params* kdu_params::access_relation(int, int, int, bool) { return NULL; } +kdu_params* kdu_params::access_cluster(const char*) { return NULL; } +void kdu_codestream::set_fast() { } +void kdu_codestream::set_fussy() { } +void kdu_codestream::get_dims(int, kdu_dims&, bool ) { } +void kdu_codestream::change_appearance(bool, bool, bool) { } +void kdu_codestream::get_tile_dims(kdu_coords, int, kdu_dims&, bool ) { } +void kdu_codestream::destroy() { } +void kdu_codestream::collect_timing_stats(int ) { } +void kdu_codestream::set_max_bytes(kdu_long, bool, bool ) { } +void kdu_codestream::get_valid_tiles(kdu_dims& ) { } +void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long ) { } +void kdu_codestream::create(kdu_compressed_source*, kdu_thread_env*) { } +void kdu_codestream::apply_input_restrictions( int, int, int, int, kdu_dims*, kdu_component_access_mode ) { } +void kdu_codestream::get_subsampling(int , kdu_coords&, bool ) { } +void kdu_codestream::flush(kdu_long *, int , kdu_uint16 *, bool, bool, double, kdu_thread_env*) { } +void kdu_codestream::set_resilient(bool ) { } +int kdu_codestream::get_num_components(bool ) { return 0; } +siz_params* kdu_codestream::access_siz() { return NULL; } +kdu_tile kdu_codestream::open_tile(kdu_coords , kdu_thread_env* ) { kdu_tile a; return a; } +kdu_codestream_comment kdu_codestream::add_comment() { kdu_codestream_comment a; return a; } +bool kdu_codestream_comment::put_text(const char*) { return false; } +void kdu_customize_warnings(kdu_message*) { } +void kdu_customize_errors(kdu_message*) { } +void kdu_convert_ycc_to_rgb(kdu_line_buf&, kdu_line_buf&, kdu_line_buf&, int) { } +kdu_long kdu_multi_analysis::create(kdu_codestream, kdu_tile, bool, kdu_roi_image*, bool, int, kdu_thread_env*, kdu_thread_queue*, bool ) { kdu_long a = 0; return a; } +siz_params::siz_params() : kdu_params(NULL, false, false, false, false, false) { } +void siz_params::finalize(bool ) { } +void siz_params::copy_with_xforms(kdu_params*, int, int, bool, bool, bool) { } +int siz_params::write_marker_segment(kdu_output*, kdu_params*, int) { return 0; } +bool siz_params::check_marker_segment(kdu_uint16, int, kdu_byte a[], int&) { return false; } +bool siz_params::read_marker_segment(kdu_uint16, int, kdu_byte a[], int) { return false; } + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- + +namespace tut +{ + // Test wrapper declarations + struct llimagej2ckdu_test + { + // Derived test class + class LLTestImageJ2CKDU : public LLImageJ2CKDU + { + public: + // Provides public access to some protected methods for testing + BOOL callGetMetadata(LLImageJ2C &base) { return getMetadata(base); } + BOOL callDecodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) + { + return decodeImpl(base, raw_image, decode_time, first_channel, max_channel_count); + } + BOOL callEncodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text) + { + return encodeImpl(base, raw_image, comment_text); + } + }; + // Instance to be tested + LLTestImageJ2CKDU* mImage; + + // Constructor and destructor of the test wrapper + llimagej2ckdu_test() + { + mImage = new LLTestImageJ2CKDU; + } + ~llimagej2ckdu_test() + { + delete mImage; + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<llimagej2ckdu_test> llimagej2ckdu_t; + typedef llimagej2ckdu_t::object llimagej2ckdu_object_t; + tut::llimagej2ckdu_t tut_llimagej2ckdu("LLImageJ2CKDU"); + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + + // Test 1 : test getMetadata() + template<> template<> + void llimagej2ckdu_object_t::test<1>() + { + LLImageJ2C* image = new LLImageJ2C(); + BOOL res = mImage->callGetMetadata(*image); + // Trying to set up a data stream with all NIL values and stubbed KDU will "work" and return TRUE + // Note that is linking with KDU, that call will throw an exception and fail, returning FALSE + ensure("getMetadata() test failed", res == TRUE); + } + + // Test 2 : test decodeImpl() + template<> template<> + void llimagej2ckdu_object_t::test<2>() + { + LLImageJ2C* image = new LLImageJ2C(); + LLImageRaw* raw = new LLImageRaw(); + BOOL res = mImage->callDecodeImpl(*image, *raw, 0.0, 0, 0); + // Decoding returns TRUE whenever there's nothing else to do, including if decoding failed, so we'll get TRUE here + ensure("decodeImpl() test failed", res == TRUE); + } + + // Test 3 : test encodeImpl() + template<> template<> + void llimagej2ckdu_object_t::test<3>() + { + LLImageJ2C* image = new LLImageJ2C(); + LLImageRaw* raw = new LLImageRaw(); + BOOL res = mImage->callEncodeImpl(*image, *raw, NULL); + // Encoding returns TRUE unless an exception was raised, so we'll get TRUE here though nothing really was done + ensure("encodeImpl() test failed", res == TRUE); + } +} diff --git a/indra/llmath/tests/m3math_test.cpp b/indra/llmath/tests/m3math_test.cpp index 8abf61b740..e4d31996a3 100644 --- a/indra/llmath/tests/m3math_test.cpp +++ b/indra/llmath/tests/m3math_test.cpp @@ -280,7 +280,6 @@ namespace tut llmat_obj.setRows(llvec1, llvec2, llvec3); llmat_obj.orthogonalize(); - skip("Grr, LLMatrix3::orthogonalize test is failing. Has it ever worked?"); ensure("LLMatrix3::orthogonalize failed ", is_approx_equal(0.19611613f, llmat_obj.mMatrix[0][0]) && is_approx_equal(0.78446454f, llmat_obj.mMatrix[0][1]) && diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index b26d412e9f..27a368df3d 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -513,6 +513,10 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL } +// +// *NOTE: Logic here is replicated in LLViewerAssetStorage::_queueDataRequest. +// Changes here may need to be replicated in the viewer's derived class. +// void LLAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType atype, LLGetAssetCallback callback, void *user_data, BOOL duplicate, diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 2f2d9099a3..767001b633 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -81,8 +81,11 @@ namespace LLAvatarNameCache // only need per-frame timing resolution LLFrameTimer sRequestTimer; - // Periodically clean out expired entries from the cache - //LLFrameTimer sEraseExpiredTimer; + /// Maximum time an unrefreshed cache entry is allowed + const F64 MAX_UNREFRESHED_TIME = 20.0 * 60.0; + + /// Time when unrefreshed cached names were checked last + static F64 sLastExpireCheck; //----------------------------------------------------------------------- // Internal methods @@ -99,8 +102,9 @@ namespace LLAvatarNameCache // Legacy name system callback void legacyNameCallback(const LLUUID& agent_id, - const std::string& full_name, - bool is_group); + const std::string& full_name, + bool is_group + ); void requestNamesViaLegacy(); @@ -117,7 +121,7 @@ namespace LLAvatarNameCache bool isRequestPending(const LLUUID& agent_id); // Erase expired names from cache - void eraseExpired(); + void eraseUnrefreshed(); bool expirationFromCacheControl(LLSD headers, F64 *expires); } @@ -187,6 +191,7 @@ public: { // Pull expiration out of headers if available F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mHeaders); + F64 now = LLFrameTimer::getTotalSeconds(); LLSD agents = content["agents"]; LLSD::array_const_iterator it = agents.beginArray(); @@ -207,100 +212,91 @@ public: av_name.mDisplayName = av_name.mUsername; } + LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << " " + << "user '" << av_name.mUsername << "' " + << "display '" << av_name.mDisplayName << "' " + << "expires in " << expires - now << " seconds" + << LL_ENDL; + // cache it and fire signals LLAvatarNameCache::processName(agent_id, av_name, true); } // Same logic as error response case LLSD unresolved_agents = content["bad_ids"]; - if (unresolved_agents.size() > 0) + S32 num_unresolved = unresolved_agents.size(); + if (num_unresolved > 0) { - const std::string DUMMY_NAME("\?\?\?"); - LLAvatarName av_name; - av_name.mUsername = DUMMY_NAME; - av_name.mDisplayName = DUMMY_NAME; - av_name.mIsDisplayNameDefault = false; - av_name.mIsDummy = true; - av_name.mExpires = expires; - + LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; " + << "expires in " << expires - now << " seconds" + << LL_ENDL; it = unresolved_agents.beginArray(); for ( ; it != unresolved_agents.endArray(); ++it) { const LLUUID& agent_id = *it; - // cache it and fire signals - LLAvatarNameCache::processName(agent_id, av_name, true); + + LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " + << "failed id " << agent_id + << LL_ENDL; + + LLAvatarNameCache::handleAgentError(agent_id); } } - } + LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result " + << LLAvatarNameCache::sCache.size() << " cached names" + << LL_ENDL; + } /*virtual*/ void error(U32 status, const std::string& reason) { - // We're going to construct a dummy record and cache it for a while, - // either briefly for a 503 Service Unavailable, or longer for other - // errors. - F64 retry_timestamp = errorRetryTimestamp(status); - - // *NOTE: "??" starts trigraphs in C/C++, escape the question marks. - const std::string DUMMY_NAME("\?\?\?"); - LLAvatarName av_name; - av_name.mUsername = DUMMY_NAME; - av_name.mDisplayName = DUMMY_NAME; - av_name.mIsDisplayNameDefault = false; - av_name.mIsDummy = true; - av_name.mExpires = retry_timestamp; - - // Add dummy records for all agent IDs in this request + // If there's an error, it might be caused by PeopleApi, + // or when loading textures on startup and using a very slow + // network, this query may time out. + // What we should do depends on whether or not we have a cached name + LL_WARNS("AvNameCache") << "LLAvatarNameResponder::error " << status << " " << reason + << LL_ENDL; + + // Add dummy records for any agent IDs in this request that we do not have cached already std::vector<LLUUID>::const_iterator it = mAgentIDs.begin(); for ( ; it != mAgentIDs.end(); ++it) { const LLUUID& agent_id = *it; - // cache it and fire signals - LLAvatarNameCache::processName(agent_id, av_name, true); - } - } - - // Return time to retry a request that generated an error, based on - // error type and headers. Return value is seconds-since-epoch. - F64 errorRetryTimestamp(S32 status) - { - F64 now = LLFrameTimer::getTotalSeconds(); - - // Retry-After takes priority - LLSD retry_after = mHeaders["retry-after"]; - if (retry_after.isDefined()) - { - // We only support the delta-seconds type - S32 delta_seconds = retry_after.asInteger(); - if (delta_seconds > 0) - { - // ...valid delta-seconds - return now + F64(delta_seconds); - } - } - - // If no Retry-After, look for Cache-Control max-age - F64 expires = 0.0; - if (LLAvatarNameCache::expirationFromCacheControl(mHeaders, &expires)) - { - return expires; - } - - // No information in header, make a guess - if (status == 503) - { - // ...service unavailable, retry soon - const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min - return now + SERVICE_UNAVAILABLE_DELAY; - } - else - { - // ...other unexpected error - const F64 DEFAULT_DELAY = 3600.0; // 1 hour - return now + DEFAULT_DELAY; + LLAvatarNameCache::handleAgentError(agent_id); } } }; +// Provide some fallback for agents that return errors +void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) +{ + std::map<LLUUID,LLAvatarName>::iterator existing = sCache.find(agent_id); + if (existing == sCache.end()) + { + // there is no existing cache entry, so make a temporary name from legacy + LL_WARNS("AvNameCache") << "LLAvatarNameCache get legacy for agent " + << agent_id << LL_ENDL; + gCacheName->get(agent_id, false, // legacy compatibility + boost::bind(&LLAvatarNameCache::legacyNameCallback, + _1, _2, _3)); + } + else + { + // we have a chached (but probably expired) entry - since that would have + // been returned by the get method, there is no need to signal anyone + + // Clear this agent from the pending list + LLAvatarNameCache::sPendingQueue.erase(agent_id); + + const LLAvatarName& av_name = existing->second; + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache use cache for agent " + << agent_id + << "user '" << av_name.mUsername << "' " + << "display '" << av_name.mDisplayName << "' " + << "expires in " << av_name.mExpires - LLFrameTimer::getTotalSeconds() << " seconds" + << LL_ENDL; + } +} + void LLAvatarNameCache::processName(const LLUUID& agent_id, const LLAvatarName& av_name, bool add_to_cache) @@ -342,6 +338,7 @@ void LLAvatarNameCache::requestNamesViaCapability() std::vector<LLUUID> agent_ids; agent_ids.reserve(128); + U32 ids = 0; ask_queue_t::const_iterator it = sAskQueue.begin(); for ( ; it != sAskQueue.end(); ++it) { @@ -352,11 +349,13 @@ void LLAvatarNameCache::requestNamesViaCapability() // ...starting new request url += sNameLookupURL; url += "?ids="; + ids = 1; } else { // ...continuing existing request url += "&ids="; + ids++; } url += agent_id.asString(); agent_ids.push_back(agent_id); @@ -366,7 +365,9 @@ void LLAvatarNameCache::requestNamesViaCapability() if (url.size() > NAME_URL_SEND_THRESHOLD) { - //llinfos << "requestNames " << url << llendl; + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability first " + << ids << " ids" + << LL_ENDL; LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); url.clear(); agent_ids.clear(); @@ -375,7 +376,9 @@ void LLAvatarNameCache::requestNamesViaCapability() if (!url.empty()) { - //llinfos << "requestNames " << url << llendl; + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability all " + << ids << " ids" + << LL_ENDL; LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); url.clear(); agent_ids.clear(); @@ -392,6 +395,11 @@ void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id, // Construct a dummy record for this name. By convention, SLID is blank // Never expires, but not written to disk, so lasts until end of session. LLAvatarName av_name; + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::legacyNameCallback " + << "agent " << agent_id << " " + << "full name '" << full_name << "'" + << ( is_group ? " [group]" : "" ) + << LL_ENDL; buildLegacyName(full_name, &av_name); // Don't add to cache, the data already exists in the legacy name system @@ -413,6 +421,8 @@ void LLAvatarNameCache::requestNamesViaLegacy() // invoked below. This should never happen in practice. sPendingQueue[agent_id] = now; + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaLegacy agent " << agent_id << LL_ENDL; + gCacheName->get(agent_id, false, // legacy compatibility boost::bind(&LLAvatarNameCache::legacyNameCallback, _1, _2, _3)); @@ -451,21 +461,24 @@ void LLAvatarNameCache::importFile(std::istream& istr) av_name.fromLLSD( it->second ); sCache[agent_id] = av_name; } - // entries may have expired since we last ran the viewer, just - // clean them out now - eraseExpired(); - llinfos << "loaded " << sCache.size() << llendl; + LL_INFOS("AvNameCache") << "loaded " << sCache.size() << LL_ENDL; + + // Some entries may have expired since the cache was stored, + // but they will be flushed in the first call to eraseUnrefreshed + // from LLAvatarNameResponder::idle } void LLAvatarNameCache::exportFile(std::ostream& ostr) { LLSD agents; + F64 max_unrefreshed = LLFrameTimer::getTotalSeconds() - MAX_UNREFRESHED_TIME; cache_t::const_iterator it = sCache.begin(); for ( ; it != sCache.end(); ++it) { const LLUUID& agent_id = it->first; const LLAvatarName& av_name = it->second; - if (!av_name.mIsDummy) + // Do not write temporary or expired entries to the stored cache + if (!av_name.mIsTemporaryName && av_name.mExpires >= max_unrefreshed) { // key must be a string agents[agent_id.asString()] = av_name.asLLSD(); @@ -500,62 +513,63 @@ void LLAvatarNameCache::idle() // return; //} - // Must be large relative to above - - // No longer deleting expired entries, just re-requesting in the get - // this way first synchronous get call on an expired entry won't return - // legacy name. LF - - //const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds - //if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) - //{ - // eraseExpired(); - //} - - if (sAskQueue.empty()) + if (!sAskQueue.empty()) { - return; + if (useDisplayNames()) + { + requestNamesViaCapability(); + } + else + { + // ...fall back to legacy name cache system + requestNamesViaLegacy(); + } } - if (useDisplayNames()) - { - requestNamesViaCapability(); - } - else - { - // ...fall back to legacy name cache system - requestNamesViaLegacy(); - } + // erase anything that has not been refreshed for more than MAX_UNREFRESHED_TIME + eraseUnrefreshed(); } bool LLAvatarNameCache::isRequestPending(const LLUUID& agent_id) { + bool isPending = false; const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; - F64 now = LLFrameTimer::getTotalSeconds(); - F64 expire_time = now - PENDING_TIMEOUT_SECS; pending_queue_t::const_iterator it = sPendingQueue.find(agent_id); if (it != sPendingQueue.end()) { - bool request_expired = (it->second < expire_time); - return !request_expired; + // in the list of requests in flight, retry if too old + F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; + isPending = (it->second > expire_time); } - return false; + return isPending; } -void LLAvatarNameCache::eraseExpired() +void LLAvatarNameCache::eraseUnrefreshed() { F64 now = LLFrameTimer::getTotalSeconds(); - cache_t::iterator it = sCache.begin(); - while (it != sCache.end()) - { - cache_t::iterator cur = it; - ++it; - const LLAvatarName& av_name = cur->second; - if (av_name.mExpires < now) - { - sCache.erase(cur); - } + F64 max_unrefreshed = now - MAX_UNREFRESHED_TIME; + + if (!sLastExpireCheck || sLastExpireCheck < max_unrefreshed) + { + sLastExpireCheck = now; + cache_t::iterator it = sCache.begin(); + while (it != sCache.end()) + { + cache_t::iterator cur = it; + ++it; + const LLAvatarName& av_name = cur->second; + if (av_name.mExpires < max_unrefreshed) + { + const LLUUID& agent_id = it->first; + LL_DEBUGS("AvNameCache") << agent_id + << " user '" << av_name.mUsername << "' " + << "expired " << now - av_name.mExpires << " secs ago" + << LL_ENDL; + sCache.erase(cur); + } + } + LL_INFOS("AvNameCache") << sCache.size() << " cached avatar names" << LL_ENDL; } } @@ -566,8 +580,11 @@ void LLAvatarNameCache::buildLegacyName(const std::string& full_name, av_name->mUsername = ""; av_name->mDisplayName = full_name; av_name->mIsDisplayNameDefault = true; - av_name->mIsDummy = true; - av_name->mExpires = F64_MAX; + av_name->mIsTemporaryName = true; + av_name->mExpires = F64_MAX; // not used because these are not cached + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::buildLegacyName " + << full_name + << LL_ENDL; } // fills in av_name if it has it in the cache, even if expired (can check expiry time) @@ -590,6 +607,9 @@ bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) { if (!isRequestPending(agent_id)) { + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::get " + << "refresh agent " << agent_id + << LL_ENDL; sAskQueue.insert(agent_id); } } @@ -611,6 +631,9 @@ bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) if (!isRequestPending(agent_id)) { + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::get " + << "queue request for agent " << agent_id + << LL_ENDL; sAskQueue.insert(agent_id); } @@ -643,7 +666,6 @@ void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) { // ...name already exists in cache, fire callback now fireSignal(agent_id, slot, av_name); - return; } } @@ -739,6 +761,9 @@ F64 LLAvatarNameCache::nameExpirationFromHeaders(LLSD headers) bool LLAvatarNameCache::expirationFromCacheControl(LLSD headers, F64 *expires) { + bool fromCacheControl = false; + F64 now = LLFrameTimer::getTotalSeconds(); + // Allow the header to override the default LLSD cache_control_header = headers["cache-control"]; if (cache_control_header.isDefined()) @@ -747,12 +772,16 @@ bool LLAvatarNameCache::expirationFromCacheControl(LLSD headers, F64 *expires) std::string cache_control = cache_control_header.asString(); if (max_age_from_cache_control(cache_control, &max_age)) { - F64 now = LLFrameTimer::getTotalSeconds(); *expires = now + (F64)max_age; - return true; + fromCacheControl = true; } } - return false; + LL_DEBUGS("AvNameCache") + << ( fromCacheControl ? "expires based on cache control " : "default expiration " ) + << "in " << *expires - now << " seconds" + << LL_ENDL; + + return fromCacheControl; } diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 8f21ace96a..59c1329ffa 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -82,6 +82,9 @@ namespace LLAvatarNameCache void erase(const LLUUID& agent_id); + /// Provide some fallback for agents that return errors + void handleAgentError(const LLUUID& agent_id); + // Force a re-fetch of the most recent data, but keep the current // data in cache void fetch(const LLUUID& agent_id); diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp index 4ab6bd2438..479efabb5f 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -556,35 +556,43 @@ std::string LLCacheName::buildUsername(const std::string& full_name) //static std::string LLCacheName::buildLegacyName(const std::string& complete_name) { - // regexp doesn't play nice with unicode, chop off the display name + //boost::regexp was showing up in the crashreporter, so doing + //painfully manual parsing using substr. LF S32 open_paren = complete_name.rfind(" ("); + S32 close_paren = complete_name.rfind(')'); - if (open_paren == std::string::npos) + if (open_paren != std::string::npos && + close_paren == complete_name.length()-1) { - return complete_name; - } + S32 length = close_paren - open_paren - 2; + std::string legacy_name = complete_name.substr(open_paren+2, length); + + if (legacy_name.length() > 0) + { + std::string cap_letter = legacy_name.substr(0, 1); + LLStringUtil::toUpper(cap_letter); + legacy_name = cap_letter + legacy_name.substr(1); + + S32 separator = legacy_name.find('.'); - std::string username = complete_name.substr(open_paren); - boost::regex complete_name_regex("( \\()([a-z0-9]+)(.[a-z]+)*(\\))"); - boost::match_results<std::string::const_iterator> name_results; - if (!boost::regex_match(username, name_results, complete_name_regex)) return complete_name; + if (separator != std::string::npos) + { + std::string last_name = legacy_name.substr(separator+1); + legacy_name = legacy_name.substr(0, separator); - std::string legacy_name = name_results[2]; - // capitalize the first letter - std::string cap_letter = legacy_name.substr(0, 1); - LLStringUtil::toUpper(cap_letter); - legacy_name = cap_letter + legacy_name.substr(1); + if (last_name.length() > 0) + { + cap_letter = last_name.substr(0, 1); + LLStringUtil::toUpper(cap_letter); + legacy_name = legacy_name + " " + cap_letter + last_name.substr(1); + } + } - if (name_results[3].matched) - { - std::string last_name = name_results[3]; - std::string cap_letter = last_name.substr(1, 1); - LLStringUtil::toUpper(cap_letter); - last_name = cap_letter + last_name.substr(2); - legacy_name = legacy_name + " " + last_name; + return legacy_name; + } } - return legacy_name; + return complete_name; } // This is a little bit kludgy. LLCacheNameCallback is a slot instead of a function pointer. @@ -967,6 +975,10 @@ void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool isGroup) if (entry->mLastName.empty()) { full_name = cleanFullName(entry->mFirstName); + + //fix what we are putting in the cache + entry->mFirstName = full_name; + entry->mLastName = "Resident"; } else { diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt index d3a73058c4..1dc05e0b20 100644 --- a/indra/llplugin/CMakeLists.txt +++ b/indra/llplugin/CMakeLists.txt @@ -20,6 +20,7 @@ include_directories( ${LLRENDER_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} + ${LLQTWEBKIT_INCLUDE_DIR} ) set(llplugin_SOURCE_FILES diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 69ed0fb09c..595c470a19 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -160,7 +160,7 @@ void LLPluginClassMedia::idle(void) mPlugin->idle(); } - if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked())) + if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked()) || (mOwner == NULL)) { // Can't process a size change at this time } @@ -522,7 +522,15 @@ bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifie } break; } - + +#if LL_DARWIN + if(modifiers & MASK_ALT) + { + // Option-key modified characters should be handled by the unicode input path instead of this one. + result = false; + } +#endif + if(result) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "key_event"); @@ -674,7 +682,21 @@ void LLPluginClassMedia::sendPickFileResponse(const std::string &file) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file_response"); message.setValue("file", file); - if(mPlugin->isBlocked()) + if(mPlugin && mPlugin->isBlocked()) + { + // If the plugin sent a blocking pick-file request, the response should unblock it. + message.setValueBoolean("blocking_response", true); + } + sendMessage(message); +} + +void LLPluginClassMedia::sendAuthResponse(bool ok, const std::string &username, const std::string &password) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "auth_response"); + message.setValueBoolean("ok", ok); + message.setValue("username", username); + message.setValue("password", password); + if(mPlugin && mPlugin->isBlocked()) { // If the plugin sent a blocking pick-file request, the response should unblock it. message.setValueBoolean("blocking_response", true); @@ -947,6 +969,12 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) { mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_PICK_FILE_REQUEST); } + else if(message_name == "auth_request") + { + mAuthURL = message.getValue("url"); + mAuthRealm = message.getValue("realm"); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_AUTH_REQUEST); + } else { LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; @@ -1019,6 +1047,15 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_GEOMETRY_CHANGE); } + else if(message_name == "link_hovered") + { + // text is not currently used -- the tooltip hover text is taken from the "title". + mHoverLink = message.getValue("link"); + mHoverText = message.getValue("title"); + // message.getValue("text"); + + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_LINK_HOVERED); + } else { LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; @@ -1192,6 +1229,20 @@ void LLPluginClassMedia::proxyWindowClosed(const std::string &uuid) sendMessage(message); } +void LLPluginClassMedia::ignore_ssl_cert_errors(bool ignore) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "ignore_ssl_cert_errors"); + message.setValueBoolean("ignore", ignore); + sendMessage(message); +} + +void LLPluginClassMedia::addCertificateFilePath(const std::string& path) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "add_certificate_file_path"); + message.setValue("path", path); + sendMessage(message); +} + void LLPluginClassMedia::crashPlugin() { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "crash"); diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 9cb67fe909..c826e13c40 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -85,6 +85,8 @@ public: void setBackgroundColor(LLColor4 color) { mBackgroundColor = color; }; + void setOwner(LLPluginClassMediaOwner *owner) { mOwner = owner; }; + // Returns true if all of the texture parameters (depth, format, size, and texture size) are set up and consistent. // This will initially be false, and will also be false for some time after setSize while the resize is processed. // Note that if this returns true, it is safe to use all the get() functions above without checking for invalid return values @@ -159,6 +161,8 @@ public: void sendPickFileResponse(const std::string &file); + void sendAuthResponse(bool ok, const std::string &username, const std::string &password); + // Valid after a MEDIA_EVENT_CURSOR_CHANGED event std::string getCursorName() const { return mCursorName; }; @@ -198,6 +202,8 @@ public: void setBrowserUserAgent(const std::string& user_agent); void proxyWindowOpened(const std::string &target, const std::string &uuid); void proxyWindowClosed(const std::string &uuid); + void ignore_ssl_cert_errors(bool ignore); + void addCertificateFilePath(const std::string& path); // This is valid after MEDIA_EVENT_NAVIGATE_BEGIN or MEDIA_EVENT_NAVIGATE_COMPLETE std::string getNavigateURI() const { return mNavigateURI; }; @@ -231,7 +237,15 @@ public: S32 getGeometryY() const { return mGeometryY; }; S32 getGeometryWidth() const { return mGeometryWidth; }; S32 getGeometryHeight() const { return mGeometryHeight; }; + + // These are valid during MEDIA_EVENT_AUTH_REQUEST + std::string getAuthURL() const { return mAuthURL; }; + std::string getAuthRealm() const { return mAuthRealm; }; + // These are valid during MEDIA_EVENT_LINK_HOVERED + std::string getHoverText() const { return mHoverText; }; + std::string getHoverLink() const { return mHoverLink; }; + std::string getMediaName() const { return mMediaName; }; std::string getMediaDescription() const { return mMediaDescription; }; @@ -369,6 +383,10 @@ protected: S32 mGeometryY; S32 mGeometryWidth; S32 mGeometryHeight; + std::string mAuthURL; + std::string mAuthRealm; + std::string mHoverText; + std::string mHoverLink; ///////////////////////////////////////// // media_time class diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h index c9efff216c..42e93cc6d7 100644 --- a/indra/llplugin/llpluginclassmediaowner.h +++ b/indra/llplugin/llpluginclassmediaowner.h @@ -59,7 +59,11 @@ public: MEDIA_EVENT_GEOMETRY_CHANGE, // The plugin requested its window geometry be changed (per the javascript window interface) MEDIA_EVENT_PLUGIN_FAILED_LAUNCH, // The plugin failed to launch - MEDIA_EVENT_PLUGIN_FAILED // The plugin died unexpectedly + MEDIA_EVENT_PLUGIN_FAILED, // The plugin died unexpectedly + + MEDIA_EVENT_AUTH_REQUEST, // The plugin wants to display an auth dialog + + MEDIA_EVENT_LINK_HOVERED // Got a "link hovered" event from the plugin } EMediaEvent; diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index c0edd92bc1..6ea63809f8 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -610,41 +610,46 @@ void LLGLManager::shutdownGL() void LLGLManager::initExtensions() { #if LL_MESA_HEADLESS -# if GL_ARB_multitexture +# ifdef GL_ARB_multitexture mHasMultitexture = TRUE; # else mHasMultitexture = FALSE; # endif -# if GL_ARB_texture_env_combine +# ifdef GL_ARB_texture_env_combine mHasARBEnvCombine = TRUE; # else mHasARBEnvCombine = FALSE; # endif -# if GL_ARB_texture_compression +# ifdef GL_ARB_texture_compression mHasCompressedTextures = TRUE; # else mHasCompressedTextures = FALSE; # endif -# if GL_ARB_vertex_buffer_object +# ifdef GL_ARB_vertex_buffer_object mHasVertexBufferObject = TRUE; # else mHasVertexBufferObject = FALSE; # endif -# if GL_EXT_framebuffer_object +# ifdef GL_EXT_framebuffer_object mHasFramebufferObject = TRUE; # else mHasFramebufferObject = FALSE; # endif -# if GL_EXT_framebuffer_multisample +# ifdef GL_EXT_framebuffer_multisample mHasFramebufferMultisample = TRUE; # else mHasFramebufferMultisample = FALSE; # endif -# if GL_ARB_draw_buffers +# ifdef GL_ARB_draw_buffers mHasDrawBuffers = TRUE; #else mHasDrawBuffers = FALSE; # endif +# if defined(GL_NV_depth_clamp) || defined(GL_ARB_depth_clamp) + mHasDepthClamp = TRUE; +#else + mHasDepthClamp = FALSE; +#endif # if GL_EXT_blend_func_separate mHasBlendFuncSeparate = TRUE; #else @@ -671,6 +676,7 @@ void LLGLManager::initExtensions() mHasCompressedTextures = glh_init_extensions("GL_ARB_texture_compression"); mHasOcclusionQuery = ExtensionExists("GL_ARB_occlusion_query", gGLHExts.mSysExts); mHasVertexBufferObject = ExtensionExists("GL_ARB_vertex_buffer_object", gGLHExts.mSysExts); + mHasDepthClamp = ExtensionExists("GL_ARB_depth_clamp", gGLHExts.mSysExts) || ExtensionExists("GL_NV_depth_clamp", gGLHExts.mSysExts); // mask out FBO support when packed_depth_stencil isn't there 'cause we need it for LLRenderTarget -Brad mHasFramebufferObject = ExtensionExists("GL_EXT_framebuffer_object", gGLHExts.mSysExts) && ExtensionExists("GL_EXT_packed_depth_stencil", gGLHExts.mSysExts); @@ -694,6 +700,7 @@ void LLGLManager::initExtensions() if (getenv("LL_GL_NOEXT")) { //mHasMultitexture = FALSE; // NEEDED! + mHasDepthClamp = FALSE; mHasARBEnvCombine = FALSE; mHasCompressedTextures = FALSE; mHasVertexBufferObject = FALSE; @@ -755,6 +762,7 @@ void LLGLManager::initExtensions() if (strchr(blacklist,'s')) mHasFramebufferMultisample = FALSE; if (strchr(blacklist,'t')) mHasTextureRectangle = FALSE; if (strchr(blacklist,'u')) mHasBlendFuncSeparate = FALSE;//S + if (strchr(blacklist,'v')) mHasDepthClamp = FALSE; } #endif // LL_LINUX || LL_SOLARIS @@ -1047,6 +1055,33 @@ void flush_glerror() glGetError(); } +//this function outputs gl error to the log file, does not crash the code. +void log_glerror() +{ + if (LL_UNLIKELY(!gGLManager.mInited)) + { + return ; + } + // Create or update texture to be used with this data + GLenum error; + error = glGetError(); + while (LL_UNLIKELY(error)) + { + GLubyte const * gl_error_msg = gluErrorString(error); + if (NULL != gl_error_msg) + { + llwarns << "GL Error: " << error << " GL Error String: " << gl_error_msg << llendl ; + } + else + { + // gluErrorString returns NULL for some extensions' error codes. + // you'll probably have to grep for the number in glext.h. + llwarns << "GL Error: UNKNOWN 0x" << std::hex << error << std::dec << llendl; + } + error = glGetError(); + } +} + void do_assert_glerror() { if (LL_UNLIKELY(!gGLManager.mInited)) @@ -2037,7 +2072,7 @@ void LLGLDepthTest::checkState() } } -LLGLClampToFarClip::LLGLClampToFarClip(glh::matrix4f P) +LLGLSquashToFarClip::LLGLSquashToFarClip(glh::matrix4f P) { for (U32 i = 0; i < 4; i++) { @@ -2050,7 +2085,7 @@ LLGLClampToFarClip::LLGLClampToFarClip(glh::matrix4f P) glMatrixMode(GL_MODELVIEW); } -LLGLClampToFarClip::~LLGLClampToFarClip() +LLGLSquashToFarClip::~LLGLSquashToFarClip() { glMatrixMode(GL_PROJECTION); glPopMatrix(); diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 5e8965c06a..85fab7a0f8 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -92,6 +92,7 @@ public: BOOL mHasOcclusionQuery; BOOL mHasPointParameters; BOOL mHasDrawBuffers; + BOOL mHasDepthClamp; BOOL mHasTextureRectangle; // Other extensions. @@ -157,6 +158,7 @@ void rotate_quat(LLQuaternion& rotation); void flush_glerror(); // Flush GL errors when we know we're handling them correctly. +void log_glerror(); void assert_glerror(); void clear_glerror(); @@ -315,11 +317,11 @@ private: leaves this class. Does not stack. */ -class LLGLClampToFarClip +class LLGLSquashToFarClip { public: - LLGLClampToFarClip(glh::matrix4f projection); - ~LLGLClampToFarClip(); + LLGLSquashToFarClip(glh::matrix4f projection); + ~LLGLSquashToFarClip(); }; /* diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h index 5a34b46d0c..576969b81a 100644 --- a/indra/llrender/llglheaders.h +++ b/indra/llrender/llglheaders.h @@ -829,5 +829,15 @@ extern void glGetBufferPointervARB (GLenum, GLenum, GLvoid* *); #endif // LL_MESA / LL_WINDOWS / LL_DARWIN +// Even when GL_ARB_depth_clamp is available in the driver, the (correct) +// headers, and therefore GL_DEPTH_CLAMP might not be defined. +// In that case GL_DEPTH_CLAMP_NV should be defined, but why not just +// use the known numeric. +// +// To avoid #ifdef's in the code. Just define this here. +#ifndef GL_DEPTH_CLAMP +// Probably (still) called GL_DEPTH_CLAMP_NV. +#define GL_DEPTH_CLAMP 0x864F +#endif #endif // LL_LLGLHEADERS_H diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 72dd283bc7..65940cb067 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1069,6 +1069,8 @@ BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_ checkTexSize(true) ; llcallstacks << fb_x << " : " << fb_y << " : " << x_pos << " : " << y_pos << " : " << width << " : " << height << " : " << (S32)mComponents << llcallstacksendl ; + + log_glerror() ; } glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height); diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index e26acd53a3..8eb160f4e7 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -431,6 +431,9 @@ void LLTexUnit::setTextureFilteringOption(LLTexUnit::eTextureFilterOptions optio if (gGL.mMaxAnisotropy < 1.f) { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gGL.mMaxAnisotropy); + + llinfos << "gGL.mMaxAnisotropy: " << gGL.mMaxAnisotropy << llendl ; + gGL.mMaxAnisotropy = llmax(1.f, gGL.mMaxAnisotropy) ; } glTexParameterf(sGLTextureType[mCurrTexType], GL_TEXTURE_MAX_ANISOTROPY_EXT, gGL.mMaxAnisotropy); } diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index de4501dd0f..02160b09c4 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -27,7 +27,7 @@ #include "linden_common.h" #include <boost/static_assert.hpp> - +#include "llsys.h" #include "llvertexbuffer.h" // #include "llrender.h" #include "llglheaders.h" @@ -854,6 +854,14 @@ U8* LLVertexBuffer::mapBuffer(S32 access) if (!mMappedData) { + log_glerror(); + + //check the availability of memory + U32 avail_phy_mem, avail_vir_mem; + LLMemoryInfo::getAvailableMemoryKB(avail_phy_mem, avail_vir_mem) ; + llinfos << "Available physical mwmory(KB): " << avail_phy_mem << llendl ; + llinfos << "Available virtual memory(KB): " << avail_vir_mem << llendl; + //-------------------- //print out more debug info before crash llinfos << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << llendl ; @@ -875,6 +883,8 @@ U8* LLVertexBuffer::mapBuffer(S32 access) if (!mMappedIndexData) { + log_glerror(); + GLint buff; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); if ((GLuint)buff != mGLIndices) diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index e98201ea63..33ab2e93b5 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -111,6 +111,7 @@ set(llui_SOURCE_FILES llviewmodel.cpp llview.cpp llviewquery.cpp + llwindowshade.cpp ) set(llui_HEADER_FILES @@ -159,6 +160,7 @@ set(llui_HEADER_FILES llnotificationslistener.h llnotificationsutil.h llnotificationtemplate.h + llnotificationvisibilityrule.h llpanel.h llprogressbar.h llradiogroup.h @@ -209,6 +211,7 @@ set(llui_HEADER_FILES llviewmodel.h llview.h llviewquery.h + llwindowshade.h ) set_source_files_properties(${llui_HEADER_FILES} diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 179b32098a..9e4849c58b 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -203,7 +203,8 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() S32 width = getRect().getWidth(); S32 height = getRect().getHeight(); - gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get(),true); + F32 alpha = getCurrentTransparency(); + gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get() % alpha,true); LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent()); bool collapsible = (parent && parent->getCollapsible()); @@ -456,8 +457,7 @@ BOOL LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask) { if(y >= (getRect().getHeight() - HEADER_HEIGHT) ) { - LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - header->setFocus(true); + mHeader->setFocus(true); changeOpenClose(getDisplayChildren()); //reset stored state @@ -509,10 +509,9 @@ void LLAccordionCtrlTab::setAccordionView(LLView* panel) std::string LLAccordionCtrlTab::getTitle() const { - LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - if (header) + if (mHeader) { - return header->getTitle(); + return mHeader->getTitle(); } else { @@ -522,57 +521,51 @@ std::string LLAccordionCtrlTab::getTitle() const void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl) { - LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - if (header) + if (mHeader) { - header->setTitle(title, hl); + mHeader->setTitle(title, hl); } } void LLAccordionCtrlTab::setTitleFontStyle(std::string style) { - LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - if (header) + if (mHeader) { - header->setTitleFontStyle(style); + mHeader->setTitleFontStyle(style); } } void LLAccordionCtrlTab::setTitleColor(LLUIColor color) { - LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - if (header) + if (mHeader) { - header->setTitleColor(color); + mHeader->setTitleColor(color); } } boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const focus_signal_t::slot_type& cb) { - LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - if (header) + if (mHeader) { - return header->setFocusReceivedCallback(cb); + return mHeader->setFocusReceivedCallback(cb); } return boost::signals2::connection(); } boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus_signal_t::slot_type& cb) { - LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - if (header) + if (mHeader) { - return header->setFocusLostCallback(cb); + return mHeader->setFocusLostCallback(cb); } return boost::signals2::connection(); } void LLAccordionCtrlTab::setSelected(bool is_selected) { - LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - if (header) + if (mHeader) { - header->setSelected(is_selected); + mHeader->setSelected(is_selected); } } @@ -776,8 +769,7 @@ S32 LLAccordionCtrlTab::notify(const LLSD& info) BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - if( !header->hasFocus() ) + if( !mHeader->hasFocus() ) return LLUICtrl::handleKey(key, mask, called_from_parent); if ( (key == KEY_RETURN )&& mask == MASK_NONE) @@ -830,15 +822,19 @@ BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent) void LLAccordionCtrlTab::showAndFocusHeader() { - LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME); - header->setFocus(true); - header->setSelected(mSelectionEnabled); + mHeader->setFocus(true); + mHeader->setSelected(mSelectionEnabled); LLRect screen_rc; - LLRect selected_rc = header->getRect(); + LLRect selected_rc = mHeader->getRect(); localRectToScreen(selected_rc, &screen_rc); - notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); + // This call to notifyParent() is intended to deliver "scrollToShowRect" command + // to the parent LLAccordionCtrl so by calling it from the direct parent of this + // accordion tab (assuming that the parent is an LLAccordionCtrl) the calls chain + // is shortened and messages from inside the collapsed tabs are avoided. + // See STORM-536. + getParent()->notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); } void LLAccordionCtrlTab::storeOpenCloseState() { diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 65ef3e5f8f..45ceaff696 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -98,7 +98,8 @@ LLButton::Params::Params() is_toggle("is_toggle", false), scale_image("scale_image", true), hover_glow_amount("hover_glow_amount"), - commit_on_return("commit_on_return", true) + commit_on_return("commit_on_return", true), + use_draw_context_alpha("use_draw_context_alpha", true) { addSynonym(is_toggle, "toggle"); held_down_delay.seconds = 0.5f; @@ -158,7 +159,8 @@ LLButton::LLButton(const LLButton::Params& p) mLastDrawCharsCount(0), mMouseDownSignal(NULL), mMouseUpSignal(NULL), - mHeldDownSignal(NULL) + mHeldDownSignal(NULL), + mUseDrawContextAlpha(p.use_draw_context_alpha) { static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0); @@ -539,7 +541,7 @@ BOOL LLButton::handleHover(S32 x, S32 y, MASK mask) // virtual void LLButton::draw() { - F32 alpha = getDrawContext().mAlpha; + F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); bool flash = FALSE; static LLUICachedControl<F32> button_flash_rate("ButtonFlashRate", 0); static LLUICachedControl<S32> button_flash_count("ButtonFlashCount", 0); diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 2d5fefa78c..16aa49b653 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -124,6 +124,8 @@ public: Optional<F32> hover_glow_amount; Optional<TimeIntervalParam> held_down_delay; + Optional<bool> use_draw_context_alpha; + Params(); }; @@ -338,6 +340,8 @@ private: S32 mImageOverlayTopPad; S32 mImageOverlayBottomPad; + bool mUseDrawContextAlpha; + /* * Space between image_overlay and label */ diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index bbd8db2645..4fe444c1a4 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -88,27 +88,19 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p) tbparams.font(p.font); } mLabel = LLUICtrlFactory::create<LLTextBox> (tbparams); + mLabel->reshapeToFitText(); addChild(mLabel); - S32 text_width = mLabel->getTextBoundingRect().getWidth(); - S32 text_height = llround(mFont->getLineHeight()); - LLRect label_rect; - label_rect.setOriginAndSize( - llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing, - llcheckboxctrl_vpad + 1, // padding to get better alignment - text_width + llcheckboxctrl_hpad, - text_height ); - mLabel->setShape(label_rect); - + LLRect label_rect = mLabel->getRect(); // Button // Note: button cover the label by extending all the way to the right. - LLRect btn_rect; + LLRect btn_rect = p.check_button.rect(); btn_rect.setOriginAndSize( - llcheckboxctrl_hpad, - llcheckboxctrl_vpad, - llcheckboxctrl_btn_size + llcheckboxctrl_spacing + text_width + llcheckboxctrl_hpad, - llmax( text_height, llcheckboxctrl_btn_size() ) + llcheckboxctrl_vpad); + btn_rect.mLeft, + btn_rect.mBottom, + llmax(btn_rect.mRight, label_rect.mRight - btn_rect.mLeft), + llmax( label_rect.getHeight(), btn_rect.mTop)); std::string active_true_id, active_false_id; std::string inactive_true_id, inactive_false_id; @@ -174,31 +166,20 @@ void LLCheckBoxCtrl::clear() void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) { - //stretch or shrink bounding rectangle of label when rebuilding UI at new scale - static LLUICachedControl<S32> llcheckboxctrl_spacing ("UICheckboxctrlSpacing", 0); - static LLUICachedControl<S32> llcheckboxctrl_hpad ("UICheckboxctrlHPad", 0); - static LLUICachedControl<S32> llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0); - static LLUICachedControl<S32> llcheckboxctrl_btn_size ("UICheckboxctrlBtnSize", 0); - S32 text_width = mLabel->getTextBoundingRect().getWidth(); - S32 text_height = llround(mFont->getLineHeight()); - LLRect label_rect; - label_rect.setOriginAndSize( - llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing, - llcheckboxctrl_vpad, - text_width, - text_height ); - mLabel->setShape(label_rect); - - LLRect btn_rect; + mLabel->reshapeToFitText(); + + LLRect label_rect = mLabel->getRect(); + + // Button + // Note: button cover the label by extending all the way to the right. + LLRect btn_rect = mButton->getRect(); btn_rect.setOriginAndSize( - llcheckboxctrl_hpad, - llcheckboxctrl_vpad, - llcheckboxctrl_btn_size + llcheckboxctrl_spacing + text_width, - llmax( text_height, llcheckboxctrl_btn_size() ) ); - mButton->setShape( btn_rect ); - - LLUICtrl::reshape(width, height, called_from_parent); + btn_rect.mLeft, + btn_rect.mBottom, + llmax(btn_rect.getWidth(), label_rect.mRight - btn_rect.mLeft), + llmax(label_rect.mTop - btn_rect.mBottom, btn_rect.getHeight())); + mButton->setShape(btn_rect); } //virtual diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 2dabbc7767..8b6a73af56 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -94,6 +94,7 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p) mMaxChars(p.max_chars), mPrearrangeCallback(p.prearrange_callback()), mTextEntryCallback(p.text_entry_callback()), + mTextChangedCallback(p.text_changed_callback()), mListPosition(p.list_position), mLastSelectedIndex(-1), mLabel(p.label) @@ -769,7 +770,8 @@ BOOL LLComboBox::handleKeyHere(KEY key, MASK mask) return FALSE; } // if selection has changed, pop open list - else if (mList->getLastSelectedItem() != last_selected_item) + else if (mList->getLastSelectedItem() != last_selected_item || + (key == KEY_DOWN || key == KEY_UP) && !mList->isEmpty()) { showList(); } @@ -833,6 +835,10 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor) mList->deselectAllItems(); mLastSelectedIndex = -1; } + if (mTextChangedCallback != NULL) + { + (mTextChangedCallback)(line_editor, LLSD()); + } return; } @@ -877,6 +883,10 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor) // RN: presumably text entry updateSelection(); } + if (mTextChangedCallback != NULL) + { + (mTextChangedCallback)(line_editor, LLSD()); + } } void LLComboBox::updateSelection() diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index 5f0e4a6843..74d64269bd 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -73,7 +73,8 @@ public: allow_new_values; Optional<S32> max_chars; Optional<commit_callback_t> prearrange_callback, - text_entry_callback; + text_entry_callback, + text_changed_callback; Optional<EPreferredPosition, PreferredPositionValues> list_position; @@ -190,6 +191,7 @@ public: void setPrearrangeCallback( commit_callback_t cb ) { mPrearrangeCallback = cb; } void setTextEntryCallback( commit_callback_t cb ) { mTextEntryCallback = cb; } + void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; } void setButtonVisible(BOOL visible); @@ -220,6 +222,7 @@ private: BOOL mTextEntryTentative; commit_callback_t mPrearrangeCallback; commit_callback_t mTextEntryCallback; + commit_callback_t mTextChangedCallback; commit_callback_t mSelectionCallback; boost::signals2::connection mTopLostSignalConnection; S32 mLastSelectedIndex; diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp index d48674f306..f6f5a0beb3 100644 --- a/indra/llui/lldockcontrol.cpp +++ b/indra/llui/lldockcontrol.cpp @@ -220,10 +220,15 @@ void LLDockControl::moveDockable() case TOP: x = dockRect.getCenterX() - dockableRect.getWidth() / 2; y = dockRect.mTop + dockableRect.getHeight(); - // unique docking used with dock tongue, so add tongue height o the Y coordinate + // unique docking used with dock tongue, so add tongue height to the Y coordinate if (use_tongue) { y += mDockTongue->getHeight(); + + if ( y > rootRect.mTop) + { + y = rootRect.mTop; + } } // check is dockable inside root view rect @@ -257,7 +262,7 @@ void LLDockControl::moveDockable() case BOTTOM: x = dockRect.getCenterX() - dockableRect.getWidth() / 2; y = dockRect.mBottom; - // unique docking used with dock tongue, so add tongue height o the Y coordinate + // unique docking used with dock tongue, so add tongue height to the Y coordinate if (use_tongue) { y -= mDockTongue->getHeight(); @@ -292,9 +297,21 @@ void LLDockControl::moveDockable() break; } - // move dockable - dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), - dockableRect.getHeight()); + S32 max_available_height = rootRect.getHeight() - mDockTongueY - mDockTongue->getHeight(); + + // A floater should be shrunk so it doesn't cover a part of its docking tongue and + // there is a space between a dockable floater and a control to which it is docked. + if (use_tongue && dockableRect.getHeight() >= max_available_height) + { + dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), max_available_height); + mDockableFloater->reshape(dockableRect.getWidth(), dockableRect.getHeight()); + } + else + { + // move dockable + dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), + dockableRect.getHeight()); + } LLRect localDocableParentRect; mDockableFloater->getParent()->screenRectToLocal(dockableRect, &localDocableParentRect); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index b758070419..d30697e178 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1,4 +1,5 @@ /** + * @file llfloater.cpp * @brief LLFloater base class * @@ -61,7 +62,6 @@ // use this to control "jumping" behavior when Ctrl-Tabbing const S32 TABBED_FLOATER_OFFSET = 0; - std::string LLFloater::sButtonNames[BUTTON_COUNT] = { "llfloater_close_btn", //BUTTON_CLOSE @@ -200,6 +200,21 @@ void LLFloater::initClass() { sButtonToolTips[i] = LLTrans::getString( sButtonToolTipsIndex[i] ); } + + LLControlVariable* ctrl = LLUI::sSettingGroups["config"]->getControl("ActiveFloaterTransparency").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency)); + updateActiveFloaterTransparency(); + } + + ctrl = LLUI::sSettingGroups["config"]->getControl("InactiveFloaterTransparency").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency)); + updateInactiveFloaterTransparency(); + } + } // defaults for floater param block pulled from widgets/floater.xml @@ -207,7 +222,7 @@ static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFl LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) : LLPanel(), // intentionally do not pass params here, see initFromParams - mDragHandle(NULL), + mDragHandle(NULL), mTitle(p.title), mShortTitle(p.short_title), mSingleInstance(p.single_instance), @@ -347,6 +362,18 @@ void LLFloater::layoutDragHandle() updateTitleButtons(); } +// static +void LLFloater::updateActiveFloaterTransparency() +{ + sActiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency"); +} + +// static +void LLFloater::updateInactiveFloaterTransparency() +{ + sInactiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency"); +} + void LLFloater::addResizeCtrls() { // Resize bars (sides) @@ -1163,6 +1190,7 @@ void LLFloater::setFocus( BOOL b ) last_focus->setFocus(TRUE); } } + updateTransparency(b ? TT_ACTIVE : TT_INACTIVE); } // virtual @@ -1435,6 +1463,9 @@ 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); + + // Make sure to set the appropriate transparency type (STORM-732). + updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE); } } @@ -1622,7 +1653,8 @@ void LLFloater::onClickCloseBtn() // virtual void LLFloater::draw() { - F32 alpha = getDrawContext().mAlpha; + const F32 alpha = getCurrentTransparency(); + // draw background if( isBackgroundVisible() ) { @@ -1720,7 +1752,6 @@ void LLFloater::draw() void LLFloater::drawShadow(LLPanel* panel) { - F32 alpha = panel->getDrawContext().mAlpha; S32 left = LLPANEL_BORDER_WIDTH; S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH; S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH; @@ -1737,10 +1768,32 @@ void LLFloater::drawShadow(LLPanel* panel) shadow_color.mV[VALPHA] *= 0.5f; } gl_drop_shadow(left, top, right, bottom, - shadow_color % alpha, + shadow_color % getCurrentTransparency(), llround(shadow_offset)); } +void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type) +{ + child_list_t children = *view->getChildList(); + child_list_t::iterator it = children.begin(); + + LLUICtrl* ctrl = dynamic_cast<LLUICtrl*>(view); + if (ctrl) + { + ctrl->setTransparencyType(transparency_type); + } + + for(; it != children.end(); ++it) + { + updateTransparency(*it, transparency_type); + } +} + +void LLFloater::updateTransparency(ETypeTransparency transparency_type) +{ + updateTransparency(this, transparency_type); +} + void LLFloater::setCanMinimize(BOOL can_minimize) { // if removing minimize/restore button programmatically, @@ -2857,7 +2910,9 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str params.from_xui = true; applyXUILayout(params, parent); initFromParams(params); - + // chrome floaters don't take focus at all + setFocusRoot(!getIsChrome()); + initFloater(params); LLMultiFloater* last_host = LLFloater::getFloaterHost(); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 32d03f9f83..bb96272d02 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -284,6 +284,8 @@ public: static void setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; } static LLMultiFloater* getFloaterHost() {return sHostp; } + + void updateTransparency(ETypeTransparency transparency_type); protected: @@ -341,6 +343,10 @@ private: void addDragHandle(); void layoutDragHandle(); // repair layout + static void updateActiveFloaterTransparency(); + static void updateInactiveFloaterTransparency(); + void updateTransparency(LLView* view, ETypeTransparency transparency_type); + public: // Called when floater is opened, passes mKey // Public so external views or floaters can watch for this floater opening diff --git a/indra/llui/llhandle.h b/indra/llui/llhandle.h index a43f095d67..8c000eee48 100644 --- a/indra/llui/llhandle.h +++ b/indra/llui/llhandle.h @@ -61,13 +61,6 @@ public: return *this; } - template<typename Subclass> - LLHandle<T>& operator =(const LLHandle<Subclass>& other) - { - mTombStone = other.mTombStone; - return *this; - } - bool isDead() const { return mTombStone->getTarget() == NULL; @@ -99,7 +92,6 @@ public: { return lhs.mTombStone > rhs.mTombStone; } -protected: protected: LLPointer<LLTombStone<T> > mTombStone; diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 627957061d..47f2cfaf89 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -41,6 +41,7 @@ static LLDefaultChildRegistry::Register<LLIconCtrl> r("icon"); LLIconCtrl::Params::Params() : image("image_name"), color("color"), + use_draw_context_alpha("use_draw_context_alpha", true), scale_image("scale_image") { tab_stop = false; @@ -51,6 +52,7 @@ LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p) : LLUICtrl(p), mColor(p.color()), mImagep(p.image), + mUseDrawContextAlpha(p.use_draw_context_alpha), mPriority(0), mDrawWidth(0), mDrawHeight(0) @@ -71,7 +73,8 @@ void LLIconCtrl::draw() { if( mImagep.notNull() ) { - mImagep->draw(getLocalRect(), mColor.get() % getDrawContext().mAlpha ); + const F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); + mImagep->draw(getLocalRect(), mColor.get() % alpha ); } LLUICtrl::draw(); diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index 79a8b0fb28..e9bdab2d47 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -48,6 +48,7 @@ public: { Optional<LLUIImage*> image; Optional<LLUIColor> color; + Optional<bool> use_draw_context_alpha; Ignored scale_image; Params(); }; @@ -79,6 +80,10 @@ protected: S32 mDrawWidth ; S32 mDrawHeight ; + // If set to true (default), use the draw context transparency. + // If false, will use transparency returned by getCurrentTransparency(). See STORM-698. + bool mUseDrawContextAlpha; + private: LLUIColor mColor; LLPointer<LLUIImage> mImagep; diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 940c7e7e18..19ac4c58a8 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -38,6 +38,12 @@ static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack"); static LLLayoutStack::LayoutStackRegistry::Register<LLLayoutPanel> register_layout_panel("layout_panel"); +void LLLayoutStack::OrientationNames::declareValues() +{ + declare("horizontal", HORIZONTAL); + declare("vertical", VERTICAL); +} + // // LLLayoutPanel // @@ -47,47 +53,47 @@ LLLayoutPanel::LLLayoutPanel(const Params& p) mMaxDim(p.max_dim), mAutoResize(p.auto_resize), mUserResize(p.user_resize), - mCollapsed(FALSE), - mCollapseAmt(0.f), - mVisibleAmt(1.f), // default to fully visible - mResizeBar(NULL) - { + mCollapsed(FALSE), + mCollapseAmt(0.f), + mVisibleAmt(1.f), // default to fully visible + mResizeBar(NULL) +{ // panels initialized as hidden should not start out partially visible if (!getVisible()) - { + { mVisibleAmt = 0.f; - } - } + } +} void LLLayoutPanel::initFromParams(const Params& p) - { +{ LLPanel::initFromParams(p); setFollowsNone(); - } +} LLLayoutPanel::~LLLayoutPanel() - { - // probably not necessary, but... - delete mResizeBar; - mResizeBar = NULL; - } +{ + // probably not necessary, but... + delete mResizeBar; + mResizeBar = NULL; +} F32 LLLayoutPanel::getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation) - { +{ if (orientation == LLLayoutStack::HORIZONTAL) - { - F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth())); - return mVisibleAmt * collapse_amt; - } - else + { + F32 collapse_amt = + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth())); + return mVisibleAmt * collapse_amt; + } + else { F32 collapse_amt = clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinDim / (F32)llmax(1, getRect().getHeight()))); return mVisibleAmt * collapse_amt; - } } +} // // LLLayoutStack @@ -97,6 +103,8 @@ LLLayoutStack::Params::Params() : orientation("orientation"), animate("animate", true), clip("clip", true), + open_time_constant("open_time_constant", 0.02f), + close_time_constant("close_time_constant", 0.03f), border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0)) { name="stack"; @@ -107,10 +115,12 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p) mMinWidth(0), mMinHeight(0), mPanelSpacing(p.border_size), - mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL), + mOrientation(p.orientation), mAnimate(p.animate), mAnimatedThisFrame(false), - mClip(p.clip) + mClip(p.clip), + mOpenTimeConstant(p.open_time_constant), + mCloseTimeConstant(p.close_time_constant) {} LLLayoutStack::~LLLayoutStack() @@ -303,9 +313,6 @@ void LLLayoutStack::updateLayout(BOOL force_resize) S32 total_width = 0; S32 total_height = 0; - const F32 ANIM_OPEN_TIME = 0.02f; - const F32 ANIM_CLOSE_TIME = 0.03f; - e_panel_list_t::iterator panel_it; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { @@ -316,7 +323,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME)); + (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant)); if ((*panel_it)->mVisibleAmt > 0.99f) { (*panel_it)->mVisibleAmt = 1.f; @@ -334,7 +341,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); + (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); if ((*panel_it)->mVisibleAmt < 0.001f) { (*panel_it)->mVisibleAmt = 0.f; @@ -349,11 +356,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) if ((*panel_it)->mCollapsed) { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); + (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); } else { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); + (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); } if (mOrientation == HORIZONTAL) diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index e19ef403ef..4ac8ef0ee9 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -37,27 +37,35 @@ class LLLayoutPanel; class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack> { public: + typedef enum e_layout_orientation + { + HORIZONTAL, + VERTICAL + } ELayoutOrientation; + + struct OrientationNames + : public LLInitParam::TypeValuesHelper<ELayoutOrientation, OrientationNames> + { + static void declareValues(); + }; + struct LayoutStackRegistry : public LLChildRegistry<LayoutStackRegistry> {}; struct Params : public LLInitParam::Block<Params, LLView::Params> { - Mandatory<std::string> orientation; + Mandatory<ELayoutOrientation, OrientationNames> orientation; Optional<S32> border_size; Optional<bool> animate, clip; + Optional<F32> open_time_constant, + close_time_constant; Params(); }; typedef LayoutStackRegistry child_registry_t; - typedef enum e_layout_orientation - { - HORIZONTAL, - VERTICAL - } ELayoutOrientation; - virtual ~LLLayoutStack(); /*virtual*/ void draw(); @@ -137,6 +145,8 @@ private: bool mAnimatedThisFrame; bool mAnimate; bool mClip; + F32 mOpenTimeConstant; + F32 mCloseTimeConstant; }; // end class LLLayoutStack class LLLayoutPanel : public LLPanel @@ -167,6 +177,9 @@ public: ~LLLayoutPanel(); void initFromParams(const Params& p); + void setMinDim(S32 value) { mMinDim = value; } + void setMaxDim(S32 value) { mMaxDim = value; } + protected: LLLayoutPanel(const Params& p) ; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 5f5fe851bb..7e348656a9 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -88,6 +88,7 @@ LLLineEditor::Params::Params() revert_on_esc("revert_on_esc", true), commit_on_focus_lost("commit_on_focus_lost", true), ignore_tab("ignore_tab", true), + is_password("is_password", false), cursor_color("cursor_color"), text_color("text_color"), text_readonly_color("text_readonly_color"), @@ -129,7 +130,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) mBorderThickness( 0 ), mIgnoreArrowKeys( FALSE ), mIgnoreTab( p.ignore_tab ), - mDrawAsterixes( FALSE ), + mDrawAsterixes( p.is_password ), mSelectAllonFocusReceived( p.select_on_focus ), mPassDelete(FALSE), mReadOnly(FALSE), @@ -1304,7 +1305,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) // handle ctrl-uparrow if we have a history enabled line editor. case KEY_UP: - if( mHaveHistory && ( MASK_CONTROL == mask ) ) + if( mHaveHistory && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) ) { if( mCurrentHistoryLine > mLineHistory.begin() ) { @@ -1319,9 +1320,9 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask) } break; - // handle ctrl-downarrow if we have a history enabled line editor + // handle [ctrl]-downarrow if we have a history enabled line editor case KEY_DOWN: - if( mHaveHistory && ( MASK_CONTROL == mask ) ) + if( mHaveHistory && ((mIgnoreArrowKeys == false) || ( MASK_CONTROL == mask )) ) { if( !mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1 ) { @@ -1529,8 +1530,11 @@ void LLLineEditor::drawBackground() { image = mBgImage; } + + if (!image) return; - F32 alpha = getDrawContext().mAlpha; + F32 alpha = getCurrentTransparency(); + // optionally draw programmatic border if (has_focus) { diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index a1aa6b71c6..723423a5b9 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -85,7 +85,8 @@ public: Optional<bool> select_on_focus, revert_on_esc, commit_on_focus_lost, - ignore_tab; + ignore_tab, + is_password; // colors Optional<LLUIColor> cursor_color, @@ -336,7 +337,7 @@ protected: std::vector<S32> mPreeditPositions; LLPreeditor::standouts_t mPreeditStandouts; - LLHandle<LLView> mContextMenuHandle; + LLHandle<LLContextMenu> mContextMenuHandle; private: // Instances that by default point to the statics but can be overidden in XML. diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index 3df05f4d3f..eed0085273 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -29,7 +29,7 @@ #include "llmenubutton.h" // Linden library includes -#include "llmenugl.h" +#include "lltoggleablemenu.h" #include "llstring.h" #include "v4color.h" @@ -44,58 +44,77 @@ LLMenuButton::Params::Params() LLMenuButton::LLMenuButton(const LLMenuButton::Params& p) : LLButton(p), - mMenu(NULL), - mMenuVisibleLastFrame(false) + mIsMenuShown(false), + mMenuPosition(MP_BOTTOM_LEFT) { std::string menu_filename = p.menu_filename; if (!menu_filename.empty()) { - mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); - if (!mMenu) + LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + if (!menu) { llwarns << "Error loading menu_button menu" << llendl; + return; } + + menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); + + mMenuHandle = menu->getHandle(); + + updateMenuOrigin(); } } -void LLMenuButton::toggleMenu() +boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb ) { - if(!mMenu) - return; + return LLUICtrl::setMouseDownCallback(cb); +} - if (mMenu->getVisible() || mMenuVisibleLastFrame) - { - mMenu->setVisible(FALSE); - } - else +void LLMenuButton::hideMenu() +{ + if(mMenuHandle.isDead()) return; + + LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); + if (menu) { - LLRect rect = getRect(); - //mMenu->needsArrange(); //so it recalculates the visible elements - LLMenuGL::showPopup(getParent(), mMenu, rect.mLeft, rect.mBottom); + menu->setVisible(FALSE); } } - -void LLMenuButton::hideMenu() -{ - if(!mMenu) - return; - mMenu->setVisible(FALSE); +LLToggleableMenu* LLMenuButton::getMenu() +{ + return dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); } +void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/) +{ + if (!menu) return; + + mMenuHandle = menu->getHandle(); + mMenuPosition = position; + + menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); +} BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) { + if (mMenuHandle.isDead()) return FALSE; + if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) { + // *HACK: We emit the mouse down signal to fire the callback bound to the + // menu emerging event before actually displaying the menu. See STORM-263. + LLUICtrl::handleMouseDown(-1, -1, MASK_NONE); + toggleMenu(); return TRUE; } - if (mMenu && mMenu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) + LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); + if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) { - mMenu->setVisible(FALSE); + menu->setVisible(FALSE); return TRUE; } @@ -104,34 +123,84 @@ BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask) { - if (hasTabStop() && !getIsChrome()) - { - setFocus(TRUE); - } + LLButton::handleMouseDown(x, y, mask); toggleMenu(); - if (getSoundFlags() & MOUSE_DOWN) - { - make_ui_sound("UISndClick"); - } - return TRUE; } -void LLMenuButton::draw() +void LLMenuButton::toggleMenu() { - //we save this off so next frame when we try to close it by - //button click, and it hides menus before we get to it, we know - mMenuVisibleLastFrame = mMenu && mMenu->getVisible(); - - if (mMenuVisibleLastFrame) + if(mMenuHandle.isDead()) return; + + LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get()); + if (!menu) return; + + // Store the button rectangle to toggle menu visibility if a mouse event + // occurred inside or outside the button rect. + menu->setButtonRect(this); + + if (!menu->toggleVisibility() && mIsMenuShown) { + setForcePressedState(false); + mIsMenuShown = false; + } + else + { + menu->buildDrawLabels(); + menu->arrangeAndClear(); + menu->updateParent(LLMenuGL::sMenuContainer); + + updateMenuOrigin(); + + LLMenuGL::showPopup(getParent(), menu, mX, mY); + setForcePressedState(true); + mIsMenuShown = true; } +} - LLButton::draw(); +void LLMenuButton::updateMenuOrigin() +{ + if (mMenuHandle.isDead()) return; + + LLRect rect = getRect(); - setForcePressedState(false); + switch (mMenuPosition) + { + case MP_TOP_LEFT: + { + mX = rect.mLeft; + mY = rect.mTop + mMenuHandle.get()->getRect().getHeight(); + break; + } + case MP_TOP_RIGHT: + { + const LLRect& menu_rect = mMenuHandle.get()->getRect(); + mX = rect.mRight - menu_rect.getWidth(); + mY = rect.mTop + menu_rect.getHeight(); + break; + } + case MP_BOTTOM_LEFT: + { + mX = rect.mLeft; + mY = rect.mBottom; + break; + } + } } +void LLMenuButton::onMenuVisibilityChange(const LLSD& param) +{ + bool new_visibility = param["visibility"].asBoolean(); + bool is_closed_by_button_click = param["closed_by_button_click"].asBoolean(); + + // Reset the button "pressed" state only if the menu is shown by this particular + // menu button (not any other control) and is not being closed by a click on the button. + if (!new_visibility && !is_closed_by_button_click && mIsMenuShown) + { + setForcePressedState(false); + mIsMenuShown = false; + } +} diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h index 81ca0e047c..7b657595da 100644 --- a/indra/llui/llmenubutton.h +++ b/indra/llui/llmenubutton.h @@ -29,7 +29,7 @@ #include "llbutton.h" -class LLMenuGL; +class LLToggleableMenu; class LLMenuButton : public LLButton @@ -42,22 +42,42 @@ public: Optional<std::string> menu_filename; Params(); - }; + }; + + typedef enum e_menu_position + { + MP_TOP_LEFT, + MP_TOP_RIGHT, + MP_BOTTOM_LEFT + } EMenuPosition; - void toggleMenu(); - /*virtual*/ void draw(); + boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); + void hideMenu(); - LLMenuGL* getMenu() { return mMenu; } + + LLToggleableMenu* getMenu(); + void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT); + + void setMenuPosition(EMenuPosition position) { mMenuPosition = position; } protected: friend class LLUICtrlFactory; LLMenuButton(const Params&); + void toggleMenu(); + void updateMenuOrigin(); + + void onMenuVisibilityChange(const LLSD& param); + private: - LLMenuGL* mMenu; - bool mMenuVisibleLastFrame; + LLHandle<LLView> mMenuHandle; + bool mIsMenuShown; + EMenuPosition mMenuPosition; + S32 mX; + S32 mY; }; diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 6d590cf54e..6c0d47ef63 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -1462,7 +1462,7 @@ BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask) { BOOL branch_visible = getBranch()->getVisible(); BOOL handled = getBranch()->handleAcceleratorKey(key, mask); - if (handled && !branch_visible && getVisible()) + if (handled && !branch_visible && isInVisibleChain()) { // flash this menu entry because we triggered an invisible menu item LLMenuHolderGL::setActivatedItem(this); @@ -1848,89 +1848,104 @@ BOOL LLMenuGL::isOpen() } } -void LLMenuGL::scrollItemsUp() + + +bool LLMenuGL::scrollItems(EScrollingDirection direction) { - // Slowing down the items scrolling when arrow button is held + // Slowing down items scrolling when arrow button is held if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem) { mScrollItemsTimer.setTimerExpirySec(.033f); } else { - return; + return false; } - item_list_t::iterator cur_item_iter; - item_list_t::iterator prev_item_iter; - for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) + switch (direction) + { + case SD_UP: { - if( (*cur_item_iter) == mFirstVisibleItem) + item_list_t::iterator cur_item_iter; + item_list_t::iterator prev_item_iter; + for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) { - break; + if( (*cur_item_iter) == mFirstVisibleItem) + { + break; + } + if ((*cur_item_iter)->getVisible()) + { + prev_item_iter = cur_item_iter; + } } - if ((*cur_item_iter)->getVisible()) + + if ((*prev_item_iter)->getVisible()) { - prev_item_iter = cur_item_iter; + mFirstVisibleItem = *prev_item_iter; } + break; } - - if ((*prev_item_iter)->getVisible()) - { - mFirstVisibleItem = *prev_item_iter; - } - - mNeedsArrange = TRUE; - arrangeAndClear(); -} - -void LLMenuGL::scrollItemsDown() -{ - // Slowing down the items scrolling when arrow button is held - if (mScrollItemsTimer.hasExpired()) - { - mScrollItemsTimer.setTimerExpirySec(.033f); - } - else - { - return; - } - - if (NULL == mFirstVisibleItem) - { - mFirstVisibleItem = *mItems.begin(); - } - - item_list_t::iterator cur_item_iter; - - for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) + case SD_DOWN: { - if( (*cur_item_iter) == mFirstVisibleItem) + if (NULL == mFirstVisibleItem) { - break; + mFirstVisibleItem = *mItems.begin(); } - } - item_list_t::iterator next_item_iter; + item_list_t::iterator cur_item_iter; - if (cur_item_iter != mItems.end()) - { - for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++) + for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++) { - if( (*next_item_iter)->getVisible()) + if( (*cur_item_iter) == mFirstVisibleItem) { break; } } - - if (next_item_iter != mItems.end() && - (*next_item_iter)->getVisible()) + + item_list_t::iterator next_item_iter; + + if (cur_item_iter != mItems.end()) { - mFirstVisibleItem = *next_item_iter; + for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++) + { + if( (*next_item_iter)->getVisible()) + { + break; + } + } + + if (next_item_iter != mItems.end() && + (*next_item_iter)->getVisible()) + { + mFirstVisibleItem = *next_item_iter; + } } + break; } - + case SD_BEGIN: + { + mFirstVisibleItem = *mItems.begin(); + break; + } + case SD_END: + { + item_list_t::reverse_iterator first_visible_item_iter = mItems.rend(); + + // Advance by mMaxScrollableItems back from the end of the list + // to make the last item visible. + std::advance(first_visible_item_iter, mMaxScrollableItems); + mFirstVisibleItem = *first_visible_item_iter; + break; + } + default: + llwarns << "Unknown scrolling direction: " << direction << llendl; + } + mNeedsArrange = TRUE; arrangeAndClear(); + + return true; } // rearrange the child rects so they fit the shape of the menu. @@ -2162,7 +2177,7 @@ void LLMenuGL::arrange( void ) LLMenuScrollItem::Params item_params; item_params.name(ARROW_UP); item_params.arrow_type(LLMenuScrollItem::ARROW_UP); - item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItemsUp, this)); + item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_UP)); mArrowUpItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params); LLUICtrl::addChild(mArrowUpItem); @@ -2173,7 +2188,7 @@ void LLMenuGL::arrange( void ) LLMenuScrollItem::Params item_params; item_params.name(ARROW_DOWN); item_params.arrow_type(LLMenuScrollItem::ARROW_DOWN); - item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItemsDown, this)); + item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_DOWN)); mArrowDownItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params); LLUICtrl::addChild(mArrowDownItem); @@ -2596,6 +2611,7 @@ LLMenuItemGL* LLMenuGL::getHighlightedItem() LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled) { + if (mItems.empty()) return NULL; // highlighting first item on a torn off menu is the // same as giving focus to it if (!cur_item && getTornOff()) @@ -2603,14 +2619,8 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa ((LLFloater*)getParent())->setFocus(TRUE); } - item_list_t::iterator cur_item_iter; - for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); ++cur_item_iter) - { - if( (*cur_item_iter) == cur_item) - { - break; - } - } + // Current item position in the items list + item_list_t::iterator cur_item_iter = std::find(mItems.begin(), mItems.end(), cur_item); item_list_t::iterator next_item_iter; if (cur_item_iter == mItems.end()) @@ -2621,9 +2631,37 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa { next_item_iter = cur_item_iter; next_item_iter++; + + // First visible item position in the items list + item_list_t::iterator first_visible_item_iter = std::find(mItems.begin(), mItems.end(), mFirstVisibleItem); + if (next_item_iter == mItems.end()) { next_item_iter = mItems.begin(); + + // If current item is the last in the list, the menu is scrolled to the beginning + // and the first item is highlighted. + if (mScrollable && !scrollItems(SD_BEGIN)) + { + return NULL; + } + } + // If current item is the last visible, the menu is scrolled one item down + // and the next item is highlighted. + else if (mScrollable && + (U32)std::abs(std::distance(first_visible_item_iter, next_item_iter)) >= mMaxScrollableItems) + { + // Call highlightNextItem() recursively only if the menu was successfully scrolled down. + // If scroll timer hasn't expired yet the menu won't be scrolled and calling + // highlightNextItem() will result in an endless recursion. + if (scrollItems(SD_DOWN)) + { + return highlightNextItem(cur_item, skip_disabled); + } + else + { + return NULL; + } } } @@ -2674,6 +2712,8 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled) { + if (mItems.empty()) return NULL; + // highlighting first item on a torn off menu is the // same as giving focus to it if (!cur_item && getTornOff()) @@ -2681,14 +2721,8 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa ((LLFloater*)getParent())->setFocus(TRUE); } - item_list_t::reverse_iterator cur_item_iter; - for (cur_item_iter = mItems.rbegin(); cur_item_iter != mItems.rend(); ++cur_item_iter) - { - if( (*cur_item_iter) == cur_item) - { - break; - } - } + // Current item reverse position from the end of the list + item_list_t::reverse_iterator cur_item_iter = std::find(mItems.rbegin(), mItems.rend(), cur_item); item_list_t::reverse_iterator prev_item_iter; if (cur_item_iter == mItems.rend()) @@ -2699,9 +2733,37 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa { prev_item_iter = cur_item_iter; prev_item_iter++; + + // First visible item reverse position in the items list + item_list_t::reverse_iterator first_visible_item_iter = std::find(mItems.rbegin(), mItems.rend(), mFirstVisibleItem); + if (prev_item_iter == mItems.rend()) { prev_item_iter = mItems.rbegin(); + + // If current item is the first in the list, the menu is scrolled to the end + // and the last item is highlighted. + if (mScrollable && !scrollItems(SD_END)) + { + return NULL; + } + } + // If current item is the first visible, the menu is scrolled one item up + // and the previous item is highlighted. + else if (mScrollable && + std::distance(first_visible_item_iter, cur_item_iter) <= 0) + { + // Call highlightNextItem() only if the menu was successfully scrolled up. + // If scroll timer hasn't expired yet the menu won't be scrolled and calling + // highlightNextItem() will result in an endless recursion. + if (scrollItems(SD_UP)) + { + return highlightPrevItem(cur_item, skip_disabled); + } + else + { + return NULL; + } } } @@ -2872,12 +2934,12 @@ BOOL LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) if( clicks > 0 ) { while( clicks-- ) - scrollItemsDown(); + scrollItems(SD_DOWN); } else { while( clicks++ ) - scrollItemsUp(); + scrollItems(SD_UP); } return TRUE; @@ -2986,6 +3048,11 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size const S32 CURSOR_WIDTH = 12; + if(menu->getChildList()->empty()) + { + return; + } + // Save click point for detecting cursor moves before mouse-up. // Must be in local coords to compare with mouseUp events. // If the mouse doesn't move, the menu will stay open ala the Mac. @@ -3066,7 +3133,10 @@ BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) mAltKeyTrigger = FALSE; } - if(!result && (key == KEY_F10 && mask == MASK_CONTROL) && !gKeyboard->getKeyRepeated(key)) + if(!result + && (key == KEY_F10 && mask == MASK_CONTROL) + && !gKeyboard->getKeyRepeated(key) + && isInVisibleChain()) { if (getHighlightedItem()) { @@ -3449,8 +3519,10 @@ BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) else { //highlight first enabled one - pMenu->highlightNextItem(NULL); - handled = true; + if(pMenu->highlightNextItem(NULL)) + { + handled = true; + } } } } @@ -3683,9 +3755,7 @@ public: LLContextMenuBranch(const Params&); virtual ~LLContextMenuBranch() - { - delete mBranch; - } + {} // called to rebuild the draw label virtual void buildDrawLabel( void ); @@ -3693,21 +3763,21 @@ public: // onCommit() - do the primary funcationality of the menu item. virtual void onCommit( void ); - LLContextMenu* getBranch() { return mBranch; } + LLContextMenu* getBranch() { return mBranch.get(); } void setHighlight( BOOL highlight ); protected: void showSubMenu(); - LLContextMenu* mBranch; + LLHandle<LLContextMenu> mBranch; }; LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p) : LLMenuItemGL(p), - mBranch( p.branch ) + mBranch( p.branch()->getHandle() ) { - mBranch->hide(); - mBranch->setParentMenuItem(this); + mBranch.get()->hide(); + mBranch.get()->setParentMenuItem(this); } // called to rebuild the draw label @@ -3716,12 +3786,12 @@ void LLContextMenuBranch::buildDrawLabel( void ) { // default enablement is this -- if any of the subitems are // enabled, this item is enabled. JC - U32 sub_count = mBranch->getItemCount(); + U32 sub_count = mBranch.get()->getItemCount(); U32 i; BOOL any_enabled = FALSE; for (i = 0; i < sub_count; i++) { - LLMenuItemGL* item = mBranch->getItem(i); + LLMenuItemGL* item = mBranch.get()->getItem(i); item->buildDrawLabel(); if (item->getEnabled() && !item->getDrawTextDisabled() ) { @@ -3743,13 +3813,13 @@ void LLContextMenuBranch::buildDrawLabel( void ) void LLContextMenuBranch::showSubMenu() { - LLMenuItemGL* menu_item = mBranch->getParentMenuItem(); + LLMenuItemGL* menu_item = mBranch.get()->getParentMenuItem(); if (menu_item != NULL && menu_item->getVisible()) { S32 center_x; S32 center_y; localPointToScreen(getRect().getWidth(), getRect().getHeight() , ¢er_x, ¢er_y); - mBranch->show(center_x, center_y); + mBranch.get()->show(center_x, center_y); } } @@ -3769,7 +3839,7 @@ void LLContextMenuBranch::setHighlight( BOOL highlight ) } else { - mBranch->hide(); + mBranch.get()->hide(); } } @@ -3800,6 +3870,11 @@ void LLContextMenu::setVisible(BOOL visible) // Takes cursor position in screen space? void LLContextMenu::show(S32 x, S32 y) { + if (getChildList()->empty()) + { + // nothing to show, so abort + return; + } // Save click point for detecting cursor moves before mouse-up. // Must be in local coords to compare with mouseUp events. // If the mouse doesn't move, the menu will stay open ala the Mac. diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 19b738312e..7bde8e83ec 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -397,6 +397,15 @@ public: static const std::string ARROW_UP; static const std::string ARROW_DOWN; + // for scrollable menus + typedef enum e_scrolling_direction + { + SD_UP = 0, + SD_DOWN = 1, + SD_BEGIN = 2, + SD_END = 3 + } EScrollingDirection; + protected: LLMenuGL(const LLMenuGL::Params& p); friend class LLUICtrlFactory; @@ -503,8 +512,7 @@ public: S32 getShortcutPad() { return mShortcutPad; } - void scrollItemsUp(); - void scrollItemsDown(); + bool scrollItems(EScrollingDirection direction); BOOL isScrollable() const { return mScrollable; } static class LLMenuHolderGL* sMenuContainer; @@ -670,9 +678,12 @@ public: BOOL appendContextSubMenu(LLContextMenu *menu); + LLHandle<LLContextMenu> getHandle() { mHandle.bind(this); return mHandle; } + protected: - BOOL mHoveredAnyItem; - LLMenuItemGL* mHoverItem; + BOOL mHoveredAnyItem; + LLMenuItemGL* mHoverItem; + LLRootHandle<LLContextMenu> mHandle; }; diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index dd6c632d10..cd0f0e36b0 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -28,6 +28,7 @@ #include "llnotifications.h" #include "llnotificationtemplate.h" +#include "llnotificationvisibilityrule.h" #include "llavatarnamecache.h" #include "llinstantmessage.h" @@ -64,7 +65,7 @@ LLNotificationForm::FormElementBase::FormElementBase() LLNotificationForm::FormIgnore::FormIgnore() : text("text"), control("control"), - invert_control("invert_control", true), + invert_control("invert_control", false), save_option("save_option", false) {} @@ -81,6 +82,7 @@ LLNotificationForm::FormButton::FormButton() LLNotificationForm::FormInput::FormInput() : type("type"), + text("text"), max_length_chars("max_length_chars"), width("width", 0), value("value") @@ -137,12 +139,6 @@ private: bool filterIgnoredNotifications(LLNotificationPtr notification) { - // filter everything if we are to ignore ALL - if(LLNotifications::instance().getIgnoreAllNotifications()) - { - return false; - } - LLNotificationFormPtr form = notification->getForm(); // Check to see if the user wants to ignore this alert return !notification->getForm()->getIgnored(); @@ -177,6 +173,28 @@ bool handleIgnoredNotification(const LLSD& payload) return false; } +bool defaultResponse(const LLSD& payload) +{ + if (payload["sigtype"].asString() == "add") + { + LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); + if (pNotif) + { + // supply default response + pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON)); + } + } + return false; +} + +bool visibilityRuleMached(const LLSD& payload) +{ + // This is needed because LLNotifications::isVisibleByRules may have cancelled the notification. + // Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order. + return true; +} + + namespace LLNotificationFilters { // a sample filter @@ -194,7 +212,7 @@ LLNotificationForm::LLNotificationForm() LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p) : mIgnore(IGNORE_NO), - mInvertSetting(true) // ignore settings by default mean true=show, false=ignore + mInvertSetting(false) // ignore settings by default mean true=show, false=ignore { if (p.ignore.isProvided()) { @@ -219,7 +237,7 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica } else { - LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Ignore notification with this name", TRUE); + LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", TRUE); mIgnoreSetting = LLUI::sSettingGroups["ignores"]->getControl(name); } } @@ -357,15 +375,15 @@ LLControlVariablePtr LLNotificationForm::getIgnoreSetting() bool LLNotificationForm::getIgnored() { - bool ignored = false; + bool show = true; if (mIgnore != LLNotificationForm::IGNORE_NO && mIgnoreSetting) { - ignored = mIgnoreSetting->getValue().asBoolean(); - if (mInvertSetting) ignored = !ignored; + show = mIgnoreSetting->getValue().asBoolean(); + if (mInvertSetting) show = !show; } - return ignored; + return !show; } void LLNotificationForm::setIgnored(bool ignored) @@ -373,7 +391,7 @@ void LLNotificationForm::setIgnored(bool ignored) if (mIgnoreSetting) { if (mInvertSetting) ignored = !ignored; - mIgnoreSetting->setValue(ignored); + mIgnoreSetting->setValue(!ignored); } } @@ -404,12 +422,49 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par it != end_it; ++it) { - mUniqueContext.push_back(it->key); + mUniqueContext.push_back(it->value); + } + + lldebugs << "notification \"" << mName << "\": tag count is " << p.tags.size() << llendl; + + for(LLInitParam::ParamIterator<LLNotificationTemplate::Tag>::const_iterator it = p.tags.begin(), + end_it = p.tags.end(); + it != end_it; + ++it) + { + lldebugs << " tag \"" << std::string(it->value) << "\"" << llendl; + mTags.push_back(it->value); } mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form)); } +LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p) +{ + if (p.show.isChosen()) + { + mType = p.show.type; + mTag = p.show.tag; + mName = p.show.name; + mVisible = true; + } + else if (p.hide.isChosen()) + { + mType = p.hide.type; + mTag = p.hide.tag; + mName = p.hide.name; + mVisible = false; + } + else if (p.respond.isChosen()) + { + mType = p.respond.type; + mTag = p.respond.tag; + mName = p.respond.name; + mVisible = false; + mResponse = p.respond.response; + } +} + LLNotification::LLNotification(const LLNotification::Params& p) : mTimestamp(p.time_stamp), mSubstitutions(p.substitutions), @@ -679,6 +734,25 @@ bool LLNotification::hasUniquenessConstraints() const return (mTemplatep ? mTemplatep->mUnique : false); } +bool LLNotification::matchesTag(const std::string& tag) +{ + bool result = false; + + if(mTemplatep) + { + std::list<std::string>::iterator it; + for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++) + { + if((*it) == tag) + { + result = true; + break; + } + } + } + + return result; +} void LLNotification::setIgnored(bool ignore) { @@ -719,13 +793,19 @@ bool LLNotification::isEquivalentTo(LLNotificationPtr that) const { const LLSD& these_substitutions = this->getSubstitutions(); const LLSD& those_substitutions = that->getSubstitutions(); + const LLSD& this_payload = this->getPayload(); + const LLSD& that_payload = that->getPayload(); // highlander bit sez there can only be one of these for (std::vector<std::string>::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end(); it != end_it; ++it) { - if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString()) + // if templates differ in either substitution strings or payload with the given field name + // then they are considered inequivalent + // use of get() avoids converting the LLSD value to a map as the [] operator would + if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString() + || this_payload.get(*it).asString() != that_payload.get(*it).asString()) { return false; } @@ -1064,12 +1144,12 @@ std::string LLNotificationChannel::summarize() // LLNotifications implementation // --- LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything, - LLNotificationComparators::orderByUUID()), - mIgnoreAllNotifications(false) + LLNotificationComparators::orderByUUID()), + mIgnoreAllNotifications(false) { LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2)); - - mListener.reset(new LLNotificationsListener(*this)); + + mListener.reset(new LLNotificationsListener(*this)); } @@ -1184,6 +1264,7 @@ LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelN void LLNotifications::initSingleton() { loadTemplates(); + loadVisibilityRules(); createDefaultChannels(); } @@ -1191,15 +1272,19 @@ void LLNotifications::createDefaultChannels() { // now construct the various channels AFTER loading the notifications, // because the history channel is going to rewrite the stored notifications file - LLNotificationChannel::buildChannel("Expiration", "", + LLNotificationChannel::buildChannel("Enabled", "", + !boost::bind(&LLNotifications::getIgnoreAllNotifications, this)); + LLNotificationChannel::buildChannel("Expiration", "Enabled", boost::bind(&LLNotifications::expirationFilter, this, _1)); - LLNotificationChannel::buildChannel("Unexpired", "", + LLNotificationChannel::buildChannel("Unexpired", "Enabled", !boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind LLNotificationChannel::buildChannel("Unique", "Unexpired", boost::bind(&LLNotifications::uniqueFilter, this, _1)); LLNotificationChannel::buildChannel("Ignore", "Unique", filterIgnoredNotifications); - LLNotificationChannel::buildChannel("Visible", "Ignore", + LLNotificationChannel::buildChannel("VisibilityRules", "Ignore", + boost::bind(&LLNotifications::isVisibleByRules, this, _1)); + LLNotificationChannel::buildChannel("Visible", "VisibilityRules", &LLNotificationFilters::includeEverything); // create special persistent notification channel @@ -1207,6 +1292,8 @@ void LLNotifications::createDefaultChannels() new LLPersistentNotificationChannel(); // connect action methods to these channels + LLNotifications::instance().getChannel("Enabled")-> + connectFailedFilter(&defaultResponse); LLNotifications::instance().getChannel("Expiration")-> connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1)); // uniqueHandler slot should be added as first slot of the signal due to @@ -1218,6 +1305,8 @@ void LLNotifications::createDefaultChannels() // connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1)); LLNotifications::instance().getChannel("Ignore")-> connectFailedFilter(&handleIgnoredNotification); + LLNotifications::instance().getChannel("VisibilityRules")-> + connectFailedFilter(&visibilityRuleMached); } bool LLNotifications::addTemplate(const std::string &name, @@ -1347,6 +1436,12 @@ bool LLNotifications::loadTemplates() LLXUIParser parser; parser.readXUI(root, params, full_filename); + if(!params.validateBlock()) + { + llerrs << "Problem reading UI Notifications file: " << full_filename << llendl; + return false; + } + mTemplates.clear(); for(LLInitParam::ParamIterator<LLNotificationTemplate::GlobalString>::const_iterator it = params.strings.begin(), end_it = params.strings.end(); @@ -1396,6 +1491,34 @@ bool LLNotifications::loadTemplates() return true; } +bool LLNotifications::loadVisibilityRules() +{ + const std::string xml_filename = "notification_visibility.xml"; + std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename); + + LLNotificationVisibilityRule::Rules params; + LLSimpleXUIParser parser; + parser.readXUI(full_filename, params); + + if(!params.validateBlock()) + { + llerrs << "Problem reading UI Notification Visibility Rules file: " << full_filename << llendl; + return false; + } + + mVisibilityRules.clear(); + + for(LLInitParam::ParamIterator<LLNotificationVisibilityRule::Rule>::iterator it = params.rules.begin(), + end_it = params.rules.end(); + it != end_it; + ++it) + { + mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(*it))); + } + + return true; +} + // Add a simple notification (from XUI) void LLNotifications::addFromCallback(const LLSD& name) { @@ -1546,6 +1669,94 @@ bool LLNotifications::getIgnoreAllNotifications() return mIgnoreAllNotifications; } +bool LLNotifications::isVisibleByRules(LLNotificationPtr n) +{ + if(n->isRespondedTo()) + { + // This avoids infinite recursion in the case where the filter calls respond() + return true; + } + + VisibilityRuleList::iterator it; + + for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++) + { + // An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule. + + lldebugs + << "notification \"" << n->getName() << "\" " + << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, " + << "name = \"" << (*it)->mName << "\" " + << "tag = \"" << (*it)->mTag << "\" " + << "type = \"" << (*it)->mType << "\" " + << llendl; + + if(!(*it)->mType.empty()) + { + if((*it)->mType != n->getType()) + { + // Type doesn't match, so skip this rule. + continue; + } + } + + if(!(*it)->mTag.empty()) + { + // check this notification's tag(s) against it->mTag and continue if no match is found. + if(!n->matchesTag((*it)->mTag)) + { + // This rule's non-empty tag didn't match one of the notification's tags. Skip this rule. + continue; + } + } + + if(!(*it)->mName.empty()) + { + // check this notification's name against the notification's name and continue if no match is found. + if((*it)->mName != n->getName()) + { + // This rule's non-empty name didn't match the notification. Skip this rule. + continue; + } + } + + // If we got here, the rule matches. Don't evaluate subsequent rules. + if(!(*it)->mVisible) + { + // This notification is being hidden. + + if((*it)->mResponse.empty()) + { + // Response property is empty. Cancel this notification. + lldebugs << "cancelling notification " << n->getName() << llendl; + + n->cancel(); + } + else + { + // Response property is not empty. Return the specified response. + LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON); + // TODO: verify that the response template has an item with the correct name + response[(*it)->mResponse] = true; + + lldebugs << "responding to notification " << n->getName() << " with response = " << response << llendl; + + n->respond(response); + } + + return false; + } + + // If we got here, exit the loop and return true. + break; + } + + lldebugs << "allowing notification " << n->getName() << llendl; + + return true; +} + + // --- // END OF LLNotifications implementation // ========================================================= diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 524cff70e8..34d3537781 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -195,6 +195,7 @@ public: Mandatory<std::string> type; Optional<S32> width; Optional<S32> max_length_chars; + Optional<std::string> text; Optional<std::string> value; FormInput(); @@ -270,6 +271,11 @@ struct LLNotificationTemplate; // with smart pointers typedef boost::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr; + +struct LLNotificationVisibilityRule; + +typedef boost::shared_ptr<LLNotificationVisibilityRule> LLNotificationVisibilityRulePtr; + /** * @class LLNotification * @brief The object that expresses the details of a notification @@ -506,7 +512,7 @@ public: std::string getLabel() const; std::string getURL() const; S32 getURLOption() const; - S32 getURLOpenExternally() const; + S32 getURLOpenExternally() const; const LLNotificationFormPtr getForm(); @@ -578,6 +584,8 @@ public: bool hasUniquenessConstraints() const; + bool matchesTag(const std::string& tag); + virtual ~LLNotification() {} }; @@ -859,6 +867,10 @@ public: // OK to call more than once because it will reload bool loadTemplates(); + // load visibility rules from file; + // OK to call more than once because it will reload + bool loadVisibilityRules(); + // Add a simple notification (from XUI) void addFromCallback(const LLSD& name); @@ -904,6 +916,8 @@ public: // test for existence bool templateExists(const std::string& name); + typedef std::list<LLNotificationVisibilityRulePtr> VisibilityRuleList; + void forceResponse(const LLNotification::Params& params, S32 option); void createDefaultChannels(); @@ -919,6 +933,8 @@ public: void setIgnoreAllNotifications(bool ignore); bool getIgnoreAllNotifications(); + bool isVisibleByRules(LLNotificationPtr pNotification); + private: // we're a singleton, so we don't have a public constructor LLNotifications(); @@ -938,6 +954,8 @@ private: bool addTemplate(const std::string& name, LLNotificationTemplatePtr theTemplate); TemplateMap mTemplates; + VisibilityRuleList mVisibilityRules; + std::string mFileName; LLNotificationMap mUniqueNotifications; diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h index 6bc0d2aaff..eff572b553 100644 --- a/indra/llui/llnotificationtemplate.h +++ b/indra/llui/llnotificationtemplate.h @@ -74,11 +74,13 @@ struct LLNotificationTemplate struct UniquenessContext : public LLInitParam::Block<UniquenessContext> { - Mandatory<std::string> key; + Mandatory<std::string> value; UniquenessContext() - : key("key") - {} + : value("value") + { + addSynonym(value, "key"); + } }; @@ -88,7 +90,7 @@ struct LLNotificationTemplate // this idiom allows // <notification unique="true"> // as well as - // <notification> <unique> <context key=""/> </unique>... + // <notification> <unique> <context></context> </unique>... Optional<bool> dummy_val; public: Multiple<UniquenessContext> contexts; @@ -156,6 +158,15 @@ struct LLNotificationTemplate {} }; + struct Tag : public LLInitParam::Block<Tag> + { + Mandatory<std::string> value; + + Tag() + : value("value") + {} + }; + struct Params : public LLInitParam::Block<Params> { Mandatory<std::string> name; @@ -173,6 +184,7 @@ struct LLNotificationTemplate Optional<FormRef> form_ref; Optional<ENotificationPriority, NotificationPriorityValues> priority; + Multiple<Tag> tags; Params() @@ -189,7 +201,8 @@ struct LLNotificationTemplate expire_option("expireOption", -1), url("url"), unique("unique"), - form_ref("") + form_ref(""), + tags("tag") {} }; @@ -232,8 +245,8 @@ struct LLNotificationTemplate // (used for things like progress indications, or repeating warnings // like "the grid is going down in N minutes") bool mUnique; - // if we want to be unique only if a certain part of the payload is constant - // specify the field names for the payload. The notification will only be + // if we want to be unique only if a certain part of the payload or substitutions args + // are constant specify the field names for the payload. The notification will only be // combined if all of the fields named in the context are identical in the // new and the old notification; otherwise, the notification will be // duplicated. This is to support suppressing duplicate offers from the same @@ -276,6 +289,8 @@ struct LLNotificationTemplate // this is loaded as a name, but looked up to get the UUID upon template load. // If null, it wasn't specified. LLUUID mSoundEffect; + // List of tags that rules can match against. + std::list<std::string> mTags; }; #endif //LL_LLNOTIFICATION_TEMPLATE_H diff --git a/indra/llui/llnotificationvisibilityrule.h b/indra/llui/llnotificationvisibilityrule.h new file mode 100644 index 0000000000..78bdec2a8f --- /dev/null +++ b/indra/llui/llnotificationvisibilityrule.h @@ -0,0 +1,104 @@ +/** +* @file llnotificationvisibility.h +* @brief Rules for +* @author Monroe +* +* $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_LLNOTIFICATION_VISIBILITY_RULE_H +#define LL_LLNOTIFICATION_VISIBILITY_RULE_H + +#include "llinitparam.h" +//#include "llnotifications.h" + + + +// This is the class of object read from the XML file (notification_visibility.xml, +// from the appropriate local language directory). +struct LLNotificationVisibilityRule +{ + struct Filter : public LLInitParam::Block<Filter> + { + Optional<std::string> type, + tag, + name; + + Filter() + : type("type"), + tag("tag"), + name("name") + {} + }; + + struct Respond : public LLInitParam::Block<Respond, Filter> + { + Mandatory<std::string> response; + + Respond() + : response("response") + {} + }; + + struct Rule : public LLInitParam::Choice<Rule> + { + Alternative<Filter> show; + Alternative<Filter> hide; + Alternative<Respond> respond; + + Rule() + : show("show"), + hide("hide"), + respond("respond") + {} + }; + + struct Rules : public LLInitParam::Block<Rules> + { + Multiple<Rule> rules; + + Rules() + : rules("") + {} + }; + + LLNotificationVisibilityRule(const Rule& p); + + // If true, this rule makes matching notifications visible. Otherwise, it makes them invisible. + bool mVisible; + + // Which response to give when making a notification invisible. An empty string means the notification should be cancelled instead of responded to. + std::string mResponse; + + // String to match against the notification's "type". An empty string matches all notifications. + std::string mType; + + // String to match against the notification's tag(s). An empty string matches all notifications. + std::string mTag; + + // String to match against the notification's name. An empty string matches all notifications. + std::string mName; + +}; + +#endif //LL_LLNOTIFICATION_VISIBILITY_RULE_H + diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index c8e56630f1..b2383106a8 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -194,6 +194,8 @@ void LLPanel::draw() // draw background if( mBgVisible ) { + alpha = getCurrentTransparency(); + LLRect local_rect = getLocalRect(); if (mBgOpaque ) { @@ -434,7 +436,7 @@ void LLPanel::initFromParams(const LLPanel::Params& p) //and LLView::initFromParams will use them to set visible and enabled setVisible(p.visible); setEnabled(p.enabled); - + setFocusRoot(p.focus_root); setSoundFlags(p.sound_flags); // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible @@ -904,7 +906,7 @@ LLPanel *LLPanel::childGetVisiblePanelWithHelp() child = *it; // do we have a panel with a help topic? LLPanel *panel = dynamic_cast<LLPanel *>(child); - if (panel && panel->getVisible() && !panel->getHelpTopic().empty()) + if (panel && panel->isInVisibleChain() && !panel->getHelpTopic().empty()) { return panel; } diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp index aaa328754d..ead22686bc 100644 --- a/indra/llui/llprogressbar.cpp +++ b/indra/llui/llprogressbar.cpp @@ -50,7 +50,7 @@ LLProgressBar::Params::Params() LLProgressBar::LLProgressBar(const LLProgressBar::Params& p) -: LLView(p), +: LLUICtrl(p), mImageBar(p.image_bar), mImageFill(p.image_fill), mColorBackground(p.color_bg()), @@ -80,7 +80,7 @@ void LLProgressBar::draw() mImageFill->draw(progress_rect, bar_color); } -void LLProgressBar::setPercent(const F32 percent) +void LLProgressBar::setValue(const LLSD& value) { - mPercentDone = llclamp(percent, 0.f, 100.f); + mPercentDone = llclamp((F32)value.asReal(), 0.f, 100.f); } diff --git a/indra/llui/llprogressbar.h b/indra/llui/llprogressbar.h index 13297f7493..3f308e7496 100644 --- a/indra/llui/llprogressbar.h +++ b/indra/llui/llprogressbar.h @@ -27,14 +27,14 @@ #ifndef LL_LLPROGRESSBAR_H #define LL_LLPROGRESSBAR_H -#include "llview.h" +#include "lluictrl.h" #include "llframetimer.h" class LLProgressBar - : public LLView + : public LLUICtrl { public: - struct Params : public LLInitParam::Block<Params, LLView::Params> + struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { Optional<LLUIImage*> image_bar, image_fill; @@ -47,7 +47,7 @@ public: LLProgressBar(const Params&); virtual ~LLProgressBar(); - void setPercent(const F32 percent); + void setValue(const LLSD& value); /*virtual*/ void draw(); diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index cc348fdc63..6e9586369f 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -69,7 +69,7 @@ protected: static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item"); LLRadioGroup::Params::Params() -: has_border("draw_border"), +: allow_deselect("allow_deselect"), items("item") { addSynonym(items, "radio_item"); @@ -85,18 +85,8 @@ LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p) : LLUICtrl(p), mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), mSelectedIndex(-1), - mHasBorder(p.has_border) -{ - if (mHasBorder) - { - LLViewBorder::Params params; - params.name("radio group border"); - params.rect(LLRect(0, getRect().getHeight(), getRect().getWidth(), 0)); - params.bevel_style(LLViewBorder::BEVEL_NONE); - LLViewBorder * vb = LLUICtrlFactory::create<LLViewBorder> (params); - addChild (vb); - } -} + mAllowDeselect(p.allow_deselect) +{} void LLRadioGroup::initFromParams(const Params& p) { @@ -184,7 +174,7 @@ void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled) BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event) { - if (index < 0 || (S32)mRadioButtons.size() <= index ) + if ((S32)mRadioButtons.size() <= index ) { return FALSE; } @@ -202,13 +192,16 @@ BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event) mSelectedIndex = index; - LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; - radio_item->setTabStop(true); - radio_item->setValue( TRUE ); - - if (hasFocus()) + if (mSelectedIndex >= 0) { - mRadioButtons[mSelectedIndex]->focusFirstItem(FALSE, FALSE); + LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; + radio_item->setTabStop(true); + radio_item->setValue( TRUE ); + + if (hasFocus()) + { + radio_item->focusFirstItem(FALSE, FALSE); + } } if (!from_event) @@ -307,8 +300,15 @@ void LLRadioGroup::onClickButton(LLUICtrl* ctrl) LLRadioCtrl* radio = *iter; if (radio == clicked_radio) { - // llinfos << "clicked button " << index << llendl; - setSelectedIndex(index); + if (index == mSelectedIndex && mAllowDeselect) + { + // don't select anything + setSelectedIndex(-1); + } + else + { + setSelectedIndex(index); + } // BUG: Calls click callback even if button didn't actually change onCommit(); diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h index 0588900600..8bd5698538 100644 --- a/indra/llui/llradiogroup.h +++ b/indra/llui/llradiogroup.h @@ -49,7 +49,7 @@ public: struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { - Optional<bool> has_border; + Optional<bool> allow_deselect; Multiple<ItemParams, AtLeast<1> > items; Params(); }; @@ -73,7 +73,6 @@ public: void setIndexEnabled(S32 index, BOOL enabled); // return the index value of the selected item S32 getSelectedIndex() const { return mSelectedIndex; } - // set the index value programatically BOOL setSelectedIndex(S32 index, BOOL from_event = FALSE); @@ -103,12 +102,13 @@ public: /*virtual*/ BOOL operateOnAll(EOperation op); private: - const LLFontGL* mFont; - S32 mSelectedIndex; + const LLFontGL* mFont; + S32 mSelectedIndex; + typedef std::vector<class LLRadioCtrl*> button_list_t; - button_list_t mRadioButtons; + button_list_t mRadioButtons; - BOOL mHasBorder; + bool mAllowDeselect; // user can click on an already selected option to deselect it }; #endif diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 3146418a7d..380c477eb2 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -422,9 +422,10 @@ void LLScrollContainer::draw() // Draw background if( mIsOpaque ) { + F32 alpha = getCurrentTransparency(); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4fv( mBackgroundColor.get().mV ); - gl_rect_2d( mInnerRect ); + gl_rect_2d(mInnerRect, mBackgroundColor.get() % alpha); } // Draw mScrolledViews and update scroll bars. diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp index 2a4c1ca44c..696e4a2bb1 100644 --- a/indra/llui/llscrolllistcolumn.cpp +++ b/indra/llui/llscrolllistcolumn.cpp @@ -83,7 +83,14 @@ void LLScrollColumnHeader::draw() && (sort_column == mColumn->mSortingColumn || sort_column == mColumn->mName); BOOL is_ascending = mColumn->mParentCtrl->getSortAscending(); - setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, draw_arrow ? LLColor4::white : LLColor4::transparent); + if (draw_arrow) + { + setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, LLColor4::white); + } + else + { + setImageOverlay(LLUUID::null); + } // Draw children LLButton::draw(); diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 7df7c13dc0..b7848ec37c 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -322,6 +322,7 @@ LLScrollListCtrl::~LLScrollListCtrl() delete mSortCallback; std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); + std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer()); } @@ -1482,8 +1483,9 @@ void LLScrollListCtrl::draw() // Draw background if (mBackgroundVisible) { + F32 alpha = getCurrentTransparency(); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() : mBgReadOnlyColor.get() ); + gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha ); } if (mColumnsDirty) @@ -2370,10 +2372,10 @@ void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar ) void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending) { - std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.find(name); + column_map_t::iterator itor = mColumns.find(name); if (itor != mColumns.end()) { - sortByColumnIndex((*itor).second.mIndex, ascending); + sortByColumnIndex((*itor).second->mIndex, ascending); } } @@ -2419,11 +2421,11 @@ void LLScrollListCtrl::dirtyColumns() // just in case someone indexes into it immediately mColumnsIndexed.resize(mColumns.size()); - std::map<std::string, LLScrollListColumn>::iterator column_itor; + column_map_t::iterator column_itor; for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) { - LLScrollListColumn *column = &column_itor->second; - mColumnsIndexed[column_itor->second.mIndex] = column; + LLScrollListColumn *column = column_itor->second; + mColumnsIndexed[column_itor->second->mIndex] = column; } } @@ -2581,8 +2583,8 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params if (mColumns.find(name) == mColumns.end()) { // Add column - mColumns[name] = LLScrollListColumn(column_params, this); - LLScrollListColumn* new_column = &mColumns[name]; + mColumns[name] = new LLScrollListColumn(column_params, this); + LLScrollListColumn* new_column = mColumns[name]; new_column->mIndex = mColumns.size()-1; // Add button @@ -2604,14 +2606,14 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params S32 top = mItemListRect.mTop; S32 left = mItemListRect.mLeft; - for (std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.begin(); + for (column_map_t::iterator itor = mColumns.begin(); itor != mColumns.end(); ++itor) { - if (itor->second.mIndex < new_column->mIndex && - itor->second.getWidth() > 0) + if (itor->second->mIndex < new_column->mIndex && + itor->second->getWidth() > 0) { - left += itor->second.getWidth() + mColumnPadding; + left += itor->second->getWidth() + mColumnPadding; } } @@ -2667,8 +2669,8 @@ void LLScrollListCtrl::onClickColumn(void *userdata) if (column->mSortingColumn != column->mName && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) { - LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn]; - column_index = info_redir.mIndex; + LLScrollListColumn* info_redir = parent->mColumns[column->mSortingColumn]; + column_index = info_redir->mIndex; } // if this column is the primary sort key, reverse the direction @@ -2701,16 +2703,17 @@ BOOL LLScrollListCtrl::hasSortOrder() const void LLScrollListCtrl::clearColumns() { - std::map<std::string, LLScrollListColumn>::iterator itor; + column_map_t::iterator itor; for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) { - LLScrollColumnHeader *header = itor->second.mHeader; + LLScrollColumnHeader *header = itor->second->mHeader; if (header) { removeChild(header); delete header; } } + std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer()); mColumns.clear(); mSortColumns.clear(); mTotalStaticColumnWidth = 0; @@ -2744,7 +2747,7 @@ LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name) column_map_t::iterator column_itor = mColumns.find(name); if (column_itor != mColumns.end()) { - return &column_itor->second; + return column_itor->second; } return NULL; } @@ -2805,7 +2808,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS new_column.width.pixel_width = cell_p.width; } addColumn(new_column); - columnp = &mColumns[column]; + columnp = mColumns[column]; new_item->setNumColumns(mColumns.size()); } @@ -2842,7 +2845,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value)); if (cell) { - LLScrollListColumn* columnp = &(mColumns.begin()->second); + LLScrollListColumn* columnp = mColumns.begin()->second; new_item->setColumn(0, cell); if (columnp->mHeader @@ -2857,10 +2860,10 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS // add dummy cells for missing columns for (column_map_t::iterator column_it = mColumns.begin(); column_it != mColumns.end(); ++column_it) { - S32 column_idx = column_it->second.mIndex; + S32 column_idx = column_it->second->mIndex; if (new_item->getColumn(column_idx) == NULL) { - LLScrollListColumn* column_ptr = &column_it->second; + LLScrollListColumn* column_ptr = column_it->second; LLScrollListCell::Params cell_p; cell_p.width = column_ptr->getWidth(); diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 8a2f893ba2..09ab89960d 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -491,7 +491,7 @@ private: mutable bool mSorted; - typedef std::map<std::string, LLScrollListColumn> column_map_t; + typedef std::map<std::string, LLScrollListColumn*> column_map_t; column_map_t mColumns; BOOL mDirty; diff --git a/indra/llui/llsdparam.cpp b/indra/llui/llsdparam.cpp index f97f80ab6c..9ad13054cb 100644 --- a/indra/llui/llsdparam.cpp +++ b/indra/llui/llsdparam.cpp @@ -45,6 +45,7 @@ LLParamSDParser::LLParamSDParser() if (sReadFuncs.empty()) { + registerParserFuncs<LLInitParam::NoParamValue>(readNoValue, &LLParamSDParser::writeNoValue); registerParserFuncs<S32>(readS32, &LLParamSDParser::writeTypedValue<S32>); registerParserFuncs<U32>(readU32, &LLParamSDParser::writeU32Param); registerParserFuncs<F32>(readF32, &LLParamSDParser::writeTypedValue<F32>); @@ -71,6 +72,18 @@ bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const voi return true; } +bool LLParamSDParser::writeNoValue(LLParamSDParser::parser_t& parser, const void* val_ptr, const parser_t::name_stack_t& name_stack) +{ + LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser); + if (!sdparser.mWriteRootSD) return false; + + LLSD* sd_to_write = sdparser.getSDWriteNode(name_stack); + if (!sd_to_write) return false; + + return true; +} + + void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent) { mCurReadSD = NULL; @@ -87,6 +100,8 @@ void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block) block.serializeBlock(*this); } +const LLSD NO_VALUE_MARKER; + void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block) { if (sd.isMap()) @@ -110,6 +125,11 @@ void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block readSDValues(*it, block); } } + else if (sd.isUndefined()) + { + mCurReadSD = &NO_VALUE_MARKER; + block.submitValue(mNameStack, *this); + } else { mCurReadSD = &sd; @@ -206,6 +226,13 @@ LLSD* LLParamSDParser::getSDWriteNode(const parser_t::name_stack_t& name_stack) return sd_to_write; } +bool LLParamSDParser::readNoValue(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + return self.mCurReadSD == &NO_VALUE_MARKER; +} + + bool LLParamSDParser::readS32(Parser& parser, void* val_ptr) { LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); diff --git a/indra/llui/llsdparam.h b/indra/llui/llsdparam.h index 97e8b58e49..69dab2b411 100644 --- a/indra/llui/llsdparam.h +++ b/indra/llui/llsdparam.h @@ -63,7 +63,9 @@ private: LLSD* getSDWriteNode(const parser_t::name_stack_t& name_stack); static bool writeU32Param(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack); + static bool writeNoValue(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack); + static bool readNoValue(Parser& parser, void* val_ptr); static bool readS32(Parser& parser, void* val_ptr); static bool readU32(Parser& parser, void* val_ptr); static bool readF32(Parser& parser, void* val_ptr); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 9adeddca99..49537ef78f 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1005,6 +1005,7 @@ void LLTextBase::draw() if (mBGVisible) { + F32 alpha = getCurrentTransparency(); // clip background rect against extents, if we support scrolling LLRect bg_rect = mVisibleTextRect; if (mScroller) @@ -1016,7 +1017,7 @@ void LLTextBase::draw() : hasFocus() ? mFocusBgColor.get() : mWriteableBgColor.get(); - gl_rect_2d(doc_rect, bg_color, TRUE); + gl_rect_2d(doc_rect, bg_color % alpha, TRUE); } // draw document view @@ -1591,7 +1592,10 @@ void LLTextBase::setText(const LLStringExplicit &utf8str, const LLStyle::Params& // appendText modifies mCursorPos... appendText(text, false, input_params); // ...so move cursor to top after appending text - startOfDoc(); + if (!mTrackEnd) + { + startOfDoc(); + } onValueChange(0, getLength()); } @@ -1622,7 +1626,7 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para style_params.fillFrom(getDefaultStyleParams()); S32 part = (S32)LLTextParser::WHOLE; - if(mParseHTML) + if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358). { S32 start=0,end=0; LLUrlMatch match; @@ -2214,19 +2218,39 @@ bool LLTextBase::scrolledToEnd() return mScroller->isAtBottom(); } - bool LLTextBase::setCursor(S32 row, S32 column) { - if (0 <= row && row < (S32)mLineInfoList.size()) + if (row < 0 || column < 0) return false; + + S32 n_lines = mLineInfoList.size(); + for (S32 line = row; line < n_lines; ++line) { - S32 doc_pos = mLineInfoList[row].mDocIndexStart; - column = llclamp(column, 0, mLineInfoList[row].mDocIndexEnd - mLineInfoList[row].mDocIndexStart - 1); - doc_pos += column; - updateCursorXPos(); + const line_info& li = mLineInfoList[line]; + + if (li.mLineNum < row) + { + continue; + } + else if (li.mLineNum > row) + { + break; // invalid column specified + } + + // Found the given row. + S32 line_length = li.mDocIndexEnd - li.mDocIndexStart;; + if (column >= line_length) + { + column -= line_length; + continue; + } + // Found the given column. + updateCursorXPos(); + S32 doc_pos = li.mDocIndexStart + column; return setCursorPos(doc_pos); } - return false; + + return false; // invalid row or column specified } diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 94bf716e7d..5a46c7c98e 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -277,6 +277,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN; updateRects(); } + + mParseOnTheFly = TRUE; } void LLTextEditor::initFromParams( const LLTextEditor::Params& p) @@ -324,8 +326,10 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Param blockUndo(); deselect(); - + + mParseOnTheFly = FALSE; LLTextBase::setText(utf8str, input_params); + mParseOnTheFly = TRUE; resetDirty(); } @@ -1367,6 +1371,7 @@ void LLTextEditor::pastePrimary() // paste from primary (itsprimary==true) or clipboard (itsprimary==false) void LLTextEditor::pasteHelper(bool is_primary) { + mParseOnTheFly = FALSE; bool can_paste_it; if (is_primary) { @@ -1450,6 +1455,7 @@ void LLTextEditor::pasteHelper(bool is_primary) deselect(); onKeyStroke(); + mParseOnTheFly = TRUE; } @@ -2385,7 +2391,7 @@ void LLTextEditor::loadKeywords(const std::string& filename, void LLTextEditor::updateSegments() { - if (mReflowIndex < S32_MAX && mKeywords.isLoaded()) + if (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly) { LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING); // HACK: No non-ascii keywords for now diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 58ecefdccb..9e4b95003b 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -315,6 +315,7 @@ private: BOOL mAllowEmbeddedItems; bool mShowContextMenu; + bool mParseOnTheFly; LLUUID mSourceID; diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp index 0eb2dc1387..d29260750f 100644 --- a/indra/llui/lltoggleablemenu.cpp +++ b/indra/llui/lltoggleablemenu.cpp @@ -35,10 +35,22 @@ static LLDefaultChildRegistry::Register<LLToggleableMenu> r("toggleable_menu"); LLToggleableMenu::LLToggleableMenu(const LLToggleableMenu::Params& p) : LLMenuGL(p), mButtonRect(), + mVisibilityChangeSignal(NULL), mClosedByButtonClick(false) { } +LLToggleableMenu::~LLToggleableMenu() +{ + delete mVisibilityChangeSignal; +} + +boost::signals2::connection LLToggleableMenu::setVisibilityChangeCallback(const commit_signal_t::slot_type& cb) +{ + if (!mVisibilityChangeSignal) mVisibilityChangeSignal = new commit_signal_t(); + return mVisibilityChangeSignal->connect(cb); +} + // virtual void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn) { @@ -49,6 +61,12 @@ void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn) { mClosedByButtonClick = true; } + + if (mVisibilityChangeSignal) + { + (*mVisibilityChangeSignal)(this, + LLSD().with("visibility", curVisibilityIn).with("closed_by_button_click", mClosedByButtonClick)); + } } void LLToggleableMenu::setButtonRect(const LLRect& rect, LLView* current_view) diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h index f036cdfffb..2094bd776f 100644 --- a/indra/llui/lltoggleablemenu.h +++ b/indra/llui/lltoggleablemenu.h @@ -41,6 +41,10 @@ protected: LLToggleableMenu(const Params&); friend class LLUICtrlFactory; public: + ~LLToggleableMenu(); + + boost::signals2::connection setVisibilityChangeCallback( const commit_signal_t::slot_type& cb ); + virtual void handleVisibilityChange (BOOL curVisibilityIn); const LLRect& getButtonRect() const { return mButtonRect; } @@ -57,6 +61,7 @@ public: protected: bool mClosedByButtonClick; LLRect mButtonRect; + commit_signal_t* mVisibilityChangeSignal; }; #endif // LL_LLTOGGLEABLEMENU_H diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index ff9af21e54..c300fe55d9 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -950,7 +950,7 @@ void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor } // Draw gray and white checkerboard with black border -void gl_rect_2d_checkerboard(const LLRect& rect) +void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha) { // Initialize the first time this is called. const S32 PIXELS = 32; @@ -971,11 +971,11 @@ void gl_rect_2d_checkerboard(const LLRect& rect) gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); // ...white squares - gGL.color3f( 1.f, 1.f, 1.f ); + gGL.color4f( 1.f, 1.f, 1.f, alpha ); gl_rect_2d(rect); // ...gray squares - gGL.color3f( .7f, .7f, .7f ); + gGL.color4f( .7f, .7f, .7f, alpha ); gGL.flush(); glPolygonStipple( checkerboard ); @@ -1596,23 +1596,25 @@ void LLUI::initClass(const settings_map_t& settings, sWindow = NULL; // set later in startup LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow"); + LLUICtrl::CommitCallbackRegistry::Registrar& reg = LLUICtrl::CommitCallbackRegistry::defaultRegistrar(); + // Callbacks for associating controls with floater visibilty: - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2)); - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2)); - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Hide", boost::bind(&LLFloaterReg::hideFloaterInstance, _2)); - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.InitToVisibilityControl", boost::bind(&LLFloaterReg::initUICtrlToFloaterVisibilityControl, _1, _2)); + reg.add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2)); + reg.add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2)); + reg.add("Floater.Hide", boost::bind(&LLFloaterReg::hideFloaterInstance, _2)); + reg.add("Floater.InitToVisibilityControl", boost::bind(&LLFloaterReg::initUICtrlToFloaterVisibilityControl, _1, _2)); // Button initialization callback for toggle buttons - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2)); + reg.add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2)); // Button initialization callback for toggle buttons on dockale floaters - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2)); + reg.add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2)); // Display the help topic for the current context - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2)); + reg.add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2)); // Currently unused, but kept for reference: - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2)); + reg.add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2)); // Used by menus along with Floater.Toggle to display visibility as a checkmark LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.Visible", boost::bind(&LLFloaterReg::floaterInstanceVisible, _2)); @@ -1620,7 +1622,10 @@ void LLUI::initClass(const settings_map_t& settings, void LLUI::cleanupClass() { - sImageProvider->cleanUp(); + if(sImageProvider) + { + sImageProvider->cleanUp(); + } } void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup, const clear_popups_t& clear_popups) diff --git a/indra/llui/llui.h b/indra/llui/llui.h index fc545c85d5..62d10df8b2 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -79,7 +79,7 @@ void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LL void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset = 0, BOOL filled = TRUE ); void gl_rect_2d(const LLRect& rect, BOOL filled = TRUE ); void gl_rect_2d(const LLRect& rect, const LLColor4& color, BOOL filled = TRUE ); -void gl_rect_2d_checkerboard(const LLRect& rect); +void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha = 1.0f); void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines); diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp index 0641f6d175..9455d09cc0 100644 --- a/indra/llui/lluicolortable.cpp +++ b/indra/llui/lluicolortable.cpp @@ -215,6 +215,12 @@ bool LLUIColorTable::loadFromSettings() result |= loadFromFilename(current_filename, mLoadedColors); } + current_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SKIN, "colors.xml"); + if(current_filename != default_filename) + { + result |= loadFromFilename(current_filename, mLoadedColors); + } + std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml"); loadFromFilename(user_filename, mUserSetColors); diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 3ac3bf8c41..0a06b5e74f 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -36,6 +36,9 @@ static LLDefaultChildRegistry::Register<LLUICtrl> r("ui_ctrl"); +F32 LLUICtrl::sActiveControlTransparency = 1.0f; +F32 LLUICtrl::sInactiveControlTransparency = 1.0f; + // Compiler optimization, generate extern template template class LLUICtrl* LLView::getChild<class LLUICtrl>( const std::string& name, BOOL recurse) const; @@ -110,7 +113,8 @@ LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel) mMouseUpSignal(NULL), mRightMouseDownSignal(NULL), mRightMouseUpSignal(NULL), - mDoubleClickSignal(NULL) + mDoubleClickSignal(NULL), + mTransparencyType(TT_DEFAULT) { mUICtrlHandle.bind(this); } @@ -823,7 +827,7 @@ LLUICtrl* LLUICtrl::findRootMostFocusRoot() { LLUICtrl* focus_root = NULL; LLUICtrl* next_view = this; - while(next_view) + while(next_view && next_view->hasTabStop()) { if (next_view->isFocusRoot()) { @@ -923,6 +927,37 @@ BOOL LLUICtrl::getTentative() const void LLUICtrl::setColor(const LLColor4& color) { } +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; + break; + } + + return alpha; +} + +void LLUICtrl::setTransparencyType(ETypeTransparency type) +{ + mTransparencyType = type; +} + boost::signals2::connection LLUICtrl::setCommitCallback( const commit_signal_t::slot_type& cb ) { if (!mCommitSignal) mCommitSignal = new commit_signal_t(); diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 76dfdf754c..b37e9f6b1b 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -120,6 +120,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); @@ -202,6 +209,11 @@ public: virtual void setColor(const LLColor4& color); + F32 getCurrentTransparency(); + + void setTransparencyType(ETypeTransparency type); + ETypeTransparency getTransparencyType() const {return mTransparencyType;} + BOOL focusNextItem(BOOL text_entry_only); BOOL focusPrevItem(BOOL text_entry_only); BOOL focusFirstItem(BOOL prefer_text_fields = FALSE, BOOL focus_flash = TRUE ); @@ -283,6 +295,10 @@ protected: boost::signals2::connection mMakeVisibleControlConnection; LLControlVariable* mMakeInvisibleControlVariable; boost::signals2::connection mMakeInvisibleControlConnection; + + static F32 sActiveControlTransparency; + static F32 sInactiveControlTransparency; + private: BOOL mTabStop; @@ -290,6 +306,8 @@ private: BOOL mTentative; LLRootHandle<LLUICtrl> mUICtrlHandle; + ETypeTransparency mTransparencyType; + class DefaultTabGroupFirstSorter; }; diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h index eff2467bf0..cb40c85582 100644 --- a/indra/llui/lluistring.h +++ b/indra/llui/lluistring.h @@ -58,9 +58,10 @@ class LLUIString public: // These methods all perform appropriate argument substitution // and modify mOrig where appropriate - LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {} + LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {} LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args); LLUIString(const std::string& instring) : mArgs(NULL) { assign(instring); } + ~LLUIString() { delete mArgs; } void assign(const std::string& instring); LLUIString& operator=(const std::string& s) { assign(s); return *this; } @@ -81,14 +82,14 @@ public: void clear(); void clearArgs() { if (mArgs) mArgs->clear(); } - + // These utility functions are included for text editing. // They do not affect mOrig and do not perform argument substitution void truncate(S32 maxchars); void erase(S32 charidx, S32 len); void insert(S32 charidx, const LLWString& wchars); void replace(S32 charidx, llwchar wc); - + private: // something changed, requiring reformatting of strings void dirty(); @@ -100,7 +101,7 @@ private: void updateResult() const; void updateWResult() const; LLStringUtil::format_map_t& getArgs(); - + std::string mOrig; mutable std::string mResult; mutable LLWString mWResult; // for displaying diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index efb3f1a8be..4f7b4be526 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -27,6 +27,7 @@ #include "linden_common.h" #include "llurlentry.h" +#include "lluictrl.h" #include "lluri.h" #include "llurlmatch.h" #include "llurlregistry.h" @@ -167,6 +168,15 @@ void LLUrlEntryBase::callObservers(const std::string &id, } } +/// is this a match for a URL that should not be hyperlinked? +bool LLUrlEntryBase::isLinkDisabled() const +{ + // this allows us to have a global setting to turn off text hyperlink highlighting/action + bool globally_disabled = LLUI::sSettingGroups["config"]->getBOOL("DisableTextHyperlinkActions"); + + return globally_disabled; +} + static std::string getStringAfterToken(const std::string str, const std::string token) { size_t pos = str.find(token); @@ -456,8 +466,8 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa LLStyle::Params LLUrlEntryAgent::getStyle() const { LLStyle::Params style_params = LLUrlEntryBase::getStyle(); - style_params.color = LLUIColorTable::instance().getColor("AgentLinkColor"); - style_params.readonly_color = LLUIColorTable::instance().getColor("AgentLinkColor"); + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); return style_params; } @@ -796,6 +806,69 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const } // +// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g. +// secondlife:///app/region/Ahern/128/128/0 +// +LLUrlEntryRegion::LLUrlEntryRegion() +{ + mPattern = boost::regex("secondlife:///app/region/[^/\\s]+(/\\d+)?(/\\d+)?(/\\d+)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife:///app/region/Place/X/Y/Z + // - secondlife:///app/region/Place/X/Y + // - secondlife:///app/region/Place/X + // - secondlife:///app/region/Place + // + + LLSD path_array = LLURI(url).pathArray(); + S32 path_parts = path_array.size(); + + if (path_parts < 3) // no region name + { + llwarns << "Failed to parse url [" << url << "]" << llendl; + return url; + } + + std::string label = unescapeUrl(path_array[2]); // region name + + if (path_parts > 3) // secondlife:///app/region/Place/X + { + std::string x = path_array[3]; + label += " (" + x; + + if (path_parts > 4) // secondlife:///app/region/Place/X/Y + { + std::string y = path_array[4]; + label += "," + y; + + if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z + { + std::string z = path_array[5]; + label = label + "," + z; + } + } + + label += ")"; + } + + return label; +} + +std::string LLUrlEntryRegion::getLocation(const std::string &url) const +{ + LLSD path_array = LLURI(url).pathArray(); + std::string region_name = unescapeUrl(path_array[2]); + return region_name; +} + +// // 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/ @@ -978,7 +1051,7 @@ std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const // LLUrlEntryNoLink::LLUrlEntryNoLink() { - mPattern = boost::regex("<nolink>[^<]*</nolink>", + mPattern = boost::regex("<nolink>.*</nolink>", boost::regex::perl|boost::regex::icase); } @@ -995,7 +1068,8 @@ std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelC LLStyle::Params LLUrlEntryNoLink::getStyle() const { - return LLStyle::Params(); + // Don't render as URL (i.e. no context menu or hand cursor). + return LLStyle::Params().is_link(false); } diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 9b91c103ef..1791739061 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -94,6 +94,8 @@ public: virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } + bool isLinkDisabled() const; + protected: std::string getIDStringFromUrl(const std::string &url) const; std::string escapeUrl(const std::string &url) const; @@ -183,7 +185,7 @@ private: /// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) /// that displays various forms of user name /// This is a base class for the various implementations of name display -class LLUrlEntryAgentName : public LLUrlEntryBase +class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::trackable { public: LLUrlEntryAgentName(); @@ -300,6 +302,18 @@ public: }; /// +/// LLUrlEntryRegion Describes a Second Life location Url, e.g., +/// secondlife:///app/region/Ahern/128/128/0 +/// +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; +}; + +/// /// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., /// secondlife:///app/teleport/Ahern/50/50/50/ /// diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 478b412d5e..523ee5d78c 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -54,6 +54,7 @@ LLUrlRegistry::LLUrlRegistry() registerUrl(new LLUrlEntryGroup()); registerUrl(new LLUrlEntryParcel()); registerUrl(new LLUrlEntryTeleport()); + registerUrl(new LLUrlEntryRegion()); registerUrl(new LLUrlEntryWorldMap()); registerUrl(new LLUrlEntryObjectIM()); registerUrl(new LLUrlEntryPlace()); diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 3fa86bf0ca..267640a226 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -102,6 +102,7 @@ LLView::Params::Params() left_pad("left_pad"), left_delta("left_delta", S32_MAX), from_xui("from_xui", false), + focus_root("focus_root", false), needs_translate("translate"), xmlns("xmlns"), xmlns_xsi("xmlns:xsi"), @@ -117,7 +118,7 @@ LLView::LLView(const LLView::Params& p) mParentView(NULL), mReshapeFlags(FOLLOWS_NONE), mFromXUI(p.from_xui), - mIsFocusRoot(FALSE), + mIsFocusRoot(p.focus_root), mLastVisible(FALSE), mNextInsertionOrdinal(0), mHoverCursor(getCursorFromString(p.hover_cursor)), @@ -163,8 +164,6 @@ LLView::~LLView() if (mDefaultWidgets) { - std::for_each(mDefaultWidgets->begin(), mDefaultWidgets->end(), - DeletePairedPointer()); delete mDefaultWidgets; mDefaultWidgets = NULL; } @@ -1682,18 +1681,7 @@ BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const //----------------------------------------------------------------------------- LLView* LLView::getChildView(const std::string& name, BOOL recurse) const { - LLView* child = findChildView(name, recurse); - if (!child) - { - child = getDefaultWidget<LLView>(name); - if (!child) - { - LLView::Params view_params; - view_params.name = name; - child = LLUICtrlFactory::create<LLView>(view_params); - } - } - return child; + return getChild<LLView>(name, recurse); } static LLFastTimer::DeclareTimer FTM_FIND_VIEWS("Find Widgets"); @@ -2804,11 +2792,14 @@ LLView::root_to_view_iterator_t LLView::endRootToView() // only create maps on demand, as they incur heap allocation/deallocation cost // when a view is constructed/deconstructed -LLView::default_widget_map_t& LLView::getDefaultWidgetMap() const +LLView& LLView::getDefaultWidgetContainer() const { if (!mDefaultWidgets) { - mDefaultWidgets = new default_widget_map_t(); + LLView::Params p; + p.name = "default widget container"; + p.visible = false; // ensures default widgets can't steal focus, etc. + mDefaultWidgets = new LLView(p); } return *mDefaultWidgets; } diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 33d345beff..d2bbd663b8 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -116,7 +116,8 @@ public: visible, mouse_opaque, use_bounding_rect, - from_xui; + from_xui, + focus_root; Optional<S32> tab_group, default_tab_group; @@ -412,14 +413,9 @@ public: LLControlVariable *findControl(const std::string& name); - // Moved setValue(), getValue(), setControlValue(), setControlName(), - // controlListener() to LLUICtrl because an LLView is NOT assumed to - // contain a value. If that's what you want, use LLUICtrl instead. -// virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata); - const child_list_t* getChildList() const { return &mChildList; } - const child_list_const_iter_t beginChild() { return mChildList.begin(); } - const child_list_const_iter_t endChild() { return mChildList.end(); } + child_list_const_iter_t beginChild() const { return mChildList.begin(); } + child_list_const_iter_t endChild() const { return mChildList.end(); } // LLMouseHandler functions // Default behavior is to pass events to children @@ -466,12 +462,8 @@ public: template <class T> T* getDefaultWidget(const std::string& name) const { - default_widget_map_t::const_iterator found_it = getDefaultWidgetMap().find(name); - if (found_it == getDefaultWidgetMap().end()) - { - return NULL; - } - return dynamic_cast<T*>(found_it->second); + LLView* widgetp = getDefaultWidgetContainer().findChildView(name); + return dynamic_cast<T*>(widgetp); } ////////////////////////////////////////////// @@ -585,9 +577,9 @@ private: typedef std::map<std::string, LLView*> default_widget_map_t; // allocate this map no demand, as it is rarely needed - mutable default_widget_map_t* mDefaultWidgets; + mutable LLView* mDefaultWidgets; - default_widget_map_t& getDefaultWidgetMap() const; + LLView& getDefaultWidgetContainer() const; public: // Depth in view hierarchy during rendering @@ -654,7 +646,7 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) co return NULL; } - getDefaultWidgetMap()[name] = result; + getDefaultWidgetContainer().addChild(result); } } return result; diff --git a/indra/llui/llwindowshade.cpp b/indra/llui/llwindowshade.cpp new file mode 100644 index 0000000000..77e94385d4 --- /dev/null +++ b/indra/llui/llwindowshade.cpp @@ -0,0 +1,328 @@ +/** + * @file LLWindowShade.cpp + * @brief Notification dialog that slides down and optionally disabled a piece of UI + * + * $LicenseInfo:firstyear=2006&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 "llwindowshade.h" + +#include "lllayoutstack.h" +#include "lltextbox.h" +#include "lliconctrl.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "lllineeditor.h" + +const S32 MIN_NOTIFICATION_AREA_HEIGHT = 30; +const S32 MAX_NOTIFICATION_AREA_HEIGHT = 100; + +LLWindowShade::Params::Params() +: bg_image("bg_image"), + modal("modal", false), + text_color("text_color"), + can_close("can_close", true) +{ + mouse_opaque = false; +} + +LLWindowShade::LLWindowShade(const LLWindowShade::Params& params) +: LLUICtrl(params), + mNotification(params.notification), + mModal(params.modal), + mFormHeight(0), + mTextColor(params.text_color) +{ + setFocusRoot(true); +} + +void LLWindowShade::initFromParams(const LLWindowShade::Params& params) +{ + LLUICtrl::initFromParams(params); + + LLLayoutStack::Params layout_p; + layout_p.name = "notification_stack"; + layout_p.rect = params.rect; + layout_p.follows.flags = FOLLOWS_ALL; + layout_p.mouse_opaque = false; + layout_p.orientation = LLLayoutStack::VERTICAL; + layout_p.border_size = 0; + + LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p); + addChild(stackp); + + LLLayoutPanel::Params panel_p; + panel_p.rect = LLRect(0, 30, 800, 0); + panel_p.name = "notification_area"; + panel_p.visible = false; + panel_p.user_resize = false; + panel_p.background_visible = true; + panel_p.bg_alpha_image = params.bg_image; + panel_p.auto_resize = false; + LLLayoutPanel* notification_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); + stackp->addChild(notification_panel); + + panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); + panel_p.auto_resize = true; + panel_p.user_resize = false; + panel_p.rect = params.rect; + panel_p.name = "background_area"; + panel_p.mouse_opaque = false; + panel_p.background_visible = false; + panel_p.bg_alpha_color = LLColor4(0.f, 0.f, 0.f, 0.2f); + LLLayoutPanel* dummy_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); + stackp->addChild(dummy_panel); + + layout_p = LLUICtrlFactory::getDefaultParams<LLLayoutStack>(); + layout_p.rect = LLRect(0, 30, 800, 0); + layout_p.follows.flags = FOLLOWS_ALL; + layout_p.orientation = LLLayoutStack::HORIZONTAL; + stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p); + notification_panel->addChild(stackp); + + panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); + panel_p.rect.height = 30; + LLLayoutPanel* panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); + stackp->addChild(panel); + + LLIconCtrl::Params icon_p; + icon_p.name = "notification_icon"; + icon_p.rect = LLRect(5, 23, 21, 8); + panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p)); + + LLTextBox::Params text_p; + text_p.rect = LLRect(31, 20, panel->getRect().getWidth() - 5, 0); + text_p.follows.flags = FOLLOWS_ALL; + text_p.text_color = mTextColor; + text_p.font = LLFontGL::getFontSansSerifSmall(); + text_p.font.style = "BOLD"; + text_p.name = "notification_text"; + text_p.use_ellipses = true; + text_p.wrap = true; + panel->addChild(LLUICtrlFactory::create<LLTextBox>(text_p)); + + panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); + panel_p.auto_resize = false; + panel_p.user_resize = false; + panel_p.name="form_elements"; + panel_p.rect = LLRect(0, 30, 130, 0); + LLLayoutPanel* form_elements_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); + stackp->addChild(form_elements_panel); + + if (params.can_close) + { + panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); + panel_p.auto_resize = false; + panel_p.user_resize = false; + panel_p.rect = LLRect(0, 30, 25, 0); + LLLayoutPanel* close_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); + stackp->addChild(close_panel); + + LLButton::Params button_p; + button_p.name = "close_notification"; + button_p.rect = LLRect(5, 23, 21, 7); + button_p.image_color.control="DkGray_66"; + button_p.image_unselected.name="Icon_Close_Foreground"; + button_p.image_selected.name="Icon_Close_Press"; + button_p.click_callback.function = boost::bind(&LLWindowShade::onCloseNotification, this); + + close_panel->addChild(LLUICtrlFactory::create<LLButton>(button_p)); + } + + LLSD payload = mNotification->getPayload(); + + LLNotificationFormPtr formp = mNotification->getForm(); + LLLayoutPanel& notification_area = getChildRef<LLLayoutPanel>("notification_area"); + notification_area.getChild<LLUICtrl>("notification_icon")->setValue(mNotification->getIcon()); + notification_area.getChild<LLUICtrl>("notification_text")->setValue(mNotification->getMessage()); + notification_area.getChild<LLUICtrl>("notification_text")->setToolTip(mNotification->getMessage()); + + LLNotificationForm::EIgnoreType ignore_type = formp->getIgnoreType(); + LLLayoutPanel& form_elements = notification_area.getChildRef<LLLayoutPanel>("form_elements"); + form_elements.deleteAllChildren(); + + const S32 FORM_PADDING_HORIZONTAL = 10; + const S32 FORM_PADDING_VERTICAL = 3; + const S32 WIDGET_HEIGHT = 24; + const S32 LINE_EDITOR_WIDTH = 120; + S32 cur_x = FORM_PADDING_HORIZONTAL; + S32 cur_y = FORM_PADDING_VERTICAL + WIDGET_HEIGHT; + S32 form_width = cur_x; + + if (ignore_type != LLNotificationForm::IGNORE_NO) + { + LLCheckBoxCtrl::Params checkbox_p; + checkbox_p.name = "ignore_check"; + checkbox_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT); + checkbox_p.label = formp->getIgnoreMessage(); + checkbox_p.label_text.text_color = LLColor4::black; + checkbox_p.commit_callback.function = boost::bind(&LLWindowShade::onClickIgnore, this, _1); + checkbox_p.initial_value = formp->getIgnored(); + + LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p); + check->setRect(check->getBoundingRect()); + form_elements.addChild(check); + cur_x = check->getRect().mRight + FORM_PADDING_HORIZONTAL; + form_width = llmax(form_width, cur_x); + } + + for (S32 i = 0; i < formp->getNumElements(); i++) + { + LLSD form_element = formp->getElement(i); + std::string type = form_element["type"].asString(); + if (type == "button") + { + LLButton::Params button_p; + button_p.name = form_element["name"]; + button_p.label = form_element["text"]; + button_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT); + button_p.click_callback.function = boost::bind(&LLWindowShade::onClickNotificationButton, this, form_element["name"].asString()); + button_p.auto_resize = true; + + LLButton* button = LLUICtrlFactory::create<LLButton>(button_p); + button->autoResize(); + form_elements.addChild(button); + + if (form_element["default"].asBoolean()) + { + form_elements.setDefaultBtn(button); + } + + cur_x = button->getRect().mRight + FORM_PADDING_HORIZONTAL; + form_width = llmax(form_width, cur_x); + } + else if (type == "text" || type == "password") + { + // if not at beginning of line... + if (cur_x != FORM_PADDING_HORIZONTAL) + { + // start new line + cur_x = FORM_PADDING_HORIZONTAL; + cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL; + } + LLTextBox::Params label_p; + label_p.name = form_element["name"].asString() + "_label"; + label_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT); + label_p.initial_value = form_element["text"]; + label_p.text_color = mTextColor; + label_p.font_valign = LLFontGL::VCENTER; + label_p.v_pad = 5; + LLTextBox* textbox = LLUICtrlFactory::create<LLTextBox>(label_p); + textbox->reshapeToFitText(); + textbox->reshape(textbox->getRect().getWidth(), form_elements.getRect().getHeight() - 2 * FORM_PADDING_VERTICAL); + form_elements.addChild(textbox); + cur_x = textbox->getRect().mRight + FORM_PADDING_HORIZONTAL; + + LLLineEditor::Params line_p; + line_p.name = form_element["name"]; + line_p.keystroke_callback = boost::bind(&LLWindowShade::onEnterNotificationText, this, _1, form_element["name"].asString()); + line_p.is_password = type == "password"; + line_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT); + + LLLineEditor* line_editor = LLUICtrlFactory::create<LLLineEditor>(line_p); + form_elements.addChild(line_editor); + form_width = llmax(form_width, cur_x + LINE_EDITOR_WIDTH + FORM_PADDING_HORIZONTAL); + + // reset to start of next line + cur_x = FORM_PADDING_HORIZONTAL; + cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL; + } + } + + mFormHeight = form_elements.getRect().getHeight() - (cur_y - FORM_PADDING_VERTICAL) + WIDGET_HEIGHT; + form_elements.reshape(form_width, mFormHeight); + form_elements.setMinDim(form_width); + + // move all form elements back onto form surface + S32 delta_y = WIDGET_HEIGHT + FORM_PADDING_VERTICAL - cur_y; + for (child_list_const_iter_t it = form_elements.getChildList()->begin(), end_it = form_elements.getChildList()->end(); + it != end_it; + ++it) + { + (*it)->translate(0, delta_y); + } +} + +void LLWindowShade::show() +{ + getChildRef<LLLayoutPanel>("notification_area").setVisible(true); + getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(mModal); + + setMouseOpaque(mModal); +} + +void LLWindowShade::draw() +{ + LLRect message_rect = getChild<LLTextBox>("notification_text")->getTextBoundingRect(); + + LLLayoutPanel* notification_area = getChild<LLLayoutPanel>("notification_area"); + + notification_area->reshape(notification_area->getRect().getWidth(), + llclamp(message_rect.getHeight() + 10, + llmin(mFormHeight, MAX_NOTIFICATION_AREA_HEIGHT), + MAX_NOTIFICATION_AREA_HEIGHT)); + + LLUICtrl::draw(); + if (mNotification && !mNotification->isActive()) + { + hide(); + } +} + +void LLWindowShade::hide() +{ + getChildRef<LLLayoutPanel>("notification_area").setVisible(false); + getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(false); + + setMouseOpaque(false); +} + +void LLWindowShade::onCloseNotification() +{ + LLNotifications::instance().cancel(mNotification); +} + +void LLWindowShade::onClickIgnore(LLUICtrl* ctrl) +{ + bool check = ctrl->getValue().asBoolean(); + if (mNotification && mNotification->getForm()->getIgnoreType() == LLNotificationForm::IGNORE_SHOW_AGAIN) + { + // question was "show again" so invert value to get "ignore" + check = !check; + } + mNotification->setIgnored(check); +} + +void LLWindowShade::onClickNotificationButton(const std::string& name) +{ + if (!mNotification) return; + + mNotificationResponse[name] = true; + + mNotification->respond(mNotificationResponse); +} + +void LLWindowShade::onEnterNotificationText(LLUICtrl* ctrl, const std::string& name) +{ + mNotificationResponse[name] = ctrl->getValue().asString(); +} diff --git a/indra/llui/llwindowshade.h b/indra/llui/llwindowshade.h new file mode 100644 index 0000000000..0047195929 --- /dev/null +++ b/indra/llui/llwindowshade.h @@ -0,0 +1,69 @@ +/** + * @file llwindowshade.h + * @brief Notification dialog that slides down and optionally disabled a piece of UI + * + * $LicenseInfo:firstyear=2006&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_LLWINDOWSHADE_H +#define LL_LLWINDOWSHADE_H + +#include "lluictrl.h" +#include "llnotifications.h" + +class LLWindowShade : public LLUICtrl +{ +public: + struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> + { + Mandatory<LLNotificationPtr> notification; + Optional<LLUIImage*> bg_image; + Optional<LLUIColor> text_color; + Optional<bool> modal, + can_close; + + Params(); + }; + + void show(); + /*virtual*/ void draw(); + void hide(); + +private: + friend class LLUICtrlFactory; + + LLWindowShade(const Params& p); + void initFromParams(const Params& params); + + void onCloseNotification(); + void onClickNotificationButton(const std::string& name); + void onEnterNotificationText(LLUICtrl* ctrl, const std::string& name); + void onClickIgnore(LLUICtrl* ctrl); + + LLNotificationPtr mNotification; + LLSD mNotificationResponse; + bool mModal; + S32 mFormHeight; + LLUIColor mTextColor; +}; + +#endif // LL_LLWINDOWSHADE_H diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 5c6623da61..8f0a48018f 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -27,6 +27,7 @@ #include "linden_common.h" #include "../llurlentry.h" +#include "../lluictrl.h" #include "llurlentry_stub.cpp" #include "lltut.h" #include "../lluicolortable.h" @@ -34,6 +35,14 @@ #include <boost/regex.hpp> +typedef std::map<std::string, LLControlGroup*> settings_map_t; +settings_map_t LLUI::sSettingGroups; + +BOOL LLControlGroup::getBOOL(const std::string& name) +{ + return false; +} + LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const { return LLUIColor(); @@ -94,6 +103,45 @@ namespace tut ensure_equals(testname, url, expected); } + void dummyCallback(const std::string &url, const std::string &label, const std::string& icon) + { + } + + void testLabel(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) + { + boost::regex regex = entry.getPattern(); + std::string label = ""; + boost::cmatch result; + bool found = boost::regex_search(text, result, regex); + if (found) + { + S32 start = static_cast<U32>(result[0].first - text); + S32 end = static_cast<U32>(result[0].second - text); + std::string url = std::string(text+start, end-start); + label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3)); + } + ensure_equals(testname, label, expected); + } + + void testLocation(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) + { + boost::regex regex = entry.getPattern(); + std::string location = ""; + boost::cmatch result; + bool found = boost::regex_search(text, result, regex); + if (found) + { + S32 start = static_cast<U32>(result[0].first - text); + S32 end = static_cast<U32>(result[0].second - text); + std::string url = std::string(text+start, end-start); + location = entry.getLocation(url); + } + ensure_equals(testname, location, expected); + } + + template<> template<> void object::test<1>() { @@ -688,4 +736,114 @@ namespace tut "<nolink>My Object</nolink>", "My Object"); } + + template<> template<> + void object::test<13>() + { + // + // test LLUrlEntryRegion - secondlife:///app/region/<location> URLs + // + LLUrlEntryRegion url; + + // Regex tests. + testRegex("no valid region", url, + "secondlife:///app/region/", + ""); + + testRegex("invalid coords", url, + "secondlife:///app/region/Korea2/a/b/c", + "secondlife:///app/region/Korea2/"); // don't count invalid coords + + testRegex("Ahern (50,50,50) [1]", url, + "secondlife:///app/region/Ahern/50/50/50/", + "secondlife:///app/region/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [2]", url, + "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", + "secondlife:///app/region/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [3]", url, + "XXX secondlife:///app/region/Ahern/50/50/50 XXX", + "secondlife:///app/region/Ahern/50/50/50"); + + testRegex("Ahern (50,50,50) multicase", url, + "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", + "secondlife:///app/region/Ahern/50/50/50/"); + + testRegex("Ahern (50,50) [1]", url, + "XXX secondlife:///app/region/Ahern/50/50/ XXX", + "secondlife:///app/region/Ahern/50/50/"); + + testRegex("Ahern (50,50) [2]", url, + "XXX secondlife:///app/region/Ahern/50/50 XXX", + "secondlife:///app/region/Ahern/50/50"); + + // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat + testRegex("Region with brackets", url, + "XXX secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30 XXX", + "secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30"); + + // DEV-35459: SLURLs and teleport Links not parsed properly + testRegex("Region with quote", url, + "XXX secondlife:///app/region/A'ksha%20Oasis/41/166/701 XXX", + "secondlife:///app/region/A%27ksha%20Oasis/41/166/701"); + + // Rendering tests. + testLabel("Render /app/region/Ahern/50/50/50/", url, + "secondlife:///app/region/Ahern/50/50/50/", + "Ahern (50,50,50)"); + + testLabel("Render /app/region/Ahern/50/50/50", url, + "secondlife:///app/region/Ahern/50/50/50", + "Ahern (50,50,50)"); + + testLabel("Render /app/region/Ahern/50/50/", url, + "secondlife:///app/region/Ahern/50/50/", + "Ahern (50,50)"); + + testLabel("Render /app/region/Ahern/50/50", url, + "secondlife:///app/region/Ahern/50/50", + "Ahern (50,50)"); + + testLabel("Render /app/region/Ahern/50/", url, + "secondlife:///app/region/Ahern/50/", + "Ahern (50)"); + + testLabel("Render /app/region/Ahern/50", url, + "secondlife:///app/region/Ahern/50", + "Ahern (50)"); + + testLabel("Render /app/region/Ahern/", url, + "secondlife:///app/region/Ahern/", + "Ahern"); + + testLabel("Render /app/region/Ahern/ within context", url, + "XXX secondlife:///app/region/Ahern/ XXX", + "Ahern"); + + testLabel("Render /app/region/Ahern", url, + "secondlife:///app/region/Ahern", + "Ahern"); + + testLabel("Render /app/region/Ahern within context", url, + "XXX secondlife:///app/region/Ahern XXX", + "Ahern"); + + testLabel("Render /app/region/Product%20Engine/", url, + "secondlife:///app/region/Product%20Engine/", + "Product Engine"); + + testLabel("Render /app/region/Product%20Engine", url, + "secondlife:///app/region/Product%20Engine", + "Product Engine"); + + // Location parsing texts. + testLocation("Location /app/region/Ahern/50/50/50/", url, + "secondlife:///app/region/Ahern/50/50/50/", + "Ahern"); + + testLocation("Location /app/region/Product%20Engine", url, + "secondlife:///app/region/Product%20Engine", + "Product Engine"); + } } diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 938fb008c9..64556bcb4c 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -83,7 +83,7 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask) std::string filename; std::string fullpath; S32 result; - while (getNextFileInDir(dirname, mask, filename, FALSE)) + while (getNextFileInDir(dirname, mask, filename)) { fullpath = dirname; fullpath += getDirDelimiter(); @@ -382,11 +382,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_USER_SKIN: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "user_settings"; - prefix += mDirDelimiter; - prefix += "skins"; + prefix = getUserSkinDir(); break; case LL_PATH_SKINS: diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 4f63c04aab..42996fd051 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -74,8 +74,32 @@ class LLDir // pure virtual functions virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask) = 0; - virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) = 0; - virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) = 0; + + /// Walk the files in a directory, with file pattern matching + virtual BOOL getNextFileInDir(const std::string& dirname, ///< directory path - must end in trailing slash! + const std::string& mask, ///< file pattern string (use "*" for all) + std::string& fname ///< output: found file name + ) = 0; + /**< + * @returns true if a file was found, false if the entire directory has been scanned. + * + * @note that this function is NOT thread safe + * + * This function may not be used to scan part of a directory, then start a new search of a different + * directory, and then restart the first search where it left off; the entire search must run to + * completion or be abandoned - there is no restart. + * + * @bug: See http://jira.secondlife.com/browse/VWR-23697 + * and/or the tests in test/lldir_test.cpp + * This is known to fail with patterns that have both: + * a wildcard left of a . and more than one sequential ? right of a . + * the pattern foo.??x appears to work + * but *.??x or foo?.??x do not + * + * @todo this really should be rewritten as an iterator object, and the + * filtering should be done in a platform-independent way. + */ + virtual std::string getCurPath() = 0; virtual BOOL fileExists(const std::string &filename) const = 0; diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp index a1c6669b97..73f2336f94 100644 --- a/indra/llvfs/lldir_linux.cpp +++ b/indra/llvfs/lldir_linux.cpp @@ -243,8 +243,7 @@ U32 LLDir_Linux::countFilesInDir(const std::string &dirname, const std::string & } // get the next file in the directory -// automatically wrap if we've hit the end -BOOL LLDir_Linux::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) +BOOL LLDir_Linux::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) { glob_t g; BOOL result = FALSE; @@ -276,11 +275,6 @@ BOOL LLDir_Linux::getNextFileInDir(const std::string &dirname, const std::string mCurrentDirIndex++; - if((mCurrentDirIndex >= (int)g.gl_pathc) && wrap) - { - mCurrentDirIndex = 0; - } - if(mCurrentDirIndex < (int)g.gl_pathc) { // llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl; @@ -308,47 +302,7 @@ BOOL LLDir_Linux::getNextFileInDir(const std::string &dirname, const std::string } -// get a random file in the directory -// automatically wrap if we've hit the end -void LLDir_Linux::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) -{ - S32 num_files; - S32 which_file; - DIR *dirp; - dirent *entryp = NULL; - - fname = ""; - num_files = countFilesInDir(dirname,mask); - if (!num_files) - { - return; - } - - which_file = ll_rand(num_files); - -// llinfos << "Random select file #" << which_file << llendl; - - // which_file now indicates the (zero-based) index to which file to play - - if (!((dirp = opendir(dirname.c_str())))) - { - while (which_file--) - { - if (!((entryp = readdir(dirp)))) - { - return; - } - } - - if ((!which_file) && entryp) - { - fname = entryp->d_name; - } - - closedir(dirp); - } -} std::string LLDir_Linux::getCurPath() { diff --git a/indra/llvfs/lldir_linux.h b/indra/llvfs/lldir_linux.h index 809959e873..451e81ae93 100644 --- a/indra/llvfs/lldir_linux.h +++ b/indra/llvfs/lldir_linux.h @@ -43,8 +43,7 @@ public: public: virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap); - virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); + virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); /*virtual*/ BOOL fileExists(const std::string &filename) const; /*virtual*/ std::string getLLPluginLauncher(); diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llvfs/lldir_mac.cpp index b41b0ec5dd..445285aa43 100644 --- a/indra/llvfs/lldir_mac.cpp +++ b/indra/llvfs/lldir_mac.cpp @@ -259,8 +259,7 @@ U32 LLDir_Mac::countFilesInDir(const std::string &dirname, const std::string &ma } // get the next file in the directory -// automatically wrap if we've hit the end -BOOL LLDir_Mac::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) +BOOL LLDir_Mac::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) { glob_t g; BOOL result = FALSE; @@ -292,11 +291,6 @@ BOOL LLDir_Mac::getNextFileInDir(const std::string &dirname, const std::string & mCurrentDirIndex++; - if((mCurrentDirIndex >= g.gl_pathc) && wrap) - { - mCurrentDirIndex = 0; - } - if(mCurrentDirIndex < g.gl_pathc) { // llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl; @@ -323,41 +317,7 @@ BOOL LLDir_Mac::getNextFileInDir(const std::string &dirname, const std::string & return(result); } -// get a random file in the directory -void LLDir_Mac::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) -{ - S32 which_file; - glob_t g; - fname = ""; - - std::string tmp_str; - tmp_str = dirname; - tmp_str += mask; - - if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0) - { - if(g.gl_pathc > 0) - { - - which_file = ll_rand(g.gl_pathc); - -// llinfos << "getRandomFileInDir: returning number " << which_file << ", path is " << g.gl_pathv[which_file] << llendl; - // The API wants just the filename, not the full path. - //fname = g.gl_pathv[which_file]; - char *s = strrchr(g.gl_pathv[which_file], '/'); - - if(s == NULL) - s = g.gl_pathv[which_file]; - else if(s[0] == '/') - s++; - - fname = s; - } - - globfree(&g); - } -} S32 LLDir_Mac::deleteFilesInDir(const std::string &dirname, const std::string &mask) { diff --git a/indra/llvfs/lldir_mac.h b/indra/llvfs/lldir_mac.h index 04c52dc940..4eac3c3ae6 100644 --- a/indra/llvfs/lldir_mac.h +++ b/indra/llvfs/lldir_mac.h @@ -43,8 +43,7 @@ public: virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask); virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap); - virtual void getRandomFileInDir(const std::string &dirname, const std::string &ask, std::string &fname); + virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); virtual BOOL fileExists(const std::string &filename) const; /*virtual*/ std::string getLLPluginLauncher(); diff --git a/indra/llvfs/lldir_solaris.cpp b/indra/llvfs/lldir_solaris.cpp index 4323dfd44a..515fd66b6e 100644 --- a/indra/llvfs/lldir_solaris.cpp +++ b/indra/llvfs/lldir_solaris.cpp @@ -261,8 +261,7 @@ U32 LLDir_Solaris::countFilesInDir(const std::string &dirname, const std::string } // get the next file in the directory -// automatically wrap if we've hit the end -BOOL LLDir_Solaris::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) +BOOL LLDir_Solaris::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) { glob_t g; BOOL result = FALSE; @@ -294,11 +293,6 @@ BOOL LLDir_Solaris::getNextFileInDir(const std::string &dirname, const std::stri mCurrentDirIndex++; - if((mCurrentDirIndex >= (int)g.gl_pathc) && wrap) - { - mCurrentDirIndex = 0; - } - if(mCurrentDirIndex < (int)g.gl_pathc) { // llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl; @@ -326,47 +320,7 @@ BOOL LLDir_Solaris::getNextFileInDir(const std::string &dirname, const std::stri } -// get a random file in the directory -// automatically wrap if we've hit the end -void LLDir_Solaris::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) -{ - S32 num_files; - S32 which_file; - DIR *dirp; - dirent *entryp = NULL; - - fname = ""; - - num_files = countFilesInDir(dirname,mask); - if (!num_files) - { - return; - } - - which_file = ll_rand(num_files); - -// llinfos << "Random select file #" << which_file << llendl; - // which_file now indicates the (zero-based) index to which file to play - - if (!((dirp = opendir(dirname.c_str())))) - { - while (which_file--) - { - if (!((entryp = readdir(dirp)))) - { - return; - } - } - - if ((!which_file) && entryp) - { - fname = entryp->d_name; - } - - closedir(dirp); - } -} std::string LLDir_Solaris::getCurPath() { diff --git a/indra/llvfs/lldir_solaris.h b/indra/llvfs/lldir_solaris.h index 6e0c5cfc69..4a1794f539 100644 --- a/indra/llvfs/lldir_solaris.h +++ b/indra/llvfs/lldir_solaris.h @@ -43,8 +43,7 @@ public: public: virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap); - virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); + virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); /*virtual*/ BOOL fileExists(const std::string &filename) const; private: diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp index 52d864e26f..33718e520d 100644 --- a/indra/llvfs/lldir_win32.cpp +++ b/indra/llvfs/lldir_win32.cpp @@ -206,14 +206,6 @@ void LLDir_Win32::initAppDirs(const std::string &app_name, } } - res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SKIN,"")); - if (res == -1) - { - if (errno != EEXIST) - { - llwarns << "Couldn't create LL_PATH_SKINS dir " << getExpandedFilename(LL_PATH_USER_SKIN,"") << llendl; - } - } mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); } @@ -246,21 +238,13 @@ U32 LLDir_Win32::countFilesInDir(const std::string &dirname, const std::string & // get the next file in the directory -// automatically wrap if we've hit the end -BOOL LLDir_Win32::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) +BOOL LLDir_Win32::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) { - llutf16string dirnamew = utf8str_to_utf16str(dirname); - return getNextFileInDir(dirnamew, mask, fname, wrap); - -} + BOOL fileFound = FALSE; + fname = ""; -BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname, BOOL wrap) -{ WIN32_FIND_DATAW FileData; - - fname = ""; - llutf16string pathname = dirname; - pathname += utf8str_to_utf16str(mask); + llutf16string pathname = utf8str_to_utf16str(dirname) + utf8str_to_utf16str(mask); if (pathname != mCurrentDir) { @@ -273,91 +257,44 @@ BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::stri // and open new one // Check error opening Directory structure - if ((mDirSearch_h = FindFirstFile(pathname.c_str(), &FileData)) == INVALID_HANDLE_VALUE) - { -// llinfos << "Unable to locate first file" << llendl; - return(FALSE); - } - } - else // get next file in list - { - // Find next entry - if (!FindNextFile(mDirSearch_h, &FileData)) + if ((mDirSearch_h = FindFirstFile(pathname.c_str(), &FileData)) != INVALID_HANDLE_VALUE) { - if (GetLastError() == ERROR_NO_MORE_FILES) - { - // No more files, so reset to beginning of directory - FindClose(mDirSearch_h); - mCurrentDir[0] = NULL; - - if (wrap) - { - return(getNextFileInDir(pathname,"",fname,TRUE)); - } - else - { - fname[0] = 0; - return(FALSE); - } - } - else - { - // Error -// llinfos << "Unable to locate next file" << llendl; - return(FALSE); - } + fileFound = TRUE; } } - // convert from TCHAR to char - fname = utf16str_to_utf8str(FileData.cFileName); - - // fname now first name in list - return(TRUE); -} - - -// get a random file in the directory -// automatically wrap if we've hit the end -void LLDir_Win32::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) -{ - S32 num_files; - S32 which_file; - HANDLE random_search_h; - - fname = ""; - - llutf16string pathname = utf8str_to_utf16str(dirname); - pathname += utf8str_to_utf16str(mask); - - WIN32_FIND_DATA FileData; - fname[0] = NULL; - - num_files = countFilesInDir(dirname,mask); - if (!num_files) - { - return; - } - - which_file = ll_rand(num_files); - -// llinfos << "Random select mp3 #" << which_file << llendl; - - // which_file now indicates the (zero-based) index to which file to play + // Loop to skip over the current (.) and parent (..) directory entries + // (apparently returned in Win7 but not XP) + do + { + if ( fileFound + && ( (lstrcmp(FileData.cFileName, (LPCTSTR)TEXT(".")) == 0) + ||(lstrcmp(FileData.cFileName, (LPCTSTR)TEXT("..")) == 0) + ) + ) + { + fileFound = FALSE; + } + } while ( mDirSearch_h != INVALID_HANDLE_VALUE + && !fileFound + && (fileFound = FindNextFile(mDirSearch_h, &FileData) + ) + ); + + if (!fileFound && GetLastError() == ERROR_NO_MORE_FILES) + { + // No more files, so reset to beginning of directory + FindClose(mDirSearch_h); + mCurrentDir[0] = '\000'; + } - if ((random_search_h = FindFirstFile(pathname.c_str(), &FileData)) != INVALID_HANDLE_VALUE) - { - while (which_file--) - { - if (!FindNextFile(random_search_h, &FileData)) - { - return; - } - } - FindClose(random_search_h); - - fname = utf16str_to_utf8str(llutf16string(FileData.cFileName)); + if (fileFound) + { + // convert from TCHAR to char + fname = utf16str_to_utf8str(FileData.cFileName); } + + return fileFound; } std::string LLDir_Win32::getCurPath() diff --git a/indra/llvfs/lldir_win32.h b/indra/llvfs/lldir_win32.h index d3e45dc1f3..4c932c932c 100644 --- a/indra/llvfs/lldir_win32.h +++ b/indra/llvfs/lldir_win32.h @@ -40,15 +40,14 @@ public: /*virtual*/ std::string getCurPath(); /*virtual*/ U32 countFilesInDir(const std::string &dirname, const std::string &mask); - /*virtual*/ BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap); - /*virtual*/ void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); + /*virtual*/ BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); /*virtual*/ BOOL fileExists(const std::string &filename) const; /*virtual*/ std::string getLLPluginLauncher(); /*virtual*/ std::string getLLPluginFilename(std::string base_name); private: - BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname, BOOL wrap); + BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname); void* mDirSearch_h; llutf16string mCurrentDir; diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index bcffa449c8..8788bd63e8 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -256,5 +256,160 @@ namespace tut gDirUtilp->getExtension(dottedPathExt), "ext"); } + + std::string makeTestFile( const std::string& dir, const std::string& file ) + { + std::string delim = gDirUtilp->getDirDelimiter(); + std::string path = dir + delim + file; + LLFILE* handle = LLFile::fopen( path, "w" ); + ensure("failed to open test file '"+path+"'", handle != NULL ); + // Harbison & Steele, 4th ed., p. 366: "If an error occurs, fputs + // returns EOF; otherwise, it returns some other, nonnegative value." + ensure("failed to write to test file '"+path+"'", fputs("test file", handle) >= 0); + fclose(handle); + return path; + } + + std::string makeTestDir( const std::string& dirbase ) + { + int counter; + std::string uniqueDir; + bool foundUnused; + std::string delim = gDirUtilp->getDirDelimiter(); + + for (counter=0, foundUnused=false; !foundUnused; counter++ ) + { + char counterStr[3]; + sprintf(counterStr, "%02d", counter); + uniqueDir = dirbase + counterStr; + foundUnused = ! ( LLFile::isdir(uniqueDir) || LLFile::isfile(uniqueDir) ); + } + ensure("test directory '" + uniqueDir + "' creation failed", !LLFile::mkdir(uniqueDir)); + + return uniqueDir + delim; // HACK - apparently, the trailing delimiter is needed... + } + + static const char* DirScanFilename[5] = { "file1.abc", "file2.abc", "file1.xyz", "file2.xyz", "file1.mno" }; + + void scanTest(const std::string& directory, const std::string& pattern, bool correctResult[5]) + { + + // Scan directory and see if any file1.* files are found + std::string scanResult; + int found = 0; + bool filesFound[5] = { false, false, false, false, false }; + //std::cerr << "searching '"+directory+"' for '"+pattern+"'\n"; + + while ( found <= 5 && gDirUtilp->getNextFileInDir(directory, pattern, scanResult) ) + { + found++; + //std::cerr << " found '"+scanResult+"'\n"; + int check; + for (check=0; check < 5 && ! ( scanResult == DirScanFilename[check] ); check++) + { + } + // check is now either 5 (not found) or the index of the matching name + if (check < 5) + { + ensure( "found file '"+(std::string)DirScanFilename[check]+"' twice", ! filesFound[check] ); + filesFound[check] = true; + } + else // check is 5 - should not happen + { + fail( "found unknown file '"+scanResult+"'"); + } + } + for (int i=0; i<5; i++) + { + if (correctResult[i]) + { + ensure("scan of '"+directory+"' using '"+pattern+"' did not return '"+DirScanFilename[i]+"'", filesFound[i]); + } + else + { + ensure("scan of '"+directory+"' using '"+pattern+"' incorrectly returned '"+DirScanFilename[i]+"'", !filesFound[i]); + } + } + } + + template<> template<> + void LLDirTest_object_t::test<5>() + // getNextFileInDir + { + std::string delim = gDirUtilp->getDirDelimiter(); + std::string dirTemp = LLFile::tmpdir(); + + // Create the same 5 file names of the two directories + + std::string dir1 = makeTestDir(dirTemp + "getNextFileInDir"); + std::string dir2 = makeTestDir(dirTemp + "getNextFileInDir"); + std::string dir1files[5]; + std::string dir2files[5]; + for (int i=0; i<5; i++) + { + dir1files[i] = makeTestFile(dir1, DirScanFilename[i]); + dir2files[i] = makeTestFile(dir2, DirScanFilename[i]); + } + + // Scan dir1 and see if each of the 5 files is found exactly once + bool expected1[5] = { true, true, true, true, true }; + scanTest(dir1, "*", expected1); + + // Scan dir2 and see if only the 2 *.xyz files are found + bool expected2[5] = { false, false, true, true, false }; + scanTest(dir1, "*.xyz", expected2); + + // Scan dir2 and see if only the 1 *.mno file is found + bool expected3[5] = { false, false, false, false, true }; + scanTest(dir2, "*.mno", expected3); + + // Scan dir1 and see if any *.foo files are found + bool expected4[5] = { false, false, false, false, false }; + scanTest(dir1, "*.foo", expected4); + + // Scan dir1 and see if any file1.* files are found + bool expected5[5] = { true, false, true, false, true }; + scanTest(dir1, "file1.*", expected5); + + // Scan dir1 and see if any file1.* files are found + bool expected6[5] = { true, true, false, false, false }; + scanTest(dir1, "file?.abc", expected6); + + // Scan dir2 and see if any file?.x?z files are found + bool expected7[5] = { false, false, true, true, false }; + scanTest(dir2, "file?.x?z", expected7); + + // Scan dir2 and see if any file?.??c files are found + // THESE FAIL ON Mac and Windows, SO ARE COMMENTED OUT FOR NOW + // bool expected8[5] = { true, true, false, false, false }; + // scanTest(dir2, "file?.??c", expected8); + // scanTest(dir2, "*.??c", expected8); + + // Scan dir1 and see if any *.?n? files are found + bool expected9[5] = { false, false, false, false, true }; + scanTest(dir1, "*.?n?", expected9); + + // Scan dir1 and see if any *.???? files are found + // THIS ONE FAILS ON WINDOWS (returns three charater suffixes) SO IS COMMENTED OUT FOR NOW + // bool expected10[5] = { false, false, false, false, false }; + // scanTest(dir1, "*.????", expected10); + + // Scan dir1 and see if any ?????.* files are found + bool expected11[5] = { true, true, true, true, true }; + scanTest(dir1, "?????.*", expected11); + + // Scan dir1 and see if any ??l??.xyz files are found + bool expected12[5] = { false, false, true, true, false }; + scanTest(dir1, "??l??.xyz", expected12); + + // clean up all test files and directories + for (int i=0; i<5; i++) + { + LLFile::remove(dir1files[i]); + LLFile::remove(dir2files[i]); + } + LLFile::rmdir(dir1); + LLFile::rmdir(dir2); + } } diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index bf3233f386..4d2677fd91 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -59,12 +59,13 @@ set(viewer_HEADER_FILES # Libraries on which this library depends, needed for Linux builds # Sort by high-level to low-level -if (NOT LINUX OR VIEWER) +if (LINUX AND VIEWER) set(llwindow_LINK_LIBRARIES ${UI_LIBRARIES} # for GTK ${SDL_LIBRARY} + fontconfig # For FCInit and other FC* functions. ) -endif (NOT LINUX OR VIEWER) +endif (LINUX AND VIEWER) if (DARWIN) list(APPEND llwindow_SOURCE_FILES diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index be16f31abc..ba472cfde5 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -40,7 +40,7 @@ enum EKeystate KEYSTATE_UP }; -typedef void (*LLKeyFunc)(EKeystate keystate); +typedef boost::function<void(EKeystate keystate)> LLKeyFunc; typedef std::string (LLKeyStringTranslatorFunc)(const char *label); enum EKeyboardInsertMode diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 87075c7318..ab089081e6 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -544,7 +544,27 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, if (closest_refresh == 0) { LL_WARNS("Window") << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << LL_ENDL; - success = FALSE; + //success = FALSE; + + if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode)) + { + success = FALSE; + } + else + { + if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL) + { + LL_WARNS("Window") << "Current BBP is OK falling back to that" << LL_ENDL; + window_rect.right=width=dev_mode.dmPelsWidth; + window_rect.bottom=height=dev_mode.dmPelsHeight; + success = TRUE; + } + else + { + LL_WARNS("Window") << "Current BBP is BAD" << LL_ENDL; + success = FALSE; + } + } } // If we found a good resolution, use it. diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp index f9a39826f5..6c72609122 100644 --- a/indra/llxml/llcontrol.cpp +++ b/indra/llxml/llcontrol.cpp @@ -170,6 +170,20 @@ LLSD LLControlVariable::getComparableValue(const LLSD& value) storable_value = false; } } + else if (TYPE_LLSD == type() && value.isString()) + { + LLPointer<LLSDNotationParser> parser = new LLSDNotationParser; + LLSD result; + std::stringstream value_stream(value.asString()); + if (parser->parse(value_stream, result, LLSDSerialize::SIZE_UNLIMITED) != LLSDParser::PARSE_FAILURE) + { + storable_value = result; + } + else + { + storable_value = value; + } + } else { storable_value = value; @@ -1107,7 +1121,7 @@ bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, const std::strin return sd.asBoolean(); else { - CONTROL_ERRS << "Invalid BOOL value" << llendl; + CONTROL_ERRS << "Invalid BOOL value for " << control_name << ": " << sd << llendl; return FALSE; } } @@ -1119,7 +1133,7 @@ S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, const std::string& return sd.asInteger(); else { - CONTROL_ERRS << "Invalid S32 value" << llendl; + CONTROL_ERRS << "Invalid S32 value for " << control_name << ": " << sd << llendl; return 0; } } @@ -1131,7 +1145,7 @@ U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, const std::string& return sd.asInteger(); else { - CONTROL_ERRS << "Invalid U32 value" << llendl; + CONTROL_ERRS << "Invalid U32 value for " << control_name << ": " << sd << llendl; return 0; } } @@ -1143,7 +1157,7 @@ F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, const std::string& return (F32) sd.asReal(); else { - CONTROL_ERRS << "Invalid F32 value" << llendl; + CONTROL_ERRS << "Invalid F32 value for " << control_name << ": " << sd << llendl; return 0.0f; } } @@ -1155,7 +1169,7 @@ std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, co return sd.asString(); else { - CONTROL_ERRS << "Invalid string value" << llendl; + CONTROL_ERRS << "Invalid string value for " << control_name << ": " << sd << llendl; return LLStringUtil::null; } } @@ -1173,7 +1187,7 @@ LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, const return (LLVector3)sd; else { - CONTROL_ERRS << "Invalid LLVector3 value" << llendl; + CONTROL_ERRS << "Invalid LLVector3 value for " << control_name << ": " << sd << llendl; return LLVector3::zero; } } @@ -1185,7 +1199,7 @@ LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, cons return (LLVector3d)sd; else { - CONTROL_ERRS << "Invalid LLVector3d value" << llendl; + CONTROL_ERRS << "Invalid LLVector3d value for " << control_name << ": " << sd << llendl; return LLVector3d::zero; } } @@ -1197,7 +1211,7 @@ LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, const std::s return LLRect(sd); else { - CONTROL_ERRS << "Invalid rect value" << llendl; + CONTROL_ERRS << "Invalid rect value for " << control_name << ": " << sd << llendl; return LLRect::null; } } @@ -1211,19 +1225,19 @@ LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, const st LLColor4 color(sd); if (color.mV[VRED] < 0.f || color.mV[VRED] > 1.f) { - llwarns << "Color " << control_name << " value out of range " << llendl; + llwarns << "Color " << control_name << " red value out of range: " << color << llendl; } else if (color.mV[VGREEN] < 0.f || color.mV[VGREEN] > 1.f) { - llwarns << "Color " << control_name << " value out of range " << llendl; + llwarns << "Color " << control_name << " green value out of range: " << color << llendl; } else if (color.mV[VBLUE] < 0.f || color.mV[VBLUE] > 1.f) { - llwarns << "Color " << control_name << " value out of range " << llendl; + llwarns << "Color " << control_name << " blue value out of range: " << color << llendl; } else if (color.mV[VALPHA] < 0.f || color.mV[VALPHA] > 1.f) { - llwarns << "Color " << control_name << " value out of range " << llendl; + llwarns << "Color " << control_name << " alpha value out of range: " << color << llendl; } return LLColor4(sd); @@ -1242,7 +1256,7 @@ LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, const st return sd; else { - CONTROL_ERRS << "Invalid LLColor3 value" << llendl; + CONTROL_ERRS << "Invalid LLColor3 value for " << control_name << ": " << sd << llendl; return LLColor3::white; } } diff --git a/indra/llxuixml/llinitparam.cpp b/indra/llxuixml/llinitparam.cpp index 2c92539387..fcdbaa4309 100644 --- a/indra/llxuixml/llinitparam.cpp +++ b/indra/llxuixml/llinitparam.cpp @@ -312,6 +312,14 @@ namespace LLInitParam } } + // if no match, and no names left on stack, this is just an existence assertion of this block + // verify by calling readValue with NoParamValue type, an inherently unparseable type + if (!names_left) + { + NoParamValue no_value; + return p.readValue(no_value); + } + return false; } diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h index 8cb5bd80fc..1f9045754a 100644 --- a/indra/llxuixml/llinitparam.h +++ b/indra/llxuixml/llinitparam.h @@ -278,6 +278,9 @@ namespace LLInitParam S32 mParseGeneration; }; + // used to indicate no matching value to a given name when parsing + struct NoParamValue{}; + class BaseBlock; class Param @@ -303,8 +306,8 @@ namespace LLInitParam private: friend class BaseBlock; - bool mIsProvided; U16 mEnclosingBlockOffset; + bool mIsProvided; }; // various callbacks and constraints associated with an individual param diff --git a/indra/llxuixml/llregistry.h b/indra/llxuixml/llregistry.h index eee9933739..36ce6a97b7 100644 --- a/indra/llxuixml/llregistry.h +++ b/indra/llxuixml/llregistry.h @@ -343,4 +343,9 @@ private: ScopedRegistrar* mStaticScope; }; +// helper macro for doing static registration +#define GLUED_TOKEN(x, y) x ## y +#define GLUE_TOKENS(x, y) GLUED_TOKEN(x, y) +#define LLREGISTER_STATIC(REGISTRY, KEY, VALUE) static REGISTRY::StaticRegistrar GLUE_TOKENS(reg, __LINE__)(KEY, VALUE); + #endif diff --git a/indra/llxuixml/llxuiparser.cpp b/indra/llxuixml/llxuiparser.cpp index 9942af6b37..72a7bb7af5 100644 --- a/indra/llxuixml/llxuiparser.cpp +++ b/indra/llxuixml/llxuiparser.cpp @@ -388,6 +388,7 @@ LLXUIParser::LLXUIParser() { if (sXUIReadFuncs.empty()) { + registerParserFuncs<LLInitParam::NoParamValue>(readNoValue, writeNoValue); registerParserFuncs<bool>(readBoolValue, writeBoolValue); registerParserFuncs<std::string>(readStringValue, writeStringValue); registerParserFuncs<U8>(readU8Value, writeU8Value); @@ -406,6 +407,7 @@ LLXUIParser::LLXUIParser() } static LLFastTimer::DeclareTimer FTM_PARSE_XUI("XUI Parsing"); +const LLXMLNodePtr DUMMY_NODE = new LLXMLNode(); void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, const std::string& filename, bool silent) { @@ -432,6 +434,17 @@ bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block) boost::char_separator<char> sep("."); bool values_parsed = false; + bool silent = mCurReadDepth > 0; + + if (nodep->getFirstChild().isNull() + && nodep->mAttributes.empty() + && nodep->getSanitizedValue().empty()) + { + // empty node, just parse as NoValue + mCurReadNode = DUMMY_NODE; + return block.submitValue(mNameStack, *this, silent); + } + // submit attributes for current node values_parsed |= readAttributes(nodep, block); @@ -444,7 +457,6 @@ bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block) mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration())); // child nodes are not necessarily valid parameters (could be a child widget) // so don't complain once we've recursed - bool silent = mCurReadDepth > 0; if (!block.submitValue(mNameStack, *this, true)) { mNameStack.pop_back(); @@ -549,6 +561,7 @@ bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& blo boost::char_separator<char> sep("."); bool any_parsed = false; + bool silent = mCurReadDepth > 0; for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin(); attribute_it != nodep->mAttributes.end(); @@ -567,7 +580,6 @@ bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& blo } // child nodes are not necessarily valid attributes, so don't complain once we've recursed - bool silent = mCurReadDepth > 0; any_parsed |= block.submitValue(mNameStack, *this, silent); while(num_tokens_pushed-- > 0) @@ -634,6 +646,19 @@ LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack) return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node); } +bool LLXUIParser::readNoValue(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast<LLXUIParser&>(parser); + return self.mCurReadNode == DUMMY_NODE; +} + +bool LLXUIParser::writeNoValue(Parser& parser, const void* val_ptr, const name_stack_t& stack) +{ + // just create node + LLXUIParser& self = static_cast<LLXUIParser&>(parser); + LLXMLNodePtr node = self.getNode(stack); + return node.notNull(); +} bool LLXUIParser::readBoolValue(Parser& parser, void* val_ptr) { @@ -1049,6 +1074,8 @@ static LLInitParam::Parser::parser_read_func_map_t sSimpleXUIReadFuncs; static LLInitParam::Parser::parser_write_func_map_t sSimpleXUIWriteFuncs; static LLInitParam::Parser::parser_inspect_func_map_t sSimpleXUIInspectFuncs; +const char* NO_VALUE_MARKER = "no_value"; + LLSimpleXUIParser::LLSimpleXUIParser(LLSimpleXUIParser::element_start_callback_t element_cb) : Parser(sSimpleXUIReadFuncs, sSimpleXUIWriteFuncs, sSimpleXUIInspectFuncs), mLastWriteGeneration(-1), @@ -1057,6 +1084,7 @@ LLSimpleXUIParser::LLSimpleXUIParser(LLSimpleXUIParser::element_start_callback_t { if (sSimpleXUIReadFuncs.empty()) { + registerParserFuncs<LLInitParam::NoParamValue>(readNoValue); registerParserFuncs<bool>(readBoolValue); registerParserFuncs<std::string>(readStringValue); registerParserFuncs<U8>(readU8Value); @@ -1120,6 +1148,8 @@ bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBl return false; } + mEmptyLeafNode.push_back(false); + if( !XML_ParseBuffer(mParser, bytes_read, TRUE ) ) { LL_WARNS("ReadXUI") << "Error while parsing file " << filename << LL_ENDL; @@ -1127,6 +1157,8 @@ bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBl return false; } + mEmptyLeafNode.pop_back(); + XML_ParserFree( mParser ); return true; } @@ -1211,8 +1243,14 @@ void LLSimpleXUIParser::startElement(const char *name, const char **atts) } } + // parent node is not empty + mEmptyLeafNode.back() = false; + // we are empty if we have no attributes + mEmptyLeafNode.push_back(atts[0] == NULL); + mTokenSizeStack.push_back(num_tokens_pushed); readAttributes(atts); + } bool LLSimpleXUIParser::readAttributes(const char **atts) @@ -1246,7 +1284,7 @@ bool LLSimpleXUIParser::readAttributes(const char **atts) return any_parsed; } -void LLSimpleXUIParser::processText() +bool LLSimpleXUIParser::processText() { if (!mTextContents.empty()) { @@ -1259,12 +1297,22 @@ void LLSimpleXUIParser::processText() mNameStack.pop_back(); } mTextContents.clear(); + return true; } + return false; } void LLSimpleXUIParser::endElement(const char *name) { - processText(); + bool has_text = processText(); + + // no text, attributes, or children + if (!has_text && mEmptyLeafNode.back()) + { + // submit this as a valueless name (even though there might be text contents we haven't seen yet) + mCurAttributeValueBegin = NO_VALUE_MARKER; + mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently); + } if (--mOutputStack.back().second == 0) { @@ -1282,6 +1330,7 @@ void LLSimpleXUIParser::endElement(const char *name) mNameStack.pop_back(); } mScope.pop_back(); + mEmptyLeafNode.pop_back(); } void LLSimpleXUIParser::characterData(const char *s, int len) @@ -1328,6 +1377,12 @@ void LLSimpleXUIParser::parserError(const std::string& message) #endif } +bool LLSimpleXUIParser::readNoValue(Parser& parser, void* val_ptr) +{ + LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser); + return self.mCurAttributeValueBegin == NO_VALUE_MARKER; +} + bool LLSimpleXUIParser::readBoolValue(Parser& parser, void* val_ptr) { LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser); diff --git a/indra/llxuixml/llxuiparser.h b/indra/llxuixml/llxuiparser.h index 5c613b0c69..7a748d8aea 100644 --- a/indra/llxuixml/llxuiparser.h +++ b/indra/llxuixml/llxuiparser.h @@ -116,6 +116,7 @@ private: bool readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block); //reader helper functions + static bool readNoValue(Parser& parser, void* val_ptr); static bool readBoolValue(Parser& parser, void* val_ptr); static bool readStringValue(Parser& parser, void* val_ptr); static bool readU8Value(Parser& parser, void* val_ptr); @@ -132,6 +133,7 @@ private: static bool readSDValue(Parser& parser, void* val_ptr); //writer helper functions + static bool writeNoValue(Parser& parser, const void* val_ptr, const name_stack_t&); static bool writeBoolValue(Parser& parser, const void* val_ptr, const name_stack_t&); static bool writeStringValue(Parser& parser, const void* val_ptr, const name_stack_t&); static bool writeU8Value(Parser& parser, const void* val_ptr, const name_stack_t&); @@ -194,6 +196,7 @@ public: private: //reader helper functions + static bool readNoValue(Parser&, void* val_ptr); static bool readBoolValue(Parser&, void* val_ptr); static bool readStringValue(Parser&, void* val_ptr); static bool readU8Value(Parser&, void* val_ptr); @@ -218,7 +221,7 @@ private: void endElement(const char *name); void characterData(const char *s, int len); bool readAttributes(const char **atts); - void processText(); + bool processText(); Parser::name_stack_t mNameStack; struct XML_ParserStruct* mParser; @@ -230,6 +233,7 @@ private: const char* mCurAttributeValueBegin; std::vector<S32> mTokenSizeStack; std::vector<std::string> mScope; + std::vector<bool> mEmptyLeafNode; element_start_callback_t mElementCB; std::vector<std::pair<LLInitParam::BaseBlock*, S32> > mOutputStack; diff --git a/indra/mac_updater/CMakeLists.txt b/indra/mac_updater/CMakeLists.txt index 44f98e5e18..a4a6b50c6c 100644 --- a/indra/mac_updater/CMakeLists.txt +++ b/indra/mac_updater/CMakeLists.txt @@ -3,6 +3,7 @@ project(mac_updater) include(00-Common) +include(OpenSSL) include(CURL) include(LLCommon) include(LLVFS) @@ -49,6 +50,8 @@ set_target_properties(mac-updater target_link_libraries(mac-updater ${LLVFS_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} ) diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp index e4d100d1ec..5d19e8a889 100644 --- a/indra/mac_updater/mac_updater.cpp +++ b/indra/mac_updater/mac_updater.cpp @@ -26,6 +26,9 @@ #include "linden_common.h" +#include <boost/format.hpp> + +#include <libgen.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> @@ -61,6 +64,9 @@ Boolean gCancelled = false; const char *gUpdateURL; const char *gProductName; +const char *gBundleID; +const char *gDmgFile; +const char *gMarkerPath; void *updatethreadproc(void*); @@ -329,6 +335,18 @@ int parse_args(int argc, char **argv) { gProductName = argv[j]; } + else if ((!strcmp(argv[j], "-bundleid")) && (++j < argc)) + { + gBundleID = argv[j]; + } + else if ((!strcmp(argv[j], "-dmg")) && (++j < argc)) + { + gDmgFile = argv[j]; + } + else if ((!strcmp(argv[j], "-marker")) && (++j < argc)) + { + gMarkerPath = argv[j];; + } } return 0; @@ -355,10 +373,13 @@ int main(int argc, char **argv) // gUpdateURL = NULL; gProductName = NULL; + gBundleID = NULL; + gDmgFile = NULL; + gMarkerPath = NULL; parse_args(argc, argv); - if (!gUpdateURL) + if ((gUpdateURL == NULL) && (gDmgFile == NULL)) { - llinfos << "Usage: mac_updater -url <url> [-name <product_name>] [-program <program_name>]" << llendl; + llinfos << "Usage: mac_updater -url <url> | -dmg <dmg file> [-name <product_name>] [-program <program_name>]" << llendl; exit(1); } else @@ -372,6 +393,14 @@ int main(int argc, char **argv) { gProductName = "Second Life"; } + if (gBundleID) + { + llinfos << "Bundle ID is: " << gBundleID << llendl; + } + else + { + gBundleID = "com.secondlife.indra.viewer"; + } } llinfos << "Starting " << gProductName << " Updater" << llendl; @@ -474,11 +503,18 @@ int main(int argc, char **argv) NULL, &retval_mac); } - + + if(gMarkerPath != 0) + { + // Create a install fail marker that can be used by the viewer to + // detect install problems. + std::ofstream stream(gMarkerPath); + if(stream) stream << -1; + } + exit(-1); + } else { + exit(0); } - - // Don't dispose of things, just exit. This keeps the update thread from potentially getting hosed. - exit(0); if(gWindow != NULL) { @@ -592,7 +628,8 @@ static bool isFSRefViewerBundle(FSRef *targetRef) CFURLRef targetURL = NULL; CFBundleRef targetBundle = NULL; CFStringRef targetBundleID = NULL; - + CFStringRef sourceBundleID = NULL; + targetURL = CFURLCreateFromFSRef(NULL, targetRef); if(targetURL == NULL) @@ -619,7 +656,8 @@ static bool isFSRefViewerBundle(FSRef *targetRef) } else { - if(CFStringCompare(targetBundleID, CFSTR("com.secondlife.indra.viewer"), 0) == kCFCompareEqualTo) + sourceBundleID = CFStringCreateWithCString(NULL, gBundleID, kCFStringEncodingUTF8); + if(CFStringCompare(sourceBundleID, targetBundleID, 0) == kCFCompareEqualTo) { // This is the bundle we're looking for. result = true; @@ -684,17 +722,26 @@ static OSErr findAppBundleOnDiskImage(FSRef *parent, FSRef *app) // Looks promising. Check to see if it has the right bundle identifier. if(isFSRefViewerBundle(&ref)) { + llinfos << name << " is the one" << llendl; // This is the one. Return it. *app = ref; found = true; + break; + } else { + llinfos << name << " is not the bundle we are looking for; move along" << llendl; } + } } } } - while(!err && !found); + while(!err); + + llinfos << "closing the iterator" << llendl; FSCloseIterator(iterator); + + llinfos << "closed" << llendl; } if(!err && !found) @@ -905,6 +952,22 @@ void *updatethreadproc(void*) #endif // 0 *HACK for DEV-11935 + // Skip downloading the file if the dmg was passed on the command line. + std::string dmgName; + if(gDmgFile != NULL) { + dmgName = basename((char *)gDmgFile); + char * dmgDir = dirname((char *)gDmgFile); + strncpy(tempDir, dmgDir, sizeof(tempDir)); + err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL); + if(err != noErr) throw 0; + chdir(tempDir); + goto begin_install; + } else { + // Continue on to download file. + dmgName = "SecondLife.dmg"; + } + + strncat(temp, "/SecondLifeUpdate_XXXXXX", (sizeof(temp) - strlen(temp)) - 1); if(mkdtemp(temp) == NULL) { @@ -963,14 +1026,17 @@ void *updatethreadproc(void*) fclose(downloadFile); downloadFile = NULL; } - + + begin_install: sendProgress(0, 0, CFSTR("Mounting image...")); LLFile::mkdir("mnt", 0700); // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder, // but if our cleanup fails, this makes it much harder for the user to unmount the image. std::string mountOutput; - FILE* mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r"); /* Flawfinder: ignore */ + boost::format cmdFormat("hdiutil attach %s -mountpoint mnt"); + cmdFormat % dmgName; + FILE* mounter = popen(cmdFormat.str().c_str(), "r"); /* Flawfinder: ignore */ if(mounter == NULL) { @@ -1036,12 +1102,19 @@ void *updatethreadproc(void*) throw 0; } + sendProgress(0, 0, CFSTR("Searching for the app bundle...")); err = findAppBundleOnDiskImage(&mountRef, &sourceRef); if(err != noErr) { llinfos << "Couldn't find application bundle on mounted disk image." << llendl; throw 0; } + else + { + llinfos << "found the bundle." << llendl; + } + + sendProgress(0, 0, CFSTR("Preparing to copy files...")); FSRef asideRef; char aside[MAX_PATH]; /* Flawfinder: ignore */ @@ -1061,7 +1134,11 @@ void *updatethreadproc(void*) // Move aside old version (into work directory) err = FSMoveObject(&targetRef, &tempDirRef, &asideRef); if(err != noErr) + { + llwarns << "failed to move aside old version (error code " << + err << ")" << llendl; throw 0; + } // Grab the path for later use. err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside)); @@ -1159,6 +1236,10 @@ void *updatethreadproc(void*) llinfos << "Moving work directory to the trash." << llendl; err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL); + if(err != noErr) { + llwarns << "failed to move files to trash, (error code " << + err << ")" << llendl; + } // snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir); // printf("%s\n", temp); diff --git a/indra/media_plugins/example/media_plugin_example.cpp b/indra/media_plugins/example/media_plugin_example.cpp index f8a871930e..da7de01799 100644 --- a/indra/media_plugins/example/media_plugin_example.cpp +++ b/indra/media_plugins/example/media_plugin_example.cpp @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2008&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$ * @endcond @@ -39,48 +39,48 @@ //////////////////////////////////////////////////////////////////////////////// // class MediaPluginExample : - public MediaPluginBase + public MediaPluginBase { - public: - MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data ); - ~MediaPluginExample(); - - /*virtual*/ void receiveMessage( const char* message_string ); - - private: - bool init(); - void update( F64 milliseconds ); - void write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b ); - bool mFirstTime; - - time_t mLastUpdateTime; - enum Constants { ENumObjects = 10 }; - unsigned char* mBackgroundPixels; - int mColorR[ ENumObjects ]; - int mColorG[ ENumObjects ]; - int mColorB[ ENumObjects ]; - int mXpos[ ENumObjects ]; - int mYpos[ ENumObjects ]; - int mXInc[ ENumObjects ]; - int mYInc[ ENumObjects ]; - int mBlockSize[ ENumObjects ]; - bool mMouseButtonDown; - bool mStopAction; + public: + MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data ); + ~MediaPluginExample(); + + /*virtual*/ void receiveMessage( const char* message_string ); + + private: + bool init(); + void update( F64 milliseconds ); + void write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b ); + bool mFirstTime; + + time_t mLastUpdateTime; + enum Constants { ENumObjects = 10 }; + unsigned char* mBackgroundPixels; + int mColorR[ ENumObjects ]; + int mColorG[ ENumObjects ]; + int mColorB[ ENumObjects ]; + int mXpos[ ENumObjects ]; + int mYpos[ ENumObjects ]; + int mXInc[ ENumObjects ]; + int mYInc[ ENumObjects ]; + int mBlockSize[ ENumObjects ]; + bool mMouseButtonDown; + bool mStopAction; }; //////////////////////////////////////////////////////////////////////////////// // MediaPluginExample::MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data ) : - MediaPluginBase( host_send_func, host_user_data ) + MediaPluginBase( host_send_func, host_user_data ) { - mFirstTime = true; - mWidth = 0; - mHeight = 0; - mDepth = 4; - mPixels = 0; - mMouseButtonDown = false; - mStopAction = false; - mLastUpdateTime = 0; + mFirstTime = true; + mWidth = 0; + mHeight = 0; + mDepth = 4; + mPixels = 0; + mMouseButtonDown = false; + mStopAction = false; + mLastUpdateTime = 0; } //////////////////////////////////////////////////////////////////////////////// @@ -93,395 +93,320 @@ MediaPluginExample::~MediaPluginExample() // void MediaPluginExample::receiveMessage( const char* message_string ) { - LLPluginMessage message_in; - - if ( message_in.parse( message_string ) >= 0 ) - { - std::string message_class = message_in.getClass(); - std::string message_name = message_in.getName(); - - if ( message_class == LLPLUGIN_MESSAGE_CLASS_BASE ) - { - if ( message_name == "init" ) - { - LLPluginMessage message( "base", "init_response" ); - LLSD versions = LLSD::emptyMap(); - versions[ LLPLUGIN_MESSAGE_CLASS_BASE ] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; - versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; - versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; - message.setValueLLSD( "versions", versions ); - - std::string plugin_version = "Example media plugin, Example Version 1.0.0.0"; - message.setValue( "plugin_version", plugin_version ); - sendMessage( message ); - } - else - if ( message_name == "idle" ) - { - // no response is necessary here. - F64 time = message_in.getValueReal( "time" ); - - // Convert time to milliseconds for update() - update( time ); - } - else - if ( message_name == "cleanup" ) - { - // clean up here - } - else - if ( message_name == "shm_added" ) - { - SharedSegmentInfo info; - info.mAddress = message_in.getValuePointer( "address" ); - info.mSize = ( size_t )message_in.getValueS32( "size" ); - std::string name = message_in.getValue( "name" ); - - mSharedSegments.insert( SharedSegmentMap::value_type( name, info ) ); - - } - else - if ( message_name == "shm_remove" ) - { - std::string name = message_in.getValue( "name" ); - - SharedSegmentMap::iterator iter = mSharedSegments.find( name ); - if( iter != mSharedSegments.end() ) - { - if ( mPixels == iter->second.mAddress ) - { - // This is the currently active pixel buffer. - // Make sure we stop drawing to it. - mPixels = NULL; - mTextureSegmentName.clear(); - }; - mSharedSegments.erase( iter ); - } - else - { - //std::cerr << "MediaPluginExample::receiveMessage: unknown shared memory region!" << std::endl; - }; - - // Send the response so it can be cleaned up. - LLPluginMessage message( "base", "shm_remove_response" ); - message.setValue( "name", name ); - sendMessage( message ); - } - else - { - //std::cerr << "MediaPluginExample::receiveMessage: unknown base message: " << message_name << std::endl; - }; - } - else - if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA ) - { - if ( message_name == "init" ) - { - // Plugin gets to decide the texture parameters to use. - LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params" ); - message.setValueS32( "default_width", mWidth ); - message.setValueS32( "default_height", mHeight ); - message.setValueS32( "depth", mDepth ); - message.setValueU32( "internalformat", GL_RGBA ); - message.setValueU32( "format", GL_RGBA ); - message.setValueU32( "type", GL_UNSIGNED_BYTE ); - message.setValueBoolean( "coords_opengl", false ); - sendMessage( message ); - } - else if ( message_name == "size_change" ) - { - std::string name = message_in.getValue( "name" ); - S32 width = message_in.getValueS32( "width" ); - S32 height = message_in.getValueS32( "height" ); - S32 texture_width = message_in.getValueS32( "texture_width" ); - S32 texture_height = message_in.getValueS32( "texture_height" ); - - if ( ! name.empty() ) - { - // Find the shared memory region with this name - SharedSegmentMap::iterator iter = mSharedSegments.find( name ); - if ( iter != mSharedSegments.end() ) - { - mPixels = ( unsigned char* )iter->second.mAddress; - mWidth = width; - mHeight = height; - - mTextureWidth = texture_width; - mTextureHeight = texture_height; - - init(); - }; - }; - - LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response" ); - message.setValue( "name", name ); - message.setValueS32( "width", width ); - message.setValueS32( "height", height ); - message.setValueS32( "texture_width", texture_width ); - message.setValueS32( "texture_height", texture_height ); - sendMessage( message ); - } - else - if ( message_name == "load_uri" ) - { - std::string uri = message_in.getValue( "uri" ); - if ( ! uri.empty() ) - { - }; - } - else - if ( message_name == "mouse_event" ) - { - std::string event = message_in.getValue( "event" ); - S32 button = message_in.getValueS32( "button" ); - - // left mouse button - if ( button == 0 ) - { - int mouse_x = message_in.getValueS32( "x" ); - int mouse_y = message_in.getValueS32( "y" ); - std::string modifiers = message_in.getValue( "modifiers" ); - - if ( event == "move" ) - { - if ( mMouseButtonDown ) - write_pixel( mouse_x, mouse_y, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80 ); - } - else - if ( event == "down" ) - { - mMouseButtonDown = true; - } - else - if ( event == "up" ) - { - mMouseButtonDown = false; - } - else - if ( event == "double_click" ) - { - }; - }; - } - else - if ( message_name == "key_event" ) - { - std::string event = message_in.getValue( "event" ); - S32 key = message_in.getValueS32( "key" ); - std::string modifiers = message_in.getValue( "modifiers" ); - - if ( event == "down" ) - { - if ( key == ' ') - { - mLastUpdateTime = 0; - update( 0.0f ); - }; - }; - } - else - { - //std::cerr << "MediaPluginExample::receiveMessage: unknown media message: " << message_string << std::endl; - }; - } - else - if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER ) - { - if ( message_name == "browse_reload" ) - { - mLastUpdateTime = 0; - mFirstTime = true; - mStopAction = false; - update( 0.0f ); - } - else - if ( message_name == "browse_stop" ) - { - for( int n = 0; n < ENumObjects; ++n ) - mXInc[ n ] = mYInc[ n ] = 0; - - mStopAction = true; - update( 0.0f ); - } - else - { - //std::cerr << "MediaPluginExample::receiveMessage: unknown media_browser message: " << message_string << std::endl; - }; - } - else - { - //std::cerr << "MediaPluginExample::receiveMessage: unknown message class: " << message_class << std::endl; - }; - }; +// std::cerr << "MediaPluginWebKit::receiveMessage: received message: \"" << message_string << "\"" << std::endl; + LLPluginMessage message_in; + + if(message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if(message_name == "init") + { + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; + message.setValueLLSD("versions", versions); + + std::string plugin_version = "Example plugin 1.0..0"; + message.setValue("plugin_version", plugin_version); + sendMessage(message); + } + else if(message_name == "idle") + { + // no response is necessary here. + F64 time = message_in.getValueReal("time"); + + // Convert time to milliseconds for update() + update((int)(time * 1000.0f)); + } + else if(message_name == "cleanup") + { + } + else if(message_name == "shm_added") + { + SharedSegmentInfo info; + info.mAddress = message_in.getValuePointer("address"); + info.mSize = (size_t)message_in.getValueS32("size"); + std::string name = message_in.getValue("name"); + + mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + + } + else if(message_name == "shm_remove") + { + std::string name = message_in.getValue("name"); + + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { + if(mPixels == iter->second.mAddress) + { + // This is the currently active pixel buffer. Make sure we stop drawing to it. + mPixels = NULL; + mTextureSegmentName.clear(); + } + mSharedSegments.erase(iter); + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl; + } + + // Send the response so it can be cleaned up. + LLPluginMessage message("base", "shm_remove_response"); + message.setValue("name", name); + sendMessage(message); + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; + } + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + if(message_name == "init") + { + // Plugin gets to decide the texture parameters to use. + mDepth = 4; + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + message.setValueS32("default_width", 1024); + message.setValueS32("default_height", 1024); + message.setValueS32("depth", mDepth); + message.setValueU32("internalformat", GL_RGBA); + message.setValueU32("format", GL_RGBA); + message.setValueU32("type", GL_UNSIGNED_BYTE); + message.setValueBoolean("coords_opengl", true); + sendMessage(message); + } + else if(message_name == "size_change") + { + std::string name = message_in.getValue("name"); + S32 width = message_in.getValueS32("width"); + S32 height = message_in.getValueS32("height"); + S32 texture_width = message_in.getValueS32("texture_width"); + S32 texture_height = message_in.getValueS32("texture_height"); + + if(!name.empty()) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { + mPixels = (unsigned char*)iter->second.mAddress; + mWidth = width; + mHeight = height; + + mTextureWidth = texture_width; + mTextureHeight = texture_height; + }; + }; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); + message.setValue("name", name); + message.setValueS32("width", width); + message.setValueS32("height", height); + message.setValueS32("texture_width", texture_width); + message.setValueS32("texture_height", texture_height); + sendMessage(message); + + } + else if(message_name == "load_uri") + { + } + else if(message_name == "mouse_event") + { + std::string event = message_in.getValue("event"); + if(event == "down") + { + + } + else if(event == "up") + { + } + else if(event == "double_click") + { + } + } + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl; + }; + } } //////////////////////////////////////////////////////////////////////////////// // void MediaPluginExample::write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b ) { - // make sure we don't write outside the buffer - if ( ( x < 0 ) || ( x >= mWidth ) || ( y < 0 ) || ( y >= mHeight ) ) - return; - - if ( mBackgroundPixels != NULL ) - { - unsigned char *pixel = mBackgroundPixels; - pixel += y * mWidth * mDepth; - pixel += ( x * mDepth ); - pixel[ 0 ] = b; - pixel[ 1 ] = g; - pixel[ 2 ] = r; - - setDirty( x, y, x + 1, y + 1 ); - }; + // make sure we don't write outside the buffer + if ( ( x < 0 ) || ( x >= mWidth ) || ( y < 0 ) || ( y >= mHeight ) ) + return; + + if ( mBackgroundPixels != NULL ) + { + unsigned char *pixel = mBackgroundPixels; + pixel += y * mWidth * mDepth; + pixel += ( x * mDepth ); + pixel[ 0 ] = b; + pixel[ 1 ] = g; + pixel[ 2 ] = r; + + setDirty( x, y, x + 1, y + 1 ); + }; } //////////////////////////////////////////////////////////////////////////////// // void MediaPluginExample::update( F64 milliseconds ) { - if ( mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048 ) - return; - - if ( mPixels == 0 ) - return; - - if ( mFirstTime ) - { - for( int n = 0; n < ENumObjects; ++n ) - { - mXpos[ n ] = ( mWidth / 2 ) + rand() % ( mWidth / 16 ) - ( mWidth / 32 ); - mYpos[ n ] = ( mHeight / 2 ) + rand() % ( mHeight / 16 ) - ( mHeight / 32 ); - - mColorR[ n ] = rand() % 0x60 + 0x60; - mColorG[ n ] = rand() % 0x60 + 0x60; - mColorB[ n ] = rand() % 0x60 + 0x60; - - mXInc[ n ] = 0; - while ( mXInc[ n ] == 0 ) - mXInc[ n ] = rand() % 7 - 3; - - mYInc[ n ] = 0; - while ( mYInc[ n ] == 0 ) - mYInc[ n ] = rand() % 9 - 4; - - mBlockSize[ n ] = rand() % 0x30 + 0x10; - }; - - delete [] mBackgroundPixels; - - mBackgroundPixels = new unsigned char[ mWidth * mHeight * mDepth ]; - - mFirstTime = false; - }; - - if ( mStopAction ) - return; - - if ( time( NULL ) > mLastUpdateTime + 3 ) - { - const int num_squares = rand() % 20 + 4; - int sqr1_r = rand() % 0x80 + 0x20; - int sqr1_g = rand() % 0x80 + 0x20; - int sqr1_b = rand() % 0x80 + 0x20; - int sqr2_r = rand() % 0x80 + 0x20; - int sqr2_g = rand() % 0x80 + 0x20; - int sqr2_b = rand() % 0x80 + 0x20; - - for ( int y1 = 0; y1 < num_squares; ++y1 ) - { - for ( int x1 = 0; x1 < num_squares; ++x1 ) - { - int px_start = mWidth * x1 / num_squares; - int px_end = ( mWidth * ( x1 + 1 ) ) / num_squares; - int py_start = mHeight * y1 / num_squares; - int py_end = ( mHeight * ( y1 + 1 ) ) / num_squares; - - for( int y2 = py_start; y2 < py_end; ++y2 ) - { - for( int x2 = px_start; x2 < px_end; ++x2 ) - { - int rowspan = mWidth * mDepth; - - if ( ( y1 % 2 ) ^ ( x1 % 2 ) ) - { - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr1_r; - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr1_g; - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr1_b; - } - else - { - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr2_r; - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr2_g; - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr2_b; - }; - }; - }; - }; - }; - - time( &mLastUpdateTime ); - }; - - memcpy( mPixels, mBackgroundPixels, mWidth * mHeight * mDepth ); - - for( int n = 0; n < ENumObjects; ++n ) - { - if ( rand() % 50 == 0 ) - { - mXInc[ n ] = 0; - while ( mXInc[ n ] == 0 ) - mXInc[ n ] = rand() % 7 - 3; - - mYInc[ n ] = 0; - while ( mYInc[ n ] == 0 ) - mYInc[ n ] = rand() % 9 - 4; - }; - - if ( mXpos[ n ] + mXInc[ n ] < 0 || mXpos[ n ] + mXInc[ n ] >= mWidth - mBlockSize[ n ] ) - mXInc[ n ] =- mXInc[ n ]; - - if ( mYpos[ n ] + mYInc[ n ] < 0 || mYpos[ n ] + mYInc[ n ] >= mHeight - mBlockSize[ n ] ) - mYInc[ n ] =- mYInc[ n ]; - - mXpos[ n ] += mXInc[ n ]; - mYpos[ n ] += mYInc[ n ]; - - for( int y = 0; y < mBlockSize[ n ]; ++y ) - { - for( int x = 0; x < mBlockSize[ n ]; ++x ) - { - mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 0 ] = mColorR[ n ]; - mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 1 ] = mColorG[ n ]; - mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 2 ] = mColorB[ n ]; - }; - }; - }; - - setDirty( 0, 0, mWidth, mHeight ); + if ( mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048 ) + return; + + if ( mPixels == 0 ) + return; + + if ( mFirstTime ) + { + for( int n = 0; n < ENumObjects; ++n ) + { + mXpos[ n ] = ( mWidth / 2 ) + rand() % ( mWidth / 16 ) - ( mWidth / 32 ); + mYpos[ n ] = ( mHeight / 2 ) + rand() % ( mHeight / 16 ) - ( mHeight / 32 ); + + mColorR[ n ] = rand() % 0x60 + 0x60; + mColorG[ n ] = rand() % 0x60 + 0x60; + mColorB[ n ] = rand() % 0x60 + 0x60; + + mXInc[ n ] = 0; + while ( mXInc[ n ] == 0 ) + mXInc[ n ] = rand() % 7 - 3; + + mYInc[ n ] = 0; + while ( mYInc[ n ] == 0 ) + mYInc[ n ] = rand() % 9 - 4; + + mBlockSize[ n ] = rand() % 0x30 + 0x10; + }; + + delete [] mBackgroundPixels; + + mBackgroundPixels = new unsigned char[ mWidth * mHeight * mDepth ]; + + mFirstTime = false; + }; + + if ( mStopAction ) + return; + + if ( time( NULL ) > mLastUpdateTime + 3 ) + { + const int num_squares = rand() % 20 + 4; + int sqr1_r = rand() % 0x80 + 0x20; + int sqr1_g = rand() % 0x80 + 0x20; + int sqr1_b = rand() % 0x80 + 0x20; + int sqr2_r = rand() % 0x80 + 0x20; + int sqr2_g = rand() % 0x80 + 0x20; + int sqr2_b = rand() % 0x80 + 0x20; + + for ( int y1 = 0; y1 < num_squares; ++y1 ) + { + for ( int x1 = 0; x1 < num_squares; ++x1 ) + { + int px_start = mWidth * x1 / num_squares; + int px_end = ( mWidth * ( x1 + 1 ) ) / num_squares; + int py_start = mHeight * y1 / num_squares; + int py_end = ( mHeight * ( y1 + 1 ) ) / num_squares; + + for( int y2 = py_start; y2 < py_end; ++y2 ) + { + for( int x2 = px_start; x2 < px_end; ++x2 ) + { + int rowspan = mWidth * mDepth; + + if ( ( y1 % 2 ) ^ ( x1 % 2 ) ) + { + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr1_r; + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr1_g; + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr1_b; + } + else + { + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr2_r; + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr2_g; + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr2_b; + }; + }; + }; + }; + }; + + time( &mLastUpdateTime ); + }; + + memcpy( mPixels, mBackgroundPixels, mWidth * mHeight * mDepth ); + + for( int n = 0; n < ENumObjects; ++n ) + { + if ( rand() % 50 == 0 ) + { + mXInc[ n ] = 0; + while ( mXInc[ n ] == 0 ) + mXInc[ n ] = rand() % 7 - 3; + + mYInc[ n ] = 0; + while ( mYInc[ n ] == 0 ) + mYInc[ n ] = rand() % 9 - 4; + }; + + if ( mXpos[ n ] + mXInc[ n ] < 0 || mXpos[ n ] + mXInc[ n ] >= mWidth - mBlockSize[ n ] ) + mXInc[ n ] =- mXInc[ n ]; + + if ( mYpos[ n ] + mYInc[ n ] < 0 || mYpos[ n ] + mYInc[ n ] >= mHeight - mBlockSize[ n ] ) + mYInc[ n ] =- mYInc[ n ]; + + mXpos[ n ] += mXInc[ n ]; + mYpos[ n ] += mYInc[ n ]; + + for( int y = 0; y < mBlockSize[ n ]; ++y ) + { + for( int x = 0; x < mBlockSize[ n ]; ++x ) + { + mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 0 ] = mColorR[ n ]; + mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 1 ] = mColorG[ n ]; + mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 2 ] = mColorB[ n ]; + }; + }; + }; + + setDirty( 0, 0, mWidth, mHeight ); }; //////////////////////////////////////////////////////////////////////////////// // bool MediaPluginExample::init() { - LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text" ); - message.setValue( "name", "Example Plugin" ); - sendMessage( message ); + LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text" ); + message.setValue( "name", "Example Plugin" ); + sendMessage( message ); - return true; + return true; }; //////////////////////////////////////////////////////////////////////////////// // int init_media_plugin( LLPluginInstance::sendMessageFunction host_send_func, - void* host_user_data, - LLPluginInstance::sendMessageFunction *plugin_send_func, - void **plugin_user_data ) + void* host_user_data, + LLPluginInstance::sendMessageFunction *plugin_send_func, + void **plugin_user_data ) { - MediaPluginExample* self = new MediaPluginExample( host_send_func, host_user_data ); - *plugin_send_func = MediaPluginExample::staticReceiveMessage; - *plugin_user_data = ( void* )self; + MediaPluginExample* self = new MediaPluginExample( host_send_func, host_user_data ); + *plugin_send_func = MediaPluginExample::staticReceiveMessage; + *plugin_user_data = ( void* )self; - return 0; + return 0; } + diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt index 05f1236606..3b1f679540 100644 --- a/indra/media_plugins/webkit/CMakeLists.txt +++ b/indra/media_plugins/webkit/CMakeLists.txt @@ -27,6 +27,7 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} + ${LLQTWEBKIT_INCLUDE_DIR} ) diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index bd1a44a930..d6f8ae3e16 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -341,7 +341,7 @@ private: url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f); url << "%22%3E%3C/body%3E%3C/html%3E"; - lldebugs << "data url is: " << url.str() << llendl; + //lldebugs << "data url is: " << url.str() << llendl; LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() ); // LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); @@ -407,6 +407,8 @@ private: { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); message.setValue("uri", event.getEventUri()); + message.setValueBoolean("history_back_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK)); + message.setValueBoolean("history_forward_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD)); sendMessage(message); setStatus(STATUS_LOADING); @@ -569,6 +571,57 @@ private: return blockingPickFile(); } + std::string mAuthUsername; + std::string mAuthPassword; + bool mAuthOK; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + bool onAuthRequest(const std::string &in_url, const std::string &in_realm, std::string &out_username, std::string &out_password) + { + mAuthOK = false; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "auth_request"); + message.setValue("url", in_url); + message.setValue("realm", in_realm); + message.setValueBoolean("blocking_request", true); + + // The "blocking_request" key in the message means this sendMessage call will block until a response is received. + sendMessage(message); + + if(mAuthOK) + { + out_username = mAuthUsername; + out_password = mAuthPassword; + } + + return mAuthOK; + } + + void authResponse(LLPluginMessage &message) + { + mAuthOK = message.getValueBoolean("ok"); + if(mAuthOK) + { + mAuthUsername = message.getValue("username"); + mAuthPassword = message.getValue("password"); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onLinkHovered(const EventType& event) + { + if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "link_hovered"); + message.setValue("link", event.getEventUri()); + message.setValue("title", event.getStringValue()); + message.setValue("text", event.getStringValue2()); + sendMessage(message); + } + } + LLQtWebKit::EKeyboardModifier decodeModifiers(std::string &modifiers) { int result = 0; @@ -1096,6 +1149,10 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) { onPickFileResponse(message_in.getValue("file")); } + if(message_name == "auth_response") + { + authResponse(message_in); + } else { // std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl; @@ -1182,6 +1239,22 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) mUserAgent = message_in.getValue("user_agent"); LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent ); } + else if(message_name == "ignore_ssl_cert_errors") + { +#if LLQTWEBKIT_API_VERSION >= 3 + LLQtWebKit::getInstance()->setIgnoreSSLCertErrors( message_in.getValueBoolean("ignore") ); +#else + llwarns << "Ignoring ignore_ssl_cert_errors message (llqtwebkit version is too old)." << llendl; +#endif + } + else if(message_name == "add_certificate_file_path") + { +#if LLQTWEBKIT_API_VERSION >= 6 + LLQtWebKit::getInstance()->addCAFile( message_in.getValue("path") ); +#else + llwarns << "Ignoring add_certificate_file_path message (llqtwebkit version is too old)." << llendl; +#endif + } else if(message_name == "init_history") { // Initialize browser history diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index bf885e5934..a61c35abd2 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -50,6 +50,7 @@ include_directories( ${LLCHARACTER_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLIMAGE_INCLUDE_DIRS} + ${LLKDU_INCLUDE_DIRS} ${LLINVENTORY_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} @@ -64,6 +65,7 @@ include_directories( ${LSCRIPT_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS}/lscript_compile ${LLLOGIN_INCLUDE_DIRS} + ${UPDATER_INCLUDE_DIRS} ) set(viewer_SOURCE_FILES @@ -143,6 +145,7 @@ set(viewer_SOURCE_FILES lleventnotifier.cpp lleventpoll.cpp llexpandabletextbox.cpp + llexternaleditor.cpp llface.cpp llfasttimerview.cpp llfavoritesbar.cpp @@ -200,6 +203,7 @@ set(viewer_SOURCE_FILES llfloaterpostprocess.cpp llfloaterpreference.cpp llfloaterproperties.cpp + llfloaterregiondebugconsole.cpp llfloaterregioninfo.cpp llfloaterreporter.cpp llfloaterscriptdebug.cpp @@ -219,6 +223,7 @@ set(viewer_SOURCE_FILES llfloaterurlentry.cpp llfloatervoiceeffect.cpp llfloaterwater.cpp + llfloaterwebcontent.cpp llfloaterwhitelistentry.cpp llfloaterwindlight.cpp llfloaterwindowsize.cpp @@ -282,6 +287,7 @@ set(viewer_SOURCE_FILES llloginhandler.cpp lllogininstance.cpp llmachineid.cpp + llmainlooprepeater.cpp llmanip.cpp llmaniprotate.cpp llmanipscale.cpp @@ -290,7 +296,6 @@ set(viewer_SOURCE_FILES llmediadataclient.cpp llmemoryview.cpp llmenucommands.cpp - llmetricperformancetester.cpp llmimetypes.cpp llmorphview.cpp llmoveview.cpp @@ -400,6 +405,7 @@ set(viewer_SOURCE_FILES llsecapi.cpp llsechandler_basic.cpp llselectmgr.cpp + llshareavatarhandler.cpp llsidepanelappearance.cpp llsidepanelinventory.cpp llsidepanelinventorysubpanel.cpp @@ -444,6 +450,7 @@ set(viewer_SOURCE_FILES lltoastimpanel.cpp lltoastnotifypanel.cpp lltoastpanel.cpp + lltoastscripttextbox.cpp lltool.cpp lltoolbrush.cpp lltoolcomp.cpp @@ -477,6 +484,7 @@ set(viewer_SOURCE_FILES llvectorperfoptions.cpp llversioninfo.cpp llviewchildren.cpp + llviewerassetstats.cpp llviewerassetstorage.cpp llviewerassettype.cpp llviewerattachmenu.cpp @@ -539,6 +547,7 @@ set(viewer_SOURCE_FILES llvoclouds.cpp llvograss.cpp llvoground.cpp + llvoicecallhandler.cpp llvoicechannel.cpp llvoiceclient.cpp llvoicevisualizer.cpp @@ -675,6 +684,7 @@ set(viewer_HEADER_FILES lleventnotifier.h lleventpoll.h llexpandabletextbox.h + llexternaleditor.h llface.h llfasttimerview.h llfavoritesbar.h @@ -732,6 +742,7 @@ set(viewer_HEADER_FILES llfloaterpostprocess.h llfloaterpreference.h llfloaterproperties.h + llfloaterregiondebugconsole.h llfloaterregioninfo.h llfloaterreporter.h llfloaterscriptdebug.h @@ -751,6 +762,7 @@ set(viewer_HEADER_FILES llfloaterurlentry.h llfloatervoiceeffect.h llfloaterwater.h + llfloaterwebcontent.h llfloaterwhitelistentry.h llfloaterwindlight.h llfloaterwindowsize.h @@ -814,6 +826,7 @@ set(viewer_HEADER_FILES llloginhandler.h lllogininstance.h llmachineid.h + llmainlooprepeater.h llmanip.h llmaniprotate.h llmanipscale.h @@ -822,7 +835,6 @@ set(viewer_HEADER_FILES llmediadataclient.h llmemoryview.h llmenucommands.h - llmetricperformancetester.h llmimetypes.h llmorphview.h llmoveview.h @@ -973,6 +985,7 @@ set(viewer_HEADER_FILES lltoastimpanel.h lltoastnotifypanel.h lltoastpanel.h + lltoastscripttextbox.h lltool.h lltoolbrush.h lltoolcomp.h @@ -1007,6 +1020,7 @@ set(viewer_HEADER_FILES llvectorperfoptions.h llversioninfo.h llviewchildren.h + llviewerassetstats.h llviewerassetstorage.h llviewerassettype.h llviewerattachmenu.h @@ -1318,7 +1332,7 @@ set(viewer_APPSETTINGS_FILES app_settings/grass.xml app_settings/high_graphics.xml app_settings/ignorable_dialogs.xml - app_settings/keys.ini + app_settings/keys.xml app_settings/keywords.ini app_settings/logcontrol.xml app_settings/low_graphics.xml @@ -1446,11 +1460,6 @@ if (WINDOWS) # In the meantime, if you have any ideas on how to easily maintain one list, either here or in viewer_manifest.py # and have the build deps get tracked *please* tell me about it. - if(LLKDU_LIBRARY) - # Configure a var for llkdu which may not exist for all builds. - set(LLKDU_DLL_SOURCE ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/llkdu.dll) - endif(LLKDU_LIBRARY) - if(USE_GOOGLE_PERFTOOLS) # Configure a var for tcmalloc location, if used. # Note the need to specify multiple names explicitly. @@ -1467,7 +1476,6 @@ if (WINDOWS) #${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libtcmalloc_minimal.dll => None ... Skipping libtcmalloc_minimal.dll ${CMAKE_SOURCE_DIR}/../etc/message.xml ${CMAKE_SOURCE_DIR}/../scripts/messages/message_template.msg - ${LLKDU_DLL_SOURCE} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/llcommon.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libapr-1.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libaprutil-1.dll @@ -1642,11 +1650,17 @@ if (WINDOWS) endif (PACKAGE) endif (WINDOWS) +# *NOTE - this list is very sensitive to ordering, test carefully on all +# platforms if you change the releative order of the entries here. +# In particular, cmake 2.6.4 (when buidling with linux/makefile generators) +# appears to sometimes de-duplicate redundantly listed dependencies improperly. +# To work around this, higher level modules should be listed before the modules +# that they depend upon. -brad target_link_libraries(${VIEWER_BINARY_NAME} + ${UPDATER_LIBRARIES} ${LLAUDIO_LIBRARIES} ${LLCHARACTER_LIBRARIES} ${LLIMAGE_LIBRARIES} - ${LLIMAGEJ2COJ_LIBRARIES} ${LLINVENTORY_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLPLUGIN_LIBRARIES} @@ -1682,6 +1696,17 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${GOOGLE_PERFTOOLS_LIBRARIES} ) +if (USE_KDU) + target_link_libraries(${VIEWER_BINARY_NAME} + ${LLKDU_LIBRARIES} + ${KDU_LIBRARY} + ) +else (USE_KDU) + target_link_libraries(${VIEWER_BINARY_NAME} + ${LLIMAGEJ2COJ_LIBRARIES} + ) +endif (USE_KDU) + build_version(viewer) set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH @@ -1829,21 +1854,31 @@ if (PACKAGE) set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest) endif (LINUX) + if(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) + if(CMAKE_CFG_INTDIR STREQUAL ".") + set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE}) + else(CMAKE_CFG_INTDIR STREQUAL ".") + # set LLBUILD_CONFIG to be a shell variable evaluated at build time + # reflecting the configuration we are currently building. + set(LLBUILD_CONFIG ${CMAKE_CFG_INTDIR}) + endif(CMAKE_CFG_INTDIR STREQUAL ".") add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}" COMMAND "${PYTHON_EXECUTABLE}" ARGS "${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py" + "${LLBUILD_CONFIG}" "${VIEWER_DIST_DIR}" "${VIEWER_EXE_GLOBS}" "${VIEWER_LIB_GLOB}" "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/bin/dump_syms" "${VIEWER_SYMBOL_FILE}" DEPENDS generate_breakpad_symbols.py - VERBATIM - ) - add_custom_target(generate_breakpad_symbols ALL DEPENDS "${VIEWER_SYMBOL_FILE}") + VERBATIM) + + add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}") add_dependencies(generate_breakpad_symbols "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}") add_dependencies(package generate_breakpad_symbols) + endif(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) endif (PACKAGE) if (LL_TESTS) @@ -1855,7 +1890,11 @@ if (LL_TESTS) lldateutil.cpp llmediadataclient.cpp lllogininstance.cpp + llremoteparcelrequest.cpp llviewerhelputil.cpp + llversioninfo.cpp + llworldmap.cpp + llworldmipmap.cpp ) ################################################## @@ -1931,10 +1970,18 @@ if (LL_TESTS) "${test_libs}" ) + LL_ADD_INTEGRATION_TEST(llsimplestat + "" + "${test_libs}" + ) + + LL_ADD_INTEGRATION_TEST(llviewerassetstats + llviewerassetstats.cpp + "${test_libs}" + ) + #ADD_VIEWER_BUILD_TEST(llmemoryview viewer) #ADD_VIEWER_BUILD_TEST(llagentaccess viewer) - #ADD_VIEWER_BUILD_TEST(llworldmap viewer) - #ADD_VIEWER_BUILD_TEST(llworldmipmap viewer) #ADD_VIEWER_BUILD_TEST(lltextureinfo viewer) #ADD_VIEWER_BUILD_TEST(lltextureinfodetails viewer) #ADD_VIEWER_BUILD_TEST(lltexturestatsuploader viewer) diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml index 00d69f805e..e4ac455e7c 100644 --- a/indra/newview/app_settings/cmd_line.xml +++ b/indra/newview/app_settings/cmd_line.xml @@ -118,6 +118,8 @@ <map> <key>desc</key> <string>Log metrics for benchmarking</string> + <key>count</key> + <integer>1</integer> <key>map-to</key> <string>LogMetrics</string> </map> @@ -361,8 +363,7 @@ <map> <key>count</key> <integer>1</integer> - <key>map-to</key> - <string>VersionChannelName</string> + <!-- Special case. Not mapped to a setting. --> </map> <key>loginpage</key> @@ -391,5 +392,12 @@ <string>CrashOnStartup</string> </map> + <key>disablecrashlogger</key> + <map> + <key>desc</key> + <string>Disables the crash logger and lets the OS handle crashes</string> + <key>map-to</key> + <string>DisableCrashLogger</string> + </map> </map> </llsd> diff --git a/indra/newview/app_settings/ignorable_dialogs.xml b/indra/newview/app_settings/ignorable_dialogs.xml index 9ddf007ce7..89fd4e5935 100644 --- a/indra/newview/app_settings/ignorable_dialogs.xml +++ b/indra/newview/app_settings/ignorable_dialogs.xml @@ -23,6 +23,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>FirstNotUseAvatarPicker</key> + <map> + <key>Comment</key> + <string>Shows hint when resident doesn't activate avatar picker</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>FirstNotUseSidePanel</key> <map> <key>Comment</key> @@ -56,6 +67,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>FirstViewPopup</key> + <map> + <key>Comment</key> + <string>Shows hint when resident opens view popup</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>FirstReceiveLindens</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/keys.ini b/indra/newview/app_settings/keys.ini deleted file mode 100644 index b79e5bf508..0000000000 --- a/indra/newview/app_settings/keys.ini +++ /dev/null @@ -1,357 +0,0 @@ -# keys.ini -# -# keyboard binding initialization -# -# comments must have # in the first column -# blank lines OK -# -# Format: -# mode key mask function -# -# mode must be one of FIRST_PERSON, THIRD_PERSON, EDIT, EDIT_AVATAR, or CONVERSATION -# key must be upper case, or SPACE, HOME, END, PGUP, PGDN, LEFT, RIGHT, UP, DOWN, -# or one of ,.;'[] -# mask must be NONE, SHIFT, ALT, ALT_SHIFT. -# Control is reserved for user commands. -# function must be a function named in llkeyboard.cpp - -FIRST_PERSON A NONE slide_left -FIRST_PERSON D NONE slide_right -FIRST_PERSON W NONE push_forward -FIRST_PERSON S NONE push_backward -FIRST_PERSON E NONE jump -FIRST_PERSON C NONE push_down -FIRST_PERSON F NONE toggle_fly - -FIRST_PERSON LEFT NONE slide_left -FIRST_PERSON RIGHT NONE slide_right -FIRST_PERSON UP NONE push_forward -FIRST_PERSON DOWN NONE push_backward -FIRST_PERSON PGUP NONE jump -FIRST_PERSON PGDN NONE push_down -FIRST_PERSON HOME NONE toggle_fly - -FIRST_PERSON PAD_LEFT NONE slide_left -FIRST_PERSON PAD_RIGHT NONE slide_right -FIRST_PERSON PAD_UP NONE push_forward -FIRST_PERSON PAD_DOWN NONE push_backward -FIRST_PERSON PAD_PGUP NONE jump -FIRST_PERSON PAD_PGDN NONE push_down -FIRST_PERSON PAD_HOME NONE toggle_fly -FIRST_PERSON PAD_CENTER NONE stop_moving -FIRST_PERSON PAD_ENTER NONE start_chat -FIRST_PERSON PAD_DIVIDE NONE start_gesture - -FIRST_PERSON A SHIFT slide_left -FIRST_PERSON D SHIFT slide_right -FIRST_PERSON W SHIFT push_forward -FIRST_PERSON S SHIFT push_backward -FIRST_PERSON E SHIFT jump -FIRST_PERSON C SHIFT push_down -FIRST_PERSON F SHIFT toggle_fly - -FIRST_PERSON SPACE NONE stop_moving -FIRST_PERSON ENTER NONE start_chat -FIRST_PERSON DIVIDE NONE start_gesture - -FIRST_PERSON LEFT SHIFT slide_left -FIRST_PERSON RIGHT SHIFT slide_right -FIRST_PERSON UP SHIFT push_forward -FIRST_PERSON DOWN SHIFT push_backward -FIRST_PERSON PGUP SHIFT jump -FIRST_PERSON PGDN SHIFT push_down - -FIRST_PERSON PAD_LEFT SHIFT slide_left -FIRST_PERSON PAD_RIGHT SHIFT slide_right -FIRST_PERSON PAD_UP SHIFT push_forward -FIRST_PERSON PAD_DOWN SHIFT push_backward -FIRST_PERSON PAD_PGUP SHIFT jump -FIRST_PERSON PAD_PGDN SHIFT push_down -FIRST_PERSON PAD_HOME SHIFT toggle_fly -FIRST_PERSON PAD_ENTER SHIFT start_chat -FIRST_PERSON PAD_DIVIDE SHIFT start_gesture - -THIRD_PERSON A NONE turn_left -THIRD_PERSON D NONE turn_right -THIRD_PERSON A SHIFT slide_left -THIRD_PERSON D SHIFT slide_right -THIRD_PERSON W NONE push_forward -THIRD_PERSON S NONE push_backward -THIRD_PERSON W SHIFT push_forward -THIRD_PERSON S SHIFT push_backward -THIRD_PERSON E NONE jump -THIRD_PERSON C NONE push_down -THIRD_PERSON E SHIFT jump -THIRD_PERSON C SHIFT push_down - -THIRD_PERSON F NONE toggle_fly -THIRD_PERSON F SHIFT toggle_fly - -THIRD_PERSON SPACE NONE stop_moving -THIRD_PERSON ENTER NONE start_chat -THIRD_PERSON DIVIDE NONE start_gesture - -THIRD_PERSON LEFT NONE turn_left -THIRD_PERSON LEFT SHIFT slide_left -THIRD_PERSON RIGHT NONE turn_right -THIRD_PERSON RIGHT SHIFT slide_right -THIRD_PERSON UP NONE push_forward -THIRD_PERSON DOWN NONE push_backward -THIRD_PERSON UP SHIFT push_forward -THIRD_PERSON DOWN SHIFT push_backward -THIRD_PERSON PGUP NONE jump -THIRD_PERSON PGDN NONE push_down -THIRD_PERSON PGUP SHIFT jump -THIRD_PERSON PGDN SHIFT push_down -THIRD_PERSON HOME SHIFT toggle_fly -THIRD_PERSON HOME NONE toggle_fly - -THIRD_PERSON PAD_LEFT NONE turn_left -THIRD_PERSON PAD_LEFT SHIFT slide_left -THIRD_PERSON PAD_RIGHT NONE turn_right -THIRD_PERSON PAD_RIGHT SHIFT slide_right -THIRD_PERSON PAD_UP NONE push_forward -THIRD_PERSON PAD_DOWN NONE push_backward -THIRD_PERSON PAD_UP SHIFT push_forward -THIRD_PERSON PAD_DOWN SHIFT push_backward -THIRD_PERSON PAD_PGUP NONE jump -THIRD_PERSON PAD_PGDN NONE push_down -THIRD_PERSON PAD_PGUP SHIFT jump -THIRD_PERSON PAD_PGDN SHIFT push_down -THIRD_PERSON PAD_HOME NONE toggle_fly -THIRD_PERSON PAD_HOME SHIFT toggle_fly -THIRD_PERSON PAD_CENTER NONE stop_moving -THIRD_PERSON PAD_CENTER SHIFT stop_moving -THIRD_PERSON PAD_ENTER NONE start_chat -THIRD_PERSON PAD_ENTER SHIFT start_chat -THIRD_PERSON PAD_DIVIDE NONE start_gesture -THIRD_PERSON PAD_DIVIDE SHIFT start_gesture - -# Camera controls in third person on Alt -THIRD_PERSON LEFT ALT spin_around_cw -THIRD_PERSON RIGHT ALT spin_around_ccw -THIRD_PERSON UP ALT move_forward -THIRD_PERSON DOWN ALT move_backward -THIRD_PERSON PGUP ALT spin_over -THIRD_PERSON PGDN ALT spin_under - -THIRD_PERSON A ALT spin_around_cw -THIRD_PERSON D ALT spin_around_ccw -THIRD_PERSON W ALT move_forward -THIRD_PERSON S ALT move_backward -THIRD_PERSON E ALT spin_over -THIRD_PERSON C ALT spin_under - -THIRD_PERSON PAD_LEFT ALT spin_around_cw -THIRD_PERSON PAD_RIGHT ALT spin_around_ccw -THIRD_PERSON PAD_UP ALT move_forward -THIRD_PERSON PAD_DOWN ALT move_backward -THIRD_PERSON PAD_PGUP ALT spin_over -THIRD_PERSON PAD_PGDN ALT spin_under -THIRD_PERSON PAD_ENTER ALT start_chat -THIRD_PERSON PAD_DIVIDE ALT start_gesture - -# mimic alt zoom behavior with keyboard only -THIRD_PERSON A CTL_ALT spin_around_cw -THIRD_PERSON D CTL_ALT spin_around_ccw -THIRD_PERSON W CTL_ALT spin_over -THIRD_PERSON S CTL_ALT spin_under -THIRD_PERSON E CTL_ALT spin_over -THIRD_PERSON C CTL_ALT spin_under - -THIRD_PERSON LEFT CTL_ALT spin_around_cw -THIRD_PERSON RIGHT CTL_ALT spin_around_ccw -THIRD_PERSON UP CTL_ALT spin_over -THIRD_PERSON DOWN CTL_ALT spin_under -THIRD_PERSON PGUP CTL_ALT spin_over -THIRD_PERSON PGDN CTL_ALT spin_under - -THIRD_PERSON PAD_LEFT CTL_ALT spin_around_cw -THIRD_PERSON PAD_RIGHT CTL_ALT spin_around_ccw -THIRD_PERSON PAD_UP CTL_ALT spin_over -THIRD_PERSON PAD_DOWN CTL_ALT spin_under -THIRD_PERSON PAD_PGUP CTL_ALT spin_over -THIRD_PERSON PAD_PGDN CTL_ALT spin_under -THIRD_PERSON PAD_ENTER CTL_ALT start_chat -THIRD_PERSON PAD_DIVIDE CTL_ALT start_gesture - -# Therefore pan on Alt-Shift -THIRD_PERSON A CTL_ALT_SHIFT pan_left -THIRD_PERSON D CTL_ALT_SHIFT pan_right -THIRD_PERSON W CTL_ALT_SHIFT pan_up -THIRD_PERSON S CTL_ALT_SHIFT pan_down - -THIRD_PERSON LEFT CTL_ALT_SHIFT pan_left -THIRD_PERSON RIGHT CTL_ALT_SHIFT pan_right -THIRD_PERSON UP CTL_ALT_SHIFT pan_up -THIRD_PERSON DOWN CTL_ALT_SHIFT pan_down - -THIRD_PERSON PAD_LEFT CTL_ALT_SHIFT pan_left -THIRD_PERSON PAD_RIGHT CTL_ALT_SHIFT pan_right -THIRD_PERSON PAD_UP CTL_ALT_SHIFT pan_up -THIRD_PERSON PAD_DOWN CTL_ALT_SHIFT pan_down -THIRD_PERSON PAD_ENTER CTL_ALT_SHIFT start_chat -THIRD_PERSON PAD_DIVIDE CTL_ALT_SHIFT start_gesture - -# Basic editing camera control -EDIT A NONE spin_around_cw -EDIT D NONE spin_around_ccw -EDIT W NONE move_forward -EDIT S NONE move_backward -EDIT E NONE spin_over -EDIT C NONE spin_under -EDIT ENTER NONE start_chat -EDIT DIVIDE NONE start_gesture -EDIT PAD_ENTER NONE start_chat -EDIT PAD_DIVIDE NONE start_gesture - -EDIT LEFT NONE spin_around_cw -EDIT RIGHT NONE spin_around_ccw -EDIT UP NONE move_forward -EDIT DOWN NONE move_backward -EDIT PGUP NONE spin_over -EDIT PGDN NONE spin_under - -EDIT A SHIFT pan_left -EDIT D SHIFT pan_right -EDIT W SHIFT pan_up -EDIT S SHIFT pan_down - -EDIT LEFT SHIFT pan_left -EDIT RIGHT SHIFT pan_right -EDIT UP SHIFT pan_up -EDIT DOWN SHIFT pan_down - -# Walking works with ALT held down. -EDIT A ALT slide_left -EDIT D ALT slide_right -EDIT W ALT push_forward -EDIT S ALT push_backward -EDIT E ALT jump -EDIT C ALT push_down - -EDIT LEFT ALT slide_left -EDIT RIGHT ALT slide_right -EDIT UP ALT push_forward -EDIT DOWN ALT push_backward -EDIT PGUP ALT jump -EDIT PGDN ALT push_down -EDIT HOME ALT toggle_fly - -EDIT PAD_LEFT ALT slide_left -EDIT PAD_RIGHT ALT slide_right -EDIT PAD_UP ALT push_forward -EDIT PAD_DOWN ALT push_backward -EDIT PAD_PGUP ALT jump -EDIT PAD_PGDN ALT push_down -EDIT PAD_ENTER ALT start_chat -EDIT PAD_DIVIDE ALT start_gesture - -SITTING A ALT spin_around_cw -SITTING D ALT spin_around_ccw -SITTING W ALT move_forward -SITTING S ALT move_backward -SITTING E ALT spin_over_sitting -SITTING C ALT spin_under_sitting - -SITTING LEFT ALT spin_around_cw -SITTING RIGHT ALT spin_around_ccw -SITTING UP ALT move_forward -SITTING DOWN ALT move_backward -SITTING PGUP ALT spin_over -SITTING PGDN ALT spin_under - -SITTING A CTL_ALT spin_around_cw -SITTING D CTL_ALT spin_around_ccw -SITTING W CTL_ALT spin_over -SITTING S CTL_ALT spin_under -SITTING E CTL_ALT spin_over -SITTING C CTL_ALT spin_under - -SITTING LEFT CTL_ALT spin_around_cw -SITTING RIGHT CTL_ALT spin_around_ccw -SITTING UP CTL_ALT spin_over -SITTING DOWN CTL_ALT spin_under -SITTING PGUP CTL_ALT spin_over -SITTING PGDN CTL_ALT spin_under - - -SITTING A NONE spin_around_cw_sitting -SITTING D NONE spin_around_ccw_sitting -SITTING W NONE move_forward_sitting -SITTING S NONE move_backward_sitting -SITTING E NONE spin_over_sitting -SITTING C NONE spin_under_sitting - -SITTING LEFT NONE spin_around_cw_sitting -SITTING RIGHT NONE spin_around_ccw_sitting -SITTING UP NONE move_forward_sitting -SITTING DOWN NONE move_backward_sitting -SITTING PGUP NONE spin_over_sitting -SITTING PGDN NONE spin_under_sitting - -SITTING PAD_LEFT NONE spin_around_cw_sitting -SITTING PAD_RIGHT NONE spin_around_ccw_sitting -SITTING PAD_UP NONE move_forward_sitting -SITTING PAD_DOWN NONE move_backward_sitting -SITTING PAD_PGUP NONE spin_over_sitting -SITTING PAD_PGDN NONE spin_under_sitting -SITTING PAD_CENTER NONE stop_moving -SITTING PAD_ENTER NONE start_chat -SITTING PAD_DIVIDE NONE start_gesture - -# these are for passing controls when sitting on vehicles -SITTING A SHIFT slide_left -SITTING D SHIFT slide_right -SITTING LEFT SHIFT slide_left -SITTING RIGHT SHIFT slide_right - -SITTING PAD_LEFT SHIFT slide_left -SITTING PAD_RIGHT SHIFT slide_right -SITTING PAD_ENTER SHIFT start_chat -SITTING PAD_DIVIDE SHIFT start_gesture - -# pan on Alt-Shift -SITTING A CTL_ALT_SHIFT pan_left -SITTING D CTL_ALT_SHIFT pan_right -SITTING W CTL_ALT_SHIFT pan_up -SITTING S CTL_ALT_SHIFT pan_down - -SITTING LEFT CTL_ALT_SHIFT pan_left -SITTING RIGHT CTL_ALT_SHIFT pan_right -SITTING UP CTL_ALT_SHIFT pan_up -SITTING DOWN CTL_ALT_SHIFT pan_down - -SITTING PAD_LEFT CTL_ALT_SHIFT pan_left -SITTING PAD_RIGHT CTL_ALT_SHIFT pan_right -SITTING PAD_UP CTL_ALT_SHIFT pan_up -SITTING PAD_DOWN CTL_ALT_SHIFT pan_down -SITTING PAD_ENTER CTL_ALT_SHIFT start_chat -SITTING PAD_DIVIDE CTL_ALT_SHIFT start_gesture - -SITTING ENTER NONE start_chat -SITTING DIVIDE NONE start_gesture - -# Avatar editing camera controls -EDIT_AVATAR A NONE edit_avatar_spin_cw -EDIT_AVATAR D NONE edit_avatar_spin_ccw -EDIT_AVATAR W NONE edit_avatar_move_forward -EDIT_AVATAR S NONE edit_avatar_move_backward -EDIT_AVATAR E NONE edit_avatar_spin_over -EDIT_AVATAR C NONE edit_avatar_spin_under -EDIT_AVATAR LEFT NONE edit_avatar_spin_cw -EDIT_AVATAR RIGHT NONE edit_avatar_spin_ccw -EDIT_AVATAR UP NONE edit_avatar_move_forward -EDIT_AVATAR DOWN NONE edit_avatar_move_backward -EDIT_AVATAR PGUP NONE edit_avatar_spin_over -EDIT_AVATAR PGDN NONE edit_avatar_spin_under -EDIT_AVATAR ENTER NONE start_chat -EDIT_AVATAR DIVIDE NONE start_gesture -EDIT_AVATAR PAD_LEFT NONE edit_avatar_spin_cw -EDIT_AVATAR PAD_RIGHT NONE edit_avatar_spin_ccw -EDIT_AVATAR PAD_UP NONE edit_avatar_move_forward -EDIT_AVATAR PAD_DOWN NONE edit_avatar_move_backward -EDIT_AVATAR PAD_PGUP NONE edit_avatar_spin_over -EDIT_AVATAR PAD_PGDN NONE edit_avatar_spin_under -EDIT_AVATAR PAD_ENTER NONE start_chat -EDIT_AVATAR PAD_DIVIDE NONE start_gesture diff --git a/indra/newview/app_settings/keys.xml b/indra/newview/app_settings/keys.xml new file mode 100644 index 0000000000..d085475c6c --- /dev/null +++ b/indra/newview/app_settings/keys.xml @@ -0,0 +1,350 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<keys> + <first_person> + <binding key="A" mask="NONE" command="slide_left"/> + <binding key="D" mask="NONE" command="slide_right"/> + <binding key="W" mask="NONE" command="push_forward"/> + <binding key="S" mask="NONE" command="push_backward"/> + <binding key="E" mask="NONE" command="jump"/> + <binding key="C" mask="NONE" command="push_down"/> + <binding key="F" mask="NONE" command="toggle_fly"/> + + <binding key="LEFT" mask="NONE" command="slide_left"/> + <binding key="RIGHT" mask="NONE" command="slide_right"/> + <binding key="UP" mask="NONE" command="push_forward"/> + <binding key="DOWN" mask="NONE" command="push_backward"/> + <binding key="PGUP" mask="NONE" command="jump"/> + <binding key="PGDN" mask="NONE" command="push_down"/> + <binding key="HOME" mask="NONE" command="toggle_fly"/> + + <binding key="PAD_LEFT" mask="NONE" command="slide_left"/> + <binding key="PAD_RIGHT" mask="NONE" command="slide_right"/> + <binding key="PAD_UP" mask="NONE" command="push_forward"/> + <binding key="PAD_DOWN" mask="NONE" command="push_backward"/> + <binding key="PAD_PGUP" mask="NONE" command="jump"/> + <binding key="PAD_PGDN" mask="NONE" command="push_down"/> + <binding key="PAD_HOME" mask="NONE" command="toggle_fly"/> + <binding key="PAD_CENTER" mask="NONE" command="stop_moving"/> + <binding key="PAD_ENTER" mask="NONE" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="NONE" command="start_gesture"/> + + <binding key="A" mask="SHIFT" command="slide_left"/> + <binding key="D" mask="SHIFT" command="slide_right"/> + <binding key="W" mask="SHIFT" command="push_forward"/> + <binding key="S" mask="SHIFT" command="push_backward"/> + <binding key="E" mask="SHIFT" command="jump"/> + <binding key="C" mask="SHIFT" command="push_down"/> + <binding key="F" mask="SHIFT" command="toggle_fly"/> + + <binding key="SPACE" mask="NONE" command="stop_moving"/> + <binding key="ENTER" mask="NONE" command="start_chat"/> + <binding key="DIVIDE" mask="NONE" command="start_gesture"/> + + <binding key="LEFT" mask="SHIFT" command="slide_left"/> + <binding key="RIGHT" mask="SHIFT" command="slide_right"/> + <binding key="UP" mask="SHIFT" command="push_forward"/> + <binding key="DOWN" mask="SHIFT" command="push_backward"/> + <binding key="PGUP" mask="SHIFT" command="jump"/> + <binding key="PGDN" mask="SHIFT" command="push_down"/> + + <binding key="PAD_LEFT" mask="SHIFT" command="slide_left"/> + <binding key="PAD_RIGHT" mask="SHIFT" command="slide_right"/> + <binding key="PAD_UP" mask="SHIFT" command="push_forward"/> + <binding key="PAD_DOWN" mask="SHIFT" command="push_backward"/> + <binding key="PAD_PGUP" mask="SHIFT" command="jump"/> + <binding key="PAD_PGDN" mask="SHIFT" command="push_down"/> + <binding key="PAD_HOME" mask="SHIFT" command="toggle_fly"/> + <binding key="PAD_ENTER" mask="SHIFT" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="SHIFT" command="start_gesture"/> + </first_person> + <third_person> + <binding key="A" mask="NONE" command="turn_left"/> + <binding key="D" mask="NONE" command="turn_right"/> + <binding key="A" mask="SHIFT" command="slide_left"/> + <binding key="D" mask="SHIFT" command="slide_right"/> + <binding key="W" mask="NONE" command="push_forward"/> + <binding key="S" mask="NONE" command="push_backward"/> + <binding key="W" mask="SHIFT" command="push_forward"/> + <binding key="S" mask="SHIFT" command="push_backward"/> + <binding key="E" mask="NONE" command="jump"/> + <binding key="C" mask="NONE" command="push_down"/> + <binding key="E" mask="SHIFT" command="jump"/> + <binding key="C" mask="SHIFT" command="push_down"/> + + <binding key="F" mask="NONE" command="toggle_fly"/> + <binding key="F" mask="SHIFT" command="toggle_fly"/> + + <binding key="SPACE" mask="NONE" command="stop_moving"/> + <binding key="ENTER" mask="NONE" command="start_chat"/> + <binding key="DIVIDE" mask="NONE" command="start_gesture"/> + + <binding key="LEFT" mask="NONE" command="turn_left"/> + <binding key="LEFT" mask="SHIFT" command="slide_left"/> + <binding key="RIGHT" mask="NONE" command="turn_right"/> + <binding key="RIGHT" mask="SHIFT" command="slide_right"/> + <binding key="UP" mask="NONE" command="push_forward"/> + <binding key="DOWN" mask="NONE" command="push_backward"/> + <binding key="UP" mask="SHIFT" command="push_forward"/> + <binding key="DOWN" mask="SHIFT" command="push_backward"/> + <binding key="PGUP" mask="NONE" command="jump"/> + <binding key="PGDN" mask="NONE" command="push_down"/> + <binding key="PGUP" mask="SHIFT" command="jump"/> + <binding key="PGDN" mask="SHIFT" command="push_down"/> + <binding key="HOME" mask="SHIFT" command="toggle_fly"/> + <binding key="HOME" mask="NONE" command="toggle_fly"/> + + <binding key="PAD_LEFT" mask="NONE" command="turn_left"/> + <binding key="PAD_LEFT" mask="SHIFT" command="slide_left"/> + <binding key="PAD_RIGHT" mask="NONE" command="turn_right"/> + <binding key="PAD_RIGHT" mask="SHIFT" command="slide_right"/> + <binding key="PAD_UP" mask="NONE" command="push_forward"/> + <binding key="PAD_DOWN" mask="NONE" command="push_backward"/> + <binding key="PAD_UP" mask="SHIFT" command="push_forward"/> + <binding key="PAD_DOWN" mask="SHIFT" command="push_backward"/> + <binding key="PAD_PGUP" mask="NONE" command="jump"/> + <binding key="PAD_PGDN" mask="NONE" command="push_down"/> + <binding key="PAD_PGUP" mask="SHIFT" command="jump"/> + <binding key="PAD_PGDN" mask="SHIFT" command="push_down"/> + <binding key="PAD_HOME" mask="NONE" command="toggle_fly"/> + <binding key="PAD_HOME" mask="SHIFT" command="toggle_fly"/> + <binding key="PAD_CENTER" mask="NONE" command="stop_moving"/> + <binding key="PAD_CENTER" mask="SHIFT" command="stop_moving"/> + <binding key="PAD_ENTER" mask="NONE" command="start_chat"/> + <binding key="PAD_ENTER" mask="SHIFT" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="NONE" command="start_gesture"/> + <binding key="PAD_DIVIDE" mask="SHIFT" command="start_gesture"/> + + <!--Camera controls in third person on Alt--> + <binding key="LEFT" mask="ALT" command="spin_around_cw"/> + <binding key="RIGHT" mask="ALT" command="spin_around_ccw"/> + <binding key="UP" mask="ALT" command="move_forward"/> + <binding key="DOWN" mask="ALT" command="move_backward"/> + <binding key="PGUP" mask="ALT" command="spin_over"/> + <binding key="PGDN" mask="ALT" command="spin_under"/> + + <binding key="A" mask="ALT" command="spin_around_cw"/> + <binding key="D" mask="ALT" command="spin_around_ccw"/> + <binding key="W" mask="ALT" command="move_forward"/> + <binding key="S" mask="ALT" command="move_backward"/> + <binding key="E" mask="ALT" command="spin_over"/> + <binding key="C" mask="ALT" command="spin_under"/> + + <binding key="PAD_LEFT" mask="ALT" command="spin_around_cw"/> + <binding key="PAD_RIGHT" mask="ALT" command="spin_around_ccw"/> + <binding key="PAD_UP" mask="ALT" command="move_forward"/> + <binding key="PAD_DOWN" mask="ALT" command="move_backward"/> + <binding key="PAD_PGUP" mask="ALT" command="spin_over"/> + <binding key="PAD_PGDN" mask="ALT" command="spin_under"/> + <binding key="PAD_ENTER" mask="ALT" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="ALT" command="start_gesture"/> + + <!--mimic alt zoom behavior with keyboard only--> + <binding key="A" mask="CTL_ALT" command="spin_around_cw"/> + <binding key="D" mask="CTL_ALT" command="spin_around_ccw"/> + <binding key="W" mask="CTL_ALT" command="spin_over"/> + <binding key="S" mask="CTL_ALT" command="spin_under"/> + <binding key="E" mask="CTL_ALT" command="spin_over"/> + <binding key="C" mask="CTL_ALT" command="spin_under"/> + + <binding key="LEFT" mask="CTL_ALT" command="spin_around_cw"/> + <binding key="RIGHT" mask="CTL_ALT" command="spin_around_ccw"/> + <binding key="UP" mask="CTL_ALT" command="spin_over"/> + <binding key="DOWN" mask="CTL_ALT" command="spin_under"/> + <binding key="PGUP" mask="CTL_ALT" command="spin_over"/> + <binding key="PGDN" mask="CTL_ALT" command="spin_under"/> + + <binding key="PAD_LEFT" mask="CTL_ALT" command="spin_around_cw"/> + <binding key="PAD_RIGHT" mask="CTL_ALT" command="spin_around_ccw"/> + <binding key="PAD_UP" mask="CTL_ALT" command="spin_over"/> + <binding key="PAD_DOWN" mask="CTL_ALT" command="spin_under"/> + <binding key="PAD_PGUP" mask="CTL_ALT" command="spin_over"/> + <binding key="PAD_PGDN" mask="CTL_ALT" command="spin_under"/> + <binding key="PAD_ENTER" mask="CTL_ALT" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="CTL_ALT" command="start_gesture"/> + + <!--Therefore pan on Alt-Shift--> + <binding key="A" mask="CTL_ALT_SHIFT" command="pan_left"/> + <binding key="D" mask="CTL_ALT_SHIFT" command="pan_right"/> + <binding key="W" mask="CTL_ALT_SHIFT" command="pan_up"/> + <binding key="S" mask="CTL_ALT_SHIFT" command="pan_down"/> + + <binding key="LEFT" mask="CTL_ALT_SHIFT" command="pan_left"/> + <binding key="RIGHT" mask="CTL_ALT_SHIFT" command="pan_right"/> + <binding key="UP" mask="CTL_ALT_SHIFT" command="pan_up"/> + <binding key="DOWN" mask="CTL_ALT_SHIFT" command="pan_down"/> + + <binding key="PAD_LEFT" mask="CTL_ALT_SHIFT" command="pan_left"/> + <binding key="PAD_RIGHT" mask="CTL_ALT_SHIFT" command="pan_right"/> + <binding key="PAD_UP" mask="CTL_ALT_SHIFT" command="pan_up"/> + <binding key="PAD_DOWN" mask="CTL_ALT_SHIFT" command="pan_down"/> + <binding key="PAD_ENTER" mask="CTL_ALT_SHIFT" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="CTL_ALT_SHIFT" command="start_gesture"/> + </third_person> + + # Basic editing camera control + <edit> + <binding key="A" mask="NONE" command="spin_around_cw"/> + <binding key="D" mask="NONE" command="spin_around_ccw"/> + <binding key="W" mask="NONE" command="move_forward"/> + <binding key="S" mask="NONE" command="move_backward"/> + <binding key="E" mask="NONE" command="spin_over"/> + <binding key="C" mask="NONE" command="spin_under"/> + <binding key="ENTER" mask="NONE" command="start_chat"/> + <binding key="DIVIDE" mask="NONE" command="start_gesture"/> + <binding key="PAD_ENTER" mask="NONE" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="NONE" command="start_gesture"/> + + <binding key="LEFT" mask="NONE" command="spin_around_cw"/> + <binding key="RIGHT" mask="NONE" command="spin_around_ccw"/> + <binding key="UP" mask="NONE" command="move_forward"/> + <binding key="DOWN" mask="NONE" command="move_backward"/> + <binding key="PGUP" mask="NONE" command="spin_over"/> + <binding key="PGDN" mask="NONE" command="spin_under"/> + + <binding key="A" mask="SHIFT" command="pan_left"/> + <binding key="D" mask="SHIFT" command="pan_right"/> + <binding key="W" mask="SHIFT" command="pan_up"/> + <binding key="S" mask="SHIFT" command="pan_down"/> + + <binding key="LEFT" mask="SHIFT" command="pan_left"/> + <binding key="RIGHT" mask="SHIFT" command="pan_right"/> + <binding key="UP" mask="SHIFT" command="pan_up"/> + <binding key="DOWN" mask="SHIFT" command="pan_down"/> + + <!--Walking works with ALT held down.--> + <binding key="A" mask="ALT" command="slide_left"/> + <binding key="D" mask="ALT" command="slide_right"/> + <binding key="W" mask="ALT" command="push_forward"/> + <binding key="S" mask="ALT" command="push_backward"/> + <binding key="E" mask="ALT" command="jump"/> + <binding key="C" mask="ALT" command="push_down"/> + + <binding key="LEFT" mask="ALT" command="slide_left"/> + <binding key="RIGHT" mask="ALT" command="slide_right"/> + <binding key="UP" mask="ALT" command="push_forward"/> + <binding key="DOWN" mask="ALT" command="push_backward"/> + <binding key="PGUP" mask="ALT" command="jump"/> + <binding key="PGDN" mask="ALT" command="push_down"/> + <binding key="HOME" mask="ALT" command="toggle_fly"/> + + <binding key="PAD_LEFT" mask="ALT" command="slide_left"/> + <binding key="PAD_RIGHT" mask="ALT" command="slide_right"/> + <binding key="PAD_UP" mask="ALT" command="push_forward"/> + <binding key="PAD_DOWN" mask="ALT" command="push_backward"/> + <binding key="PAD_PGUP" mask="ALT" command="jump"/> + <binding key="PAD_PGDN" mask="ALT" command="push_down"/> + <binding key="PAD_ENTER" mask="ALT" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="ALT" command="start_gesture"/> + </edit> + <sitting> + <binding key="A" mask="ALT" command="spin_around_cw"/> + <binding key="D" mask="ALT" command="spin_around_ccw"/> + <binding key="W" mask="ALT" command="move_forward"/> + <binding key="S" mask="ALT" command="move_backward"/> + <binding key="E" mask="ALT" command="spin_over_sitting"/> + <binding key="C" mask="ALT" command="spin_under_sitting"/> + + <binding key="LEFT" mask="ALT" command="spin_around_cw"/> + <binding key="RIGHT" mask="ALT" command="spin_around_ccw"/> + <binding key="UP" mask="ALT" command="move_forward"/> + <binding key="DOWN" mask="ALT" command="move_backward"/> + <binding key="PGUP" mask="ALT" command="spin_over"/> + <binding key="PGDN" mask="ALT" command="spin_under"/> + + <binding key="A" mask="CTL_ALT" command="spin_around_cw"/> + <binding key="D" mask="CTL_ALT" command="spin_around_ccw"/> + <binding key="W" mask="CTL_ALT" command="spin_over"/> + <binding key="S" mask="CTL_ALT" command="spin_under"/> + <binding key="E" mask="CTL_ALT" command="spin_over"/> + <binding key="C" mask="CTL_ALT" command="spin_under"/> + + <binding key="LEFT" mask="CTL_ALT" command="spin_around_cw"/> + <binding key="RIGHT" mask="CTL_ALT" command="spin_around_ccw"/> + <binding key="UP" mask="CTL_ALT" command="spin_over"/> + <binding key="DOWN" mask="CTL_ALT" command="spin_under"/> + <binding key="PGUP" mask="CTL_ALT" command="spin_over"/> + <binding key="PGDN" mask="CTL_ALT" command="spin_under"/> + + + <binding key="A" mask="NONE" command="spin_around_cw_sitting"/> + <binding key="D" mask="NONE" command="spin_around_ccw_sitting"/> + <binding key="W" mask="NONE" command="move_forward_sitting"/> + <binding key="S" mask="NONE" command="move_backward_sitting"/> + <binding key="E" mask="NONE" command="spin_over_sitting"/> + <binding key="C" mask="NONE" command="spin_under_sitting"/> + + <binding key="LEFT" mask="NONE" command="spin_around_cw_sitting"/> + <binding key="RIGHT" mask="NONE" command="spin_around_ccw_sitting"/> + <binding key="UP" mask="NONE" command="move_forward_sitting"/> + <binding key="DOWN" mask="NONE" command="move_backward_sitting"/> + <binding key="PGUP" mask="NONE" command="spin_over_sitting"/> + <binding key="PGDN" mask="NONE" command="spin_under_sitting"/> + + <binding key="PAD_LEFT" mask="NONE" command="spin_around_cw_sitting"/> + <binding key="PAD_RIGHT" mask="NONE" command="spin_around_ccw_sitting"/> + <binding key="PAD_UP" mask="NONE" command="move_forward_sitting"/> + <binding key="PAD_DOWN" mask="NONE" command="move_backward_sitting"/> + <binding key="PAD_PGUP" mask="NONE" command="spin_over_sitting"/> + <binding key="PAD_PGDN" mask="NONE" command="spin_under_sitting"/> + <binding key="PAD_CENTER" mask="NONE" command="stop_moving"/> + <binding key="PAD_ENTER" mask="NONE" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="NONE" command="start_gesture"/> + + <!--these are for passing controls when sitting on vehicles--> + <binding key="A" mask="SHIFT" command="slide_left"/> + <binding key="D" mask="SHIFT" command="slide_right"/> + <binding key="LEFT" mask="SHIFT" command="slide_left"/> + <binding key="RIGHT" mask="SHIFT" command="slide_right"/> + + <binding key="PAD_LEFT" mask="SHIFT" command="slide_left"/> + <binding key="PAD_RIGHT" mask="SHIFT" command="slide_right"/> + <binding key="PAD_ENTER" mask="SHIFT" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="SHIFT" command="start_gesture"/> + + <!--pan on Alt-Shift--> + <binding key="A" mask="CTL_ALT_SHIFT" command="pan_left"/> + <binding key="D" mask="CTL_ALT_SHIFT" command="pan_right"/> + <binding key="W" mask="CTL_ALT_SHIFT" command="pan_up"/> + <binding key="S" mask="CTL_ALT_SHIFT" command="pan_down"/> + + <binding key="LEFT" mask="CTL_ALT_SHIFT" command="pan_left"/> + <binding key="RIGHT" mask="CTL_ALT_SHIFT" command="pan_right"/> + <binding key="UP" mask="CTL_ALT_SHIFT" command="pan_up"/> + <binding key="DOWN" mask="CTL_ALT_SHIFT" command="pan_down"/> + + <binding key="PAD_LEFT" mask="CTL_ALT_SHIFT" command="pan_left"/> + <binding key="PAD_RIGHT" mask="CTL_ALT_SHIFT" command="pan_right"/> + <binding key="PAD_UP" mask="CTL_ALT_SHIFT" command="pan_up"/> + <binding key="PAD_DOWN" mask="CTL_ALT_SHIFT" command="pan_down"/> + <binding key="PAD_ENTER" mask="CTL_ALT_SHIFT" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="CTL_ALT_SHIFT" command="start_gesture"/> + + <binding key="ENTER" mask="NONE" command="start_chat"/> + <binding key="DIVIDE" mask="NONE" command="start_gesture"/> + </sitting> + <edit_avatar> + <!--Avatar editing camera controls--> + <binding key="A" mask="NONE" command="edit_avatar_spin_cw"/> + <binding key="D" mask="NONE" command="edit_avatar_spin_ccw"/> + <binding key="W" mask="NONE" command="edit_avatar_move_forward"/> + <binding key="S" mask="NONE" command="edit_avatar_move_backward"/> + <binding key="E" mask="NONE" command="edit_avatar_spin_over"/> + <binding key="C" mask="NONE" command="edit_avatar_spin_under"/> + <binding key="LEFT" mask="NONE" command="edit_avatar_spin_cw"/> + <binding key="RIGHT" mask="NONE" command="edit_avatar_spin_ccw"/> + <binding key="UP" mask="NONE" command="edit_avatar_move_forward"/> + <binding key="DOWN" mask="NONE" command="edit_avatar_move_backward"/> + <binding key="PGUP" mask="NONE" command="edit_avatar_spin_over"/> + <binding key="PGDN" mask="NONE" command="edit_avatar_spin_under"/> + <binding key="ENTER" mask="NONE" command="start_chat"/> + <binding key="DIVIDE" mask="NONE" command="start_gesture"/> + <binding key="PAD_LEFT" mask="NONE" command="edit_avatar_spin_cw"/> + <binding key="PAD_RIGHT" mask="NONE" command="edit_avatar_spin_ccw"/> + <binding key="PAD_UP" mask="NONE" command="edit_avatar_move_forward"/> + <binding key="PAD_DOWN" mask="NONE" command="edit_avatar_move_backward"/> + <binding key="PAD_PGUP" mask="NONE" command="edit_avatar_spin_over"/> + <binding key="PAD_PGDN" mask="NONE" command="edit_avatar_spin_under"/> + <binding key="PAD_ENTER" mask="NONE" command="start_chat"/> + <binding key="PAD_DIVIDE" mask="NONE" command="start_gesture"/> + </edit_avatar> +</keys>
\ No newline at end of file diff --git a/indra/newview/app_settings/lindenlab.pem b/indra/newview/app_settings/lindenlab.pem new file mode 100644 index 0000000000..eddae0426d --- /dev/null +++ b/indra/newview/app_settings/lindenlab.pem @@ -0,0 +1,97 @@ +-----BEGIN CERTIFICATE----- +MIIEUDCCA7mgAwIBAgIJAN4ppNGwj6yIMA0GCSqGSIb3DQEBBAUAMIHMMQswCQYD +VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j +aXNjbzEZMBcGA1UEChMQTGluZGVuIExhYiwgSW5jLjEpMCcGA1UECxMgTGluZGVu +IExhYiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAnBgNVBAMTIExpbmRlbiBMYWIg +Q2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYJKoZIhvcNAQkBFhBjYUBsaW5kZW5s +YWIuY29tMB4XDTA1MDQyMTAyNDAzMVoXDTI1MDQxNjAyNDAzMVowgcwxCzAJBgNV +BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNp +c2NvMRkwFwYDVQQKExBMaW5kZW4gTGFiLCBJbmMuMSkwJwYDVQQLEyBMaW5kZW4g +TGFiIENlcnRpZmljYXRlIEF1dGhvcml0eTEpMCcGA1UEAxMgTGluZGVuIExhYiBD +ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgkqhkiG9w0BCQEWEGNhQGxpbmRlbmxh +Yi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKXh1MThucdTbMg9bYBO +rAm8yWns32YojB0PRfbq8rUjepEhTm3/13s0u399Uc202v4ejcGhkIDWJZd2NZMF +oKrhmRfxGHSKPCuFaXC3jh0lRECj7k8FoPkcmaPjSyodrDFDUUuv+C06oYJoI+rk +8REyal9NwgHvqCzOrZtiTXAdAgMBAAGjggE2MIIBMjAdBgNVHQ4EFgQUO1zK2e1f +1wO1fHAjq6DTJobKDrcwggEBBgNVHSMEgfkwgfaAFDtcytntX9cDtXxwI6ug0yaG +yg63oYHSpIHPMIHMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW +MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQTGluZGVuIExhYiwgSW5j +LjEpMCcGA1UECxMgTGluZGVuIExhYiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAn +BgNVBAMTIExpbmRlbiBMYWIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYJKoZI +hvcNAQkBFhBjYUBsaW5kZW5sYWIuY29tggkA3imk0bCPrIgwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQQFAAOBgQA/ZkgfvwHYqk1UIAKZS3kMCxz0HvYuEQtviwnu +xA39CIJ65Zozs28Eg1aV9/Y+Of7TnWhW+U3J3/wD/GghaAGiKK6vMn9gJBIdBX/9 +e6ef37VGyiOEFFjnUIbuk0RWty0orN76q/lI/xjCi15XSA/VSq2j4vmnwfZcPTDu +glmQ1A== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEkDCCA3igAwIBAgICTSUwDQYJKoZIhvcNAQEFBQAwQDELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDkdlb1RydXN0LCBJbmMuMRgwFgYDVQQDEw9HZW9UcnVzdCBTU0wg +Q0EwHhcNMTAxMjIwMTkxMTI2WhcNMTIwMjIxMTI1NDAzWjCBnzEpMCcGA1UEBRMg +UkMteW9jbXIwdXRmRTdOMVBlaHJHQXdqL0lNc2hJZS0xCzAJBgNVBAYTAlVTMRMw +EQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMR0wGwYD +VQQKExRMaW5kZW4gUmVzZWFyY2ggSW5jLjEZMBcGA1UEAwwQKi5zZWNvbmRsaWZl +LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN/VCCu1SZ5x4vNp +XZZ8r3lzqeLwjxVZfMSQCKM4lV5DFbqiZMMBto4Y/ib7i0audzuTDnImCLsfzlTu +7iZLoJNy42/43Rq4xtaDZ7joxALFmzXUKEipgHiTTbAbLQNCS4wPXts3tScODVZY +/mhlmXdlLuGxJbqoyOEP6NEQbgXWDCKDERnAEG/FJBVHKyBfg3abrrIuQNwYCKCS +2OZ5Z5MveGmY4tSKUOOi/c0vV9HsanQn/ymybZjxR5Kmb1CvQr7VVtbpR1MhlGkc +sfJz1NFIFxdXkUggIny+XSG1dAAJRFFumyRM+X/eh0NHNmAI14JJ43hB6Zw3dzzl +An9BSeECAwEAAaOCATIwggEuMB8GA1UdIwQYMBaAFEJ5VBthzVUrPmPVPEhX9Z/7 +Rc5KMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH +AwIwKwYDVR0RBCQwIoIQKi5zZWNvbmRsaWZlLmNvbYIOc2Vjb25kbGlmZS5jb20w +PQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2d0c3NsLWNybC5nZW90cnVzdC5jb20v +Y3Jscy9ndHNzbC5jcmwwHQYDVR0OBBYEFK9UTMkc4Fh/Ug4fVs6UVhxP6my0MAwG +A1UdEwEB/wQCMAAwQwYIKwYBBQUHAQEENzA1MDMGCCsGAQUFBzAChidodHRwOi8v +Z3Rzc2wtYWlhLmdlb3RydXN0LmNvbS9ndHNzbC5jcnQwDQYJKoZIhvcNAQEFBQAD +ggEBACIR9yggGHDcZ60AMNdFmZ8XJeahTuv6q2X/It2JxqSQp5BVQUei0NGIYYOt +yg0JFBZn5KqXiQ5Zz84K4hdvh/6grCEAn4v37sozSbkeZ92Lec8NOZR42HfYIOCo +Hx9q7CNRxdhv6ehV4LekaRBxrtp5etVsIDaWvRZEswCWl46VuLrfjcpauj6DAdOQ +FfPVAW+4nPgLr8KapZMnXYnabIwrj9DQLQ88w/D7durenu/SYJEahWW9mb++n9is +eMjyuyzYW0PTUBTaDsj+2ZmHJtoR1tBiLqh0Q62UQnmDgsf5SK5PTb8jnta/1SvN +3pirsuvjMPV19zuH6b9NpJfXfd0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT +MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0 +aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw +WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE +AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m +OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu +T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c +JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR +Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz +PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm +aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM +TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g +LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO +BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv +dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB +AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL +NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W +b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID2TCCAsGgAwIBAgIDAjbQMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTAwMjE5MjIzOTI2WhcNMjAwMjE4MjIzOTI2WjBAMQswCQYDVQQG +EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xGDAWBgNVBAMTD0dlb1RydXN0 +IFNTTCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJCzgMHk5Uat +cGA9uuUU3Z6KXot1WubKbUGlI+g5hSZ6p1V3mkihkn46HhrxJ6ujTDnMyz1Hr4Gu +FmpcN+9FQf37mpc8oEOdxt8XIdGKolbCA0mEEoE+yQpUYGa5jFTk+eb5lPHgX3UR +8im55IaisYmtph6DKWOy8FQchQt65+EuDa+kvc3nsVrXjAVaDktzKIt1XTTYdwvh +dGLicTBi2LyKBeUxY0pUiWozeKdOVSQdl+8a5BLGDzAYtDRN4dgjOyFbLTAZJQ50 +96QhS6CkIMlszZhWwPKoXz4mdaAN+DaIiixafWcwqQ/RmXAueOFRJq9VeiS+jDkN +d53eAsMMvR8CAwEAAaOB2TCB1jAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFEJ5 +VBthzVUrPmPVPEhX9Z/7Rc5KMB8GA1UdIwQYMBaAFMB6mGiNifurBWQMEX2qfWW4 +ysxOMBIGA1UdEwEB/wQIMAYBAf8CAQAwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDov +L2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwNAYIKwYBBQUHAQEE +KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nZW90cnVzdC5jb20wDQYJKoZI +hvcNAQEFBQADggEBANTvU4ToGr2hiwTAqfVfoRB4RV2yV2pOJMtlTjGXkZrUJPji +J2ZwMZzBYlQG55cdOprApClICq8kx6jEmlTBfEx4TCtoLF0XplR4TEbigMMfOHES +0tdT41SFULgCy+5jOvhWiU1Vuy7AyBh3hjELC3DwfjWDpCoTZFZnNF0WX3OsewYk +2k9QbSqr0E1TQcKOu3EDSSmGGM8hQkx0YlEVxW+o78Qn5Rsz3VqI138S0adhJR/V +4NwdzxoQ2KDLX4z6DOW/cf/lXUQdpj6HR/oaToODEj+IZpWYeZqF6wJHzSXj8gYE +TpnKXKBuervdo5AaRTPvvz7SBMS24CqFZUE+ENQ= +-----END CERTIFICATE----- diff --git a/indra/newview/app_settings/llsd.xsd b/indra/newview/app_settings/llsd.xsd new file mode 100644 index 0000000000..34612d9faa --- /dev/null +++ b/indra/newview/app_settings/llsd.xsd @@ -0,0 +1,131 @@ +<?xml version="1.0"?> +<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + + <!-- LLSD document has exactly one value --> + <xsd:element name="llsd"> + <xsd:complexType> + <xsd:group ref="llsd-value" /> + </xsd:complexType> + </xsd:element> + + <!-- Value is one of undef, boolean, integer, real, + uuid, string, date, binary, array, or map --> + <xsd:group name="llsd-value"> + <xsd:choice> + <xsd:element ref="undef"/> + <xsd:element ref="boolean"/> + <xsd:element ref="integer"/> + <xsd:element ref="real"/> + <xsd:element ref="uuid"/> + <xsd:element ref="string"/> + <xsd:element ref="date"/> + <xsd:element ref="uri"/> + <xsd:element ref="binary"/> + <xsd:element ref="array"/> + <xsd:element ref="map"/> + </xsd:choice> + </xsd:group> + + <!-- Undefined is an empty eleemnt --> + <xsd:element name="undef"> + <xsd:simpleType> + <xsd:restriction base="xsd:string"> + <xsd:length value="0" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <!-- Boolean is true or false --> + <xsd:element name="boolean"> + <xsd:simpleType> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="true" /> + <xsd:enumeration value="false" /> + + <!-- In practice, these other serializations are seen: --> + <xsd:enumeration value="" /> + <xsd:enumeration value="1" /> + <xsd:enumeration value="0" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <!-- Integer is restricted to 32-bit signed values --> + <xsd:element name="integer"> + <xsd:simpleType> + <xsd:restriction base="xsd:int" /> + </xsd:simpleType> + </xsd:element> + + <!-- Real is an IEEE 754 "double" value, including Infinities and NaN --> + <xsd:element name="real"> + <xsd:simpleType> + <!-- TODO: xsd:double uses "INF", "-INF", and "NaN", + whereas LLSD prefers "Infinity", "-Infinity" and "NaN" --> + <xsd:restriction base="xsd:double" /> + </xsd:simpleType> + </xsd:element> + + <!-- UUID per RFC 4122 --> + <xsd:element name="uuid"> + <xsd:simpleType> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <!-- String is any sequence of Unicode characters --> + <xsd:element name="string"> + <xsd:simpleType> + <xsd:restriction base="xsd:string" /> + </xsd:simpleType> + </xsd:element> + + <!-- Date is ISO 8601 in UTC --> + <xsd:element name="date"> + <xsd:simpleType> + <xsd:restriction base="xsd:dateTime"> + <!-- Restrict to UTC (Z) times --> + <xsd:pattern value="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?Z" /> + </xsd:restriction> + </xsd:simpleType> + </xsd:element> + + <!-- URI per RFC 3986 --> + <xsd:element name="uri"> + <xsd:simpleType> + <xsd:restriction base="xsd:anyURI" /> + </xsd:simpleType> + </xsd:element> + + <!-- Binary data is base64 encoded --> + <xsd:element name="binary"> + <xsd:simpleType> + <!-- TODO: Require encoding attribute? --> + <xsd:restriction base="xsd:base64Binary" /> + </xsd:simpleType> + </xsd:element> + + <!-- Array is a sequence of zero or more values --> + <xsd:element name="array"> + <xsd:complexType> + <xsd:group minOccurs="0" maxOccurs="unbounded" ref="llsd-value" /> + </xsd:complexType> + </xsd:element> + + <!-- Map is a sequence of zero or more key/value pairs --> + <xsd:element name="map"> + <xsd:complexType> + <xsd:sequence minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="key"> + <xsd:simpleType> + <xsd:restriction base="xsd:string" /> + </xsd:simpleType> + </xsd:element> + <xsd:group ref="llsd-value" /> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + +</xsd:schema> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index ee01ee7833..ef6f8fd3ee 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1,5 +1,6 @@ <?xml version="1.0" ?> -<llsd> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="llsd.xsd"> <map> <key>CrashHostUrl</key> <map> @@ -24,6 +25,28 @@ <key>Value</key> <real>300</real> </map> + <key>AdminMenu</key> + <map> + <key>Comment</key> + <string>Enable the debug admin menu from the main menu. Note: This will just allow the menu to be shown; this does not grant admin privileges.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>ActiveFloaterTransparency</key> + <map> + <key>Comment</key> + <string>Transparency of active floaters (floaters that have focus)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.95</real> + </map> <key>AdvanceSnapshot</key> <map> <key>Comment</key> @@ -585,6 +608,17 @@ <key>Value</key> <integer>2</integer> </map> + <key>AvatarPickerURL</key> + <map> + <key>Comment</key> + <string>Avatar picker contents</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>http://interest.secondlife.com/viewer/avatar</string> + </map> <key>AvatarBakedTextureUploadTimeout</key> <map> <key>Comment</key> @@ -652,6 +686,39 @@ <key>Value</key> <string>http://www.secondlife.com</string> </map> + <key>BrowserIgnoreSSLCertErrors</key> + <map> + <key>Comment</key> + <string>FOR TESTING ONLY: Tell the built-in web browser to ignore SSL cert errors.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>BrowserUseDefaultCAFile</key> + <map> + <key>Comment</key> + <string>Tell the built-in web browser to use the CA.pem file shipped with the client.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>BrowserCAFilePath</key> + <map> + <key>Comment</key> + <string>Tell the built-in web browser the path to an alternative CA.pem file (only used if BrowserUseDefaultCAFile is false).</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string></string> + </map> <key>BlockAvatarAppearanceMessages</key> <map> <key>Comment</key> @@ -1343,6 +1410,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>LetterKeysFocusChatBar</key> + <map> + <key>Comment</key> + <string>When printable characters keys (possibly with Shift held) are pressed, the chatbar takes focus</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>0</integer> + </map> <key>ChatBubbleOpacity</key> <map> <key>Comment</key> @@ -2523,7 +2601,18 @@ <key>Value</key> <integer>10</integer> </map> - <key>DisableCameraConstraints</key> + <key>DestinationGuideURL</key> + <map> + <key>Comment</key> + <string>Destination guide contents</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>http://www.secondlife.com</string> + </map> + <key>DisableCameraConstraints</key> <map> <key>Comment</key> <string>Disable the normal bounds put on the camera by avatar position</string> @@ -2545,6 +2634,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>DisableExternalBrowser</key> + <map> + <key>Comment</key> + <string>Disable opening an external browser.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>DisableRendering</key> <map> <key>Comment</key> @@ -2556,6 +2656,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>DisableTextHyperlinkActions</key> + <map> + <key>Comment</key> + <string>Disable highlighting and linking of URLs in XUI text boxes</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>DisableVerticalSync</key> <map> <key>Comment</key> @@ -2567,6 +2678,28 @@ <key>Value</key> <integer>1</integer> </map> + <key>EnableGroupChatPopups</key> + <map> + <key>Comment</key> + <string>Enable Incoming Group Chat Popups</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>EnableIMChatPopups</key> + <map> + <key>Comment</key> + <string>Enable Incoming IM Chat Popups</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>DisplayAvatarAgentTarget</key> <map> <key>Comment</key> @@ -2677,6 +2810,28 @@ <key>Value</key> <integer>1</integer> </map> + <key>ClickActionBuyEnabled</key> + <map> + <key>Comment</key> + <string>Enable click to buy actions in tool pie menu</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>ClickActionPayEnabled</key> + <map> + <key>Comment</key> + <string>Enable click to pay actions in tool pie menu</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>DoubleClickAutoPilot</key> <map> <key>Comment</key> @@ -2820,6 +2975,39 @@ <key>Value</key> <integer>1</integer> </map> + <key>EnableGrab</key> + <map> + <key>Comment</key> + <string>Use Ctrl+mouse to grab and manipulate objects</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>EnableAltZoom</key> + <map> + <key>Comment</key> + <string>Use Alt+mouse to look at and zoom in on objects</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>EnableMouselook</key> + <map> + <key>Comment</key> + <string>Allow first person perspective and mouse control of camera</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>EnableRippleWater</key> <map> <key>Comment</key> @@ -3513,72 +3701,6 @@ <key>Value</key> <real>0.5</real> </map> - <key>FontMonospace</key> - <map> - <key>Comment</key> - <string>Name of monospace font that definitely exists (Truetype file name)</string> - <key>Persist</key> - <integer>0</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>DejaVuSansMono.ttf</string> - </map> - <key>FontSansSerif</key> - <map> - <key>Comment</key> - <string>Name of primary sans-serif font that definitely exists (Truetype file name)</string> - <key>Persist</key> - <integer>0</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>MtBkLfRg.ttf</string> - </map> - <key>FontSansSerifBundledFallback</key> - <map> - <key>Comment</key> - <string>Name of secondary sans-serif font that definitely exists (Truetype file name)</string> - <key>Persist</key> - <integer>0</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>DejaVuSansCondensed.ttf</string> - </map> - <key>FontSansSerifBold</key> - <map> - <key>Comment</key> - <string>Name of bold font (Truetype file name)</string> - <key>Persist</key> - <integer>0</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>MtBdLfRg.ttf</string> - </map> - <key>FontSansSerifFallback</key> - <map> - <key>Comment</key> - <string>Name of sans-serif font (Truetype file name)</string> - <key>Persist</key> - <integer>0</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string /> - </map> - <key>FontSansSerifFallbackScale</key> - <map> - <key>Comment</key> - <string>Scale of fallback font relative to huge font (fraction of huge font size)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>F32</string> - <key>Value</key> - <real>1.0</real> - </map> <key>FontScreenDPI</key> <map> <key>Comment</key> @@ -3590,61 +3712,6 @@ <key>Value</key> <real>96.0</real> </map> - <key>FontSizeHuge</key> - <map> - <key>Comment</key> - <string>Size of huge font (points, or 1/72 of an inch)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>F32</string> - <key>Value</key> - <real>16.0</real> - </map> - <key>FontSizeLarge</key> - <map> - <key>Comment</key> - <string>Size of large font (points, or 1/72 of an inch)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>F32</string> - <key>Value</key> - <real>12.0</real> - </map> - <key>FontSizeMedium</key> - <map> - <key>Comment</key> - <string>Size of medium font (points, or 1/72 of an inch)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>F32</string> - <key>Value</key> - <real>10.0</real> - </map> - <key>FontSizeMonospace</key> - <map> - <key>Comment</key> - <string>Size of monospaced font (points, or 1/72 of an inch)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>F32</string> - <key>Value</key> - <real>8.1</real> - </map> - <key>FontSizeSmall</key> - <map> - <key>Comment</key> - <string>Size of small font (points, or 1/72 of an inch)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>F32</string> - <key>Value</key> - <real>9.0</real> - </map> <key>ForceAssetFail</key> <map> <key>Comment</key> @@ -3837,7 +3904,7 @@ <key>Comment</key> <string>URL pattern for help page; arguments will be encoded; see llviewerhelp.cpp:buildHelpURL for arguments</string> <key>Persist</key> - <integer>0</integer> + <integer>1</integer> <key>Type</key> <string>String</string> <key>Value</key> @@ -3865,6 +3932,17 @@ <key>Value</key> <string>http://search.secondlife.com/viewer/[CATEGORY]/?q=[QUERY]&p=[AUTH_TOKEN]&r=[MATURITY]&lang=[LANGUAGE]&g=[GODLIKE]&sid=[SESSION_ID]&rid=[REGION_ID]&pid=[PARCEL_ID]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD]</string> </map> + <key>WebProfileURL</key> + <map> + <key>Comment</key> + <string>URL for Web Profiles</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>https://my.secondlife.com/[AGENT_NAME]</string> + </map> <key>HighResSnapshot</key> <map> <key>Comment</key> @@ -3887,6 +3965,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>HostID</key> + <map> + <key>Comment</key> + <string>Machine identifier for hosted Second Life instances</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string /> + </map> <key>HtmlHelpLastPage</key> <map> <key>Comment</key> @@ -3925,7 +4014,7 @@ <key>Comment</key> <string>Ignore all notifications so we never need user input on them.</string> <key>Persist</key> - <integer>0</integer> + <integer>1</integer> <key>Type</key> <string>Boolean</string> <key>Value</key> @@ -3953,6 +4042,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>InactiveFloaterTransparency</key> + <map> + <key>Comment</key> + <string>Transparency of inactive floaters (floaters that have no focus)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.65</real> + </map> <key>InBandwidth</key> <map> <key>Comment</key> @@ -4548,6 +4648,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>LocalFileSystemBrowsingEnabled</key> + <map> + <key>Comment</key> + <string>Enable/disable access to the local file system via the file picker</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>LoginSRVTimeout</key> <map> <key>Comment</key> @@ -4581,6 +4692,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>LogTextureNetworkTraffic</key> + <map> + <key>Comment</key> + <string>Log network traffic for textures</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>LoginAsGod</key> <map> <key>Comment</key> @@ -4669,6 +4791,17 @@ <key>Value</key> <string>http://map.secondlife.com.s3.amazonaws.com/</string> </map> + <key>CurrentMapServerURL</key> + <map> + <key>Comment</key> + <string>Current Session World map URL</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string></string> + </map> <key>MapShowEvents</key> <map> <key>Comment</key> @@ -5164,61 +5297,6 @@ <key>Value</key> <real>60.0</real> </map> - <key>MeanCollisionBump</key> - <map> - <key>Comment</key> - <string>You have experienced an abuse of being bumped by an object or avatar</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>Boolean</string> - <key>Value</key> - <integer>0</integer> - </map> - <key>MeanCollisionPhysical</key> - <map> - <key>Comment</key> - <string>You have experienced an abuse from a physical object</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>Boolean</string> - <key>Value</key> - <integer>0</integer> - </map> - <key>MeanCollisionPushObject</key> - <map> - <key>Comment</key> - <string>You have experienced an abuse of being pushed by a scripted object</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>Boolean</string> - <key>Value</key> - <integer>0</integer> - </map> - <key>MeanCollisionScripted</key> - <map> - <key>Comment</key> - <string>You have experienced an abuse from a scripted object</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>Boolean</string> - <key>Value</key> - <integer>0</integer> - </map> - <key>MeanCollisionSelected</key> - <map> - <key>Comment</key> - <string>You have experienced an abuse of being pushed via a selected object</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>Boolean</string> - <key>Value</key> - <integer>0</integer> - </map> <key>MediaControlFadeTime</key> <map> <key>Comment</key> @@ -5893,6 +5971,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>ObjectCacheEnabled</key> + <map> + <key>Comment</key> + <string>Enable the object cache.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>OpenDebugStatAdvanced</key> <map> <key>Comment</key> @@ -6480,6 +6569,28 @@ <key>Value</key> <real>0.0</real> </map> + <key>QuitAfterSecondsOfAFK</key> + <map> + <key>Comment</key> + <string>The duration allowed after being AFK before quitting.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>QuitOnLoginActivated</key> + <map> + <key>Comment</key> + <string>Quit if login page is activated (used when auto login is on and users must not be able to login manually)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>RadioLandBrushAction</key> <map> <key>Comment</key> @@ -6516,7 +6627,18 @@ <key>MediaBrowserWindowLimit</key> <map> <key>Comment</key> - <string>Maximum number of media brower windows that can be open at once (0 for no limit)</string> + <string>Maximum number of media brower windows that can be open at once in the media browser floater (0 for no limit)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>5</integer> + </map> + <key>WebContentWindowLimit</key> + <map> + <key>Comment</key> + <string>Maximum number of web brower windows that can be open at once in the Web content floater (0 for no limit)</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -7737,17 +7859,6 @@ <key>Value</key> <integer>1</integer> </map> - <key>RenderFastUI</key> - <map> - <key>Comment</key> - <string>[NOT USED]</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>Boolean</string> - <key>Value</key> - <integer>0</integer> - </map> <key>RenderFlexTimeFactor</key> <map> <key>Comment</key> @@ -8263,6 +8374,28 @@ <key>Value</key> <real>1.0</real> </map> + <key>RenderTrackerBeacon</key> + <map> + <key>Comment</key> + <string>Display tracking arrow and beacon to target avatar, teleport destination</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>RenderTransparentWater</key> + <map> + <key>Comment</key> + <string>Render water as transparent. Setting to false renders water as opaque with a simple texture applied.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>RenderTreeLODFactor</key> <map> <key>Comment</key> @@ -8439,6 +8572,17 @@ <key>Value</key> <integer>512</integer> </map> + <key>RenderParcelSelection</key> + <map> + <key>Comment</key> + <string>Display selected parcel outline</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>RotateRight</key> <map> <key>Comment</key> @@ -8527,6 +8671,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>ScriptsCanShowUI</key> + <map> + <key>Comment</key> + <string>Allow LSL calls (such as LLMapDestination) to spawn viewer UI</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>SecondLifeEnterprise</key> <map> <key>Comment</key> @@ -9891,6 +10046,17 @@ <key>Value</key> <real>500.0</real> </map> + <key>UpdaterMaximumBandwidth</key> + <map> + <key>Comment</key> + <string>Maximum allowable downstream bandwidth for updater service (kilo bits per second)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>500.0</real> + </map> <key>ToolTipDelay</key> <map> <key>Comment</key> @@ -10991,7 +11157,62 @@ <key>Value</key> <integer>15</integer> </map> - <key>UploadBakedTexOld</key> + <key>UpdaterServiceSetting</key> + <map> + <key>Comment</key> + <string>Configure updater service.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>3</integer> + </map> + <key>UpdaterServiceCheckPeriod</key> + <map> + <key>Comment</key> + <string>Default period between update checking.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>3600</integer> + </map> + <key>UpdaterServiceURL</key> + <map> + <key>Comment</key> + <string>Default location for the updater service.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>https://update.secondlife.com</string> + </map> + <key>UpdaterServicePath</key> + <map> + <key>Comment</key> + <string>Path on the update server host.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>update</string> + </map> + <key>UpdaterServiceProtocolVersion</key> + <map> + <key>Comment</key> + <string>The update protocol version to use.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>v1.0</string> + </map> + <key>UploadBakedTexOld</key> <map> <key>Comment</key> <string>Forces the baked texture pipeline to upload using the old method.</string> @@ -11145,6 +11366,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>SpeakerParticipantDefaultOrder</key> + <map> + <key>Comment</key> + <string>Order for displaying speakers in voice controls. 0 = alphabetical. 1 = recent.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>1</integer> + </map> <key>SpeakerParticipantRemoveDelay</key> <map> <key>Comment</key> @@ -11300,27 +11532,38 @@ <key>Value</key> <integer>1</integer> </map> - <key>VerboseLogs</key> + <key>InterpolationTime</key> <map> <key>Comment</key> - <string>Display source file and line number for each log item for debugging purposes</string> + <string>How long to extrapolate object motion after last packet received</string> <key>Persist</key> <integer>1</integer> <key>Type</key> - <string>Boolean</string> + <string>F32</string> <key>Value</key> - <integer>0</integer> + <integer>3.0</integer> </map> - <key>VersionChannelName</key> + <key>InterpolationPhaseOut</key> <map> <key>Comment</key> - <string>Versioning Channel Name.</string> + <string>Seconds to phase out interpolated motion</string> <key>Persist</key> - <integer>0</integer> + <integer>1</integer> <key>Type</key> - <string>String</string> + <string>F32</string> + <key>Value</key> + <integer>1.0</integer> + </map> + <key>VerboseLogs</key> + <map> + <key>Comment</key> + <string>Display source file and line number for each log item for debugging purposes</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> <key>Value</key> - <string>Second Life Release</string> + <integer>0</integer> </map> <key>VertexShaderEnable</key> <map> @@ -11421,6 +11664,28 @@ <key>Value</key> <integer>0</integer> </map> + <key>VoiceCallsRejectAll</key> + <map> + <key>Comment</key> + <string>Silently reject all incoming voice calls.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>VoiceDisableMic</key> + <map> + <key>Comment</key> + <string>Completely disable the ability to open the mic.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>VoiceEffectExpiryWarningTime</key> <map> <key>Comment</key> @@ -11729,6 +11994,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>WindowFullScreen</key> + <map> + <key>Comment</key> + <string>SL viewer window full screen</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>WindowHeight</key> <map> <key>Comment</key> @@ -11795,10 +12071,10 @@ <key>Value</key> <real>150000.0</real> </map> - <key>XUIEditor</key> + <key>ExternalEditor</key> <map> <key>Comment</key> - <string>Path to program used to edit XUI files</string> + <string>Path to program used to edit LSL scripts and XUI files, e.g.: /usr/bin/gedit --new-window "%s"</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -12026,6 +12302,7 @@ <key>Comment</key> <string>Maximum texture width for user uploaded textures</string> <key>Persist</key> + <integer>1</integer> <key>Type</key> <string>S32</string> <key>Value</key> @@ -12065,6 +12342,7 @@ <key>Value</key> <array> <string>snapshot</string> + <string>postcard</string> <string>mini_map</string> </array> </map> @@ -12132,6 +12410,17 @@ <key>Type</key> <string>F32</string> <key>Value</key> + <real>1200.0</real> + </map> + <key>AvatarPickerHintTimeout</key> + <map> + <key>Comment</key> + <string>Number of seconds to wait before telling resident about avatar picker.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> <real>600.0</real> </map> <key>SidePanelHintTimeout</key> @@ -12145,5 +12434,27 @@ <key>Value</key> <real>300.0</real> </map> + <key>GroupMembersSortOrder</key> + <map> + <key>Comment</key> + <string>The order by which group members will be sorted (name|donated|online)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>name</string> + </map> + <key>ReleaseNotesURL</key> + <map> + <key>Comment</key> + <string>Release notes URL template</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>http://secondlife.com/app/releasenotes/?channel=[CHANNEL]&version=[VERSION]</string> + </map> </map> </llsd> diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index dc76a4e518..8efec1cff0 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -110,7 +110,17 @@ <key>Value</key> <string>00000000-0000-0000-0000-000000000000</string> </map> - + <key>LogFileNamewithDate</key> + <map> + <key>Comment</key> + <string>Add Date Stamp to chat and IM Logs with format chat-YYYY-MM-DD and 'IM file name'-YYYY-MM. To view old logs goto ..\Second Life\[login name]</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <!-- Settings below are for back compatibility only. They are not used in current viewer anymore. But they can't be removed to avoid influence on previous versions of the viewer in case of settings are not used or default value @@ -150,6 +160,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>ShowFavoritesOnLogin</key> + <map> + <key>Comment</key> + <string>Determines whether favorites of last logged in user will be saved on exit from viewer and shown on login screen</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <!-- End of back compatibility settings --> </map> </llsd> diff --git a/indra/newview/app_settings/shaders/shader_heirarchy.txt b/indra/newview/app_settings/shaders/shader_hierarchy.txt index d8bbf69b38..d8bbf69b38 100644 --- a/indra/newview/app_settings/shaders/shader_heirarchy.txt +++ b/indra/newview/app_settings/shaders/shader_hierarchy.txt diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index d69842d5f1..a82c3da4c5 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -42,6 +42,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 4 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderUseImpostors 1 1 RenderVBOEnable 1 1 @@ -80,6 +81,7 @@ RenderObjectBump 1 0 RenderReflectionDetail 1 0 RenderTerrainDetail 1 0 RenderTerrainLODFactor 1 1 +RenderTransparentWater 1 0 RenderTreeLODFactor 1 0 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 @@ -108,6 +110,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 @@ -132,9 +135,10 @@ RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 RenderObjectBump 1 1 -RenderReflectionDetail 1 2 +RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 @@ -162,6 +166,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 4 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 2.0 diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt index efe29005f2..a2cd4b834c 100644 --- a/indra/newview/featuretable_linux.txt +++ b/indra/newview/featuretable_linux.txt @@ -42,6 +42,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 4 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderUseImpostors 1 1 RenderVBOEnable 1 1 @@ -79,6 +80,7 @@ RenderObjectBump 1 0 RenderReflectionDetail 1 0 RenderTerrainDetail 1 0 RenderTerrainLODFactor 1 1 +RenderTransparentWater 1 0 RenderTreeLODFactor 1 0 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 @@ -107,6 +109,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 @@ -131,9 +134,10 @@ RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 RenderObjectBump 1 1 -RenderReflectionDetail 1 2 +RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 @@ -161,6 +165,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 4 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 2.0 diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index f030c9f8e5..3ad7f4e892 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -43,6 +43,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 3 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderUseImpostors 1 1 RenderVBOEnable 1 1 @@ -80,6 +81,7 @@ RenderObjectBump 1 0 RenderReflectionDetail 1 0 RenderTerrainDetail 1 0 RenderTerrainLODFactor 1 1 +RenderTransparentWater 1 0 RenderTreeLODFactor 1 0 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 @@ -107,6 +109,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 @@ -130,9 +133,10 @@ RenderGlowResolutionPow 1 9 RenderLightingDetail 1 1 RenderMaxPartCount 1 4096 RenderObjectBump 1 1 -RenderReflectionDetail 1 2 +RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 @@ -159,6 +163,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 3 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 2.0 diff --git a/indra/newview/featuretable_xp.txt b/indra/newview/featuretable_xp.txt index dae7705971..38e6bb1e5e 100644 --- a/indra/newview/featuretable_xp.txt +++ b/indra/newview/featuretable_xp.txt @@ -42,6 +42,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 4 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderUseImpostors 1 1 RenderVBOEnable 1 1 @@ -80,6 +81,7 @@ RenderObjectBump 1 0 RenderReflectionDetail 1 0 RenderTerrainDetail 1 0 RenderTerrainLODFactor 1 1 +RenderTransparentWater 1 0 RenderTreeLODFactor 1 0 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 0.5 @@ -108,6 +110,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 @@ -132,9 +135,10 @@ RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 RenderObjectBump 1 1 -RenderReflectionDetail 1 2 +RenderReflectionDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 1.125 @@ -162,6 +166,7 @@ RenderObjectBump 1 1 RenderReflectionDetail 1 4 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderUseImpostors 1 1 RenderVolumeLODFactor 1 2.0 diff --git a/indra/newview/generate_breakpad_symbols.py b/indra/newview/generate_breakpad_symbols.py index 8f2dfd2348..4fd04d780e 100644 --- a/indra/newview/generate_breakpad_symbols.py +++ b/indra/newview/generate_breakpad_symbols.py @@ -31,6 +31,7 @@ import fnmatch import itertools import operator import os +import re import sys import shlex import subprocess @@ -45,8 +46,12 @@ class MissingModuleError(Exception): Exception.__init__(self, "Failed to find required modules: %r" % modules) self.modules = modules -def main(viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file): - print "generate_breakpad_symbols run with args: %s" % str((viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file)) +def main(configuration, viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file): + print "generate_breakpad_symbols run with args: %s" % str((configuration, viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file)) + + if not re.match("release", configuration, re.IGNORECASE): + print "skipping breakpad symbol generation for non-release build." + return 0 # split up list of viewer_exes # "'Second Life' SLPlugin" becomes ['Second Life', 'SLPlugin'] @@ -122,7 +127,7 @@ def main(viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_fil return 0 if __name__ == "__main__": - if len(sys.argv) != 6: + if len(sys.argv) != 7: usage() sys.exit(1) sys.exit(main(*sys.argv[1:])) diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index d5712f80cf..4e8ed807ee 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -85,6 +85,8 @@ AutoCloseWindow true ; after all files install, close window InstallDir "$PROGRAMFILES\${INSTNAME}" InstallDirRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "" DirText $(DirectoryChooseTitle) $(DirectoryChooseSetup) +Page directory dirPre +Page instfiles ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Variables @@ -95,6 +97,8 @@ Var INSTFLAGS Var INSTSHORTCUT Var COMMANDLINE ; command line passed to this installer, set in .onInit Var SHORTCUT_LANG_PARAM ; "--set InstallLanguage de", passes language to viewer +Var SKIP_DIALOGS ; set from command line in .onInit. autoinstall + ; GUI and the defaults. ;;; Function definitions should go before file includes, because calls to ;;; DLLs like LangDLL trigger an implicit file include, so if that call is at @@ -110,6 +114,9 @@ Var SHORTCUT_LANG_PARAM ; "--set InstallLanguage de", passes language to viewer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Function .onInstSuccess Push $R0 # Option value, unused + + StrCmp $SKIP_DIALOGS "true" label_launch + ${GetOptions} $COMMANDLINE "/AUTOSTART" $R0 # If parameter was there (no error) just launch # Otherwise ask @@ -128,6 +135,13 @@ label_no_launch: Pop $R0 FunctionEnd +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Pre-directory page callback +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function dirPre + StrCmp $SKIP_DIALOGS "true" 0 +2 + Abort +FunctionEnd ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Make sure we're not on Windows 98 / ME @@ -145,7 +159,8 @@ Function CheckWindowsVersion StrCmp $R0 "NT" win_ver_bad Return win_ver_bad: - MessageBox MB_YESNO $(CheckWindowsVersionMB) IDNO win_ver_abort + StrCmp $SKIP_DIALOGS "true" +2 ; If skip_dialogs is set just install + MessageBox MB_YESNO $(CheckWindowsVersionMB) IDNO win_ver_abort Return win_ver_abort: Quit @@ -184,13 +199,13 @@ FunctionEnd ; If it has, allow user to bail out of install process. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Function CheckIfAlreadyCurrent - Push $0 - ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Version" - StrCmp $0 ${VERSION_LONG} 0 DONE - MessageBox MB_OKCANCEL $(CheckIfCurrentMB) /SD IDOK IDOK DONE + Push $0 + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Version" + StrCmp $0 ${VERSION_LONG} 0 continue_install + StrCmp $SKIP_DIALOGS "true" continue_install + MessageBox MB_OKCANCEL $(CheckIfCurrentMB) /SD IDOK IDOK continue_install Quit - - DONE: +continue_install: Pop $0 Return FunctionEnd @@ -203,7 +218,9 @@ Function CloseSecondLife Push $0 FindWindow $0 "Second Life" "" IntCmp $0 0 DONE - MessageBox MB_OKCANCEL $(CloseSecondLifeInstMB) IDOK CLOSE IDCANCEL CANCEL_INSTALL + + StrCmp $SKIP_DIALOGS "true" CLOSE + MessageBox MB_OKCANCEL $(CloseSecondLifeInstMB) IDOK CLOSE IDCANCEL CANCEL_INSTALL CANCEL_INSTALL: Quit @@ -659,23 +676,29 @@ FunctionEnd Function .onInit Push $0 ${GetParameters} $COMMANDLINE ; get our command line + + ${GetOptions} $COMMANDLINE "/SKIP_DIALOGS" $0 + IfErrors +2 0 ; If error jump past setting SKIP_DIALOGS + StrCpy $SKIP_DIALOGS "true" + ${GetOptions} $COMMANDLINE "/LANGID=" $0 ; /LANGID=1033 implies US English ; If no language (error), then proceed - IfErrors lbl_check_silent + IfErrors lbl_configure_default_lang ; No error means we got a language, so use it StrCpy $LANGUAGE $0 Goto lbl_return -lbl_check_silent: - ; For silent installs, no language prompt, use default - IfSilent lbl_return - - ; If we currently have a version of SL installed, default to the language of that install +lbl_configure_default_lang: + ; If we currently have a version of SL installed, default to the language of that install ; Otherwise don't change $LANGUAGE and it will default to the OS UI language. - ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "InstallerLanguage" - IfErrors lbl_build_menu + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "InstallerLanguage" + IfErrors +2 0 ; If error skip the copy instruction StrCpy $LANGUAGE $0 + ; For silent installs, no language prompt, use default + IfSilent lbl_return + StrCmp $SKIP_DIALOGS "true" lbl_return + lbl_build_menu: Push "" # Use separate file so labels can be UTF-16 but we can still merge changes diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index c9bd7851ed..77552663ab 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -56,9 +56,9 @@ #include "llparcel.h" #include "llrendersphere.h" #include "llsdutil.h" -#include "llsidetray.h" #include "llsky.h" #include "llsmoothstep.h" +#include "llstartup.h" #include "llstatusbar.h" #include "llteleportflags.h" #include "lltool.h" @@ -218,7 +218,10 @@ LLAgent::LLAgent() : mFirstLogin(FALSE), mGenderChosen(FALSE), - mAppearanceSerialNum(0) + mAppearanceSerialNum(0), + + mMouselookModeInSignal(NULL), + mMouselookModeOutSignal(NULL) { for (U32 i = 0; i < TOTAL_CONTROLS; i++) { @@ -269,6 +272,9 @@ LLAgent::~LLAgent() { cleanup(); + delete mMouselookModeInSignal; + delete mMouselookModeOutSignal; + // *Note: this is where LLViewerCamera::getInstance() used to be deleted. } @@ -637,6 +643,9 @@ void LLAgent::setRegion(LLViewerRegion *regionp) // Update all of the regions. LLWorld::getInstance()->updateAgentOffset(mAgentOriginGlobal); } + + // Pass new region along to metrics components that care about this level of detail. + LLAppViewer::metricsUpdateRegion(regionp->getHandle()); } mRegionp = regionp; @@ -1726,15 +1735,17 @@ void LLAgent::endAnimationUpdateUI() LLBottomTray::getInstance()->onMouselookModeOut(); - LLSideTray::getInstance()->getButtonsPanel()->setVisible(TRUE); - LLSideTray::getInstance()->updateSidetrayVisibility(); - LLPanelStandStopFlying::getInstance()->setVisible(TRUE); LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); LLFloaterCamera::onLeavingMouseLook(); + if (mMouselookModeOutSignal) + { + (*mMouselookModeOutSignal)(); + } + // Only pop if we have pushed... if (TRUE == mViewsPushed) { @@ -1828,9 +1839,6 @@ void LLAgent::endAnimationUpdateUI() LLBottomTray::getInstance()->onMouselookModeIn(); - LLSideTray::getInstance()->getButtonsPanel()->setVisible(FALSE); - LLSideTray::getInstance()->updateSidetrayVisibility(); - LLPanelStandStopFlying::getInstance()->setVisible(FALSE); // clear out camera lag effect @@ -1843,6 +1851,11 @@ void LLAgent::endAnimationUpdateUI() mViewsPushed = TRUE; + if (mMouselookModeInSignal) + { + (*mMouselookModeInSignal)(); + } + // hide all floaters except the mini map #if 0 // Use this once all floaters are registered @@ -1902,7 +1915,6 @@ void LLAgent::endAnimationUpdateUI() } } } - } else if (gAgentCamera.getCameraMode() == CAMERA_MODE_CUSTOMIZE_AVATAR) { @@ -1934,6 +1946,18 @@ void LLAgent::endAnimationUpdateUI() gAgentCamera.updateLastCamera(); } +boost::signals2::connection LLAgent::setMouselookModeInCallback( const camera_signal_t::slot_type& cb ) +{ + if (!mMouselookModeInSignal) mMouselookModeInSignal = new camera_signal_t(); + return mMouselookModeInSignal->connect(cb); +} + +boost::signals2::connection LLAgent::setMouselookModeOutCallback( const camera_signal_t::slot_type& cb ) +{ + if (!mMouselookModeOutSignal) mMouselookModeOutSignal = new camera_signal_t(); + return mMouselookModeOutSignal->connect(cb); +} + //----------------------------------------------------------------------------- // heardChat() //----------------------------------------------------------------------------- @@ -2452,7 +2476,7 @@ BOOL LLAgent::setUserGroupFlags(const LLUUID& group_id, BOOL accept_notices, BOO BOOL LLAgent::canJoinGroups() const { - return mGroups.count() < MAX_AGENT_GROUPS; + return mGroups.count() < gMaxAgentGroups; } LLQuaternion LLAgent::getHeadRotation() diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 6c598d5d71..896408c0dd 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -33,11 +33,14 @@ #include "llagentconstants.h" #include "llagentdata.h" // gAgentID, gAgentSessionID #include "llcharacter.h" // LLAnimPauseRequest +#include "llcoordframe.h" // for mFrameAgent #include "llpointer.h" #include "lluicolor.h" #include "llvoavatardefines.h" #include "llslurl.h" +#include <boost/signals2.hpp> + extern const BOOL ANIMATE; extern const U8 AGENT_STATE_TYPING; // Typing indication extern const U8 AGENT_STATE_EDITING; // Set when agent has objects selected @@ -409,7 +412,13 @@ public: BOOL getCustomAnim() const { return mCustomAnim; } void setCustomAnim(BOOL anim) { mCustomAnim = anim; } + typedef boost::signals2::signal<void ()> camera_signal_t; + boost::signals2::connection setMouselookModeInCallback( const camera_signal_t::slot_type& cb ); + boost::signals2::connection setMouselookModeOutCallback( const camera_signal_t::slot_type& cb ); + private: + camera_signal_t* mMouselookModeInSignal; + camera_signal_t* mMouselookModeOutSignal; BOOL mCustomAnim; // Current animation is ANIM_AGENT_CUSTOMIZE ? LLAnimPauseRequest mPauseRequest; BOOL mViewsPushed; // Keep track of whether or not we have pushed views diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index 68e408d3e4..f01d5ff1f5 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -296,8 +296,11 @@ void LLAgentCamera::resetView(BOOL reset_camera, BOOL change_camera) LLSelectMgr::getInstance()->deselectAll(); } - // Hide all popup menus - gMenuHolder->hideMenus(); + if (gMenuHolder != NULL) + { + // Hide all popup menus + gMenuHolder->hideMenus(); + } } if (change_camera && !gSavedSettings.getBOOL("FreezeTime")) @@ -2038,7 +2041,7 @@ void LLAgentCamera::resetCamera() //----------------------------------------------------------------------------- void LLAgentCamera::changeCameraToMouselook(BOOL animate) { - if (LLViewerJoystick::getInstance()->getOverrideCamera()) + if (!gSavedSettings.getBOOL("EnableMouselook") || LLViewerJoystick::getInstance()->getOverrideCamera()) { return; } @@ -2692,6 +2695,9 @@ void LLAgentCamera::lookAtLastChat() new_camera_pos -= delta_pos * 0.4f; new_camera_pos += left * 0.3f; new_camera_pos += up * 0.2f; + + setFocusOnAvatar(FALSE, FALSE); + if (chatter_av->mHeadp) { setFocusGlobal(gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()), gAgent.getLastChatter()); @@ -2702,7 +2708,6 @@ void LLAgentCamera::lookAtLastChat() setFocusGlobal(chatter->getPositionGlobal(), gAgent.getLastChatter()); mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal(); } - setFocusOnAvatar(FALSE, TRUE); } else { @@ -2722,9 +2727,10 @@ void LLAgentCamera::lookAtLastChat() new_camera_pos += left * 0.3f; new_camera_pos += up * 0.2f; + setFocusOnAvatar(FALSE, FALSE); + setFocusGlobal(chatter->getPositionGlobal(), gAgent.getLastChatter()); mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal(); - setFocusOnAvatar(FALSE, TRUE); } } diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index ed5e8ceee3..80734b0d41 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2204,12 +2204,11 @@ void LLAppearanceMgr::updateIsDirty() base_outfit = catp->getUUID(); } - if(base_outfit.isNull()) - { - // no outfit link found, display "unsaved outfit" - mOutfitIsDirty = true; - } - else + // Set dirty to "false" if no base outfit found to disable "Save" + // and leave only "Save As" enabled in My Outfits. + mOutfitIsDirty = false; + + if (base_outfit.notNull()) { LLIsOfAssetType collector = LLIsOfAssetType(LLAssetType::AT_LINK); @@ -2248,8 +2247,6 @@ void LLAppearanceMgr::updateIsDirty() return; } } - - mOutfitIsDirty = false; } } @@ -2440,6 +2437,12 @@ public: virtual ~LLShowCreatedOutfit() { + if (!LLApp::isRunning()) + { + llwarns << "called during shutdown, skipping" << llendl; + return; + } + LLSD key; //EXT-7727. For new accounts LLShowCreatedOutfit is created during login process @@ -2635,6 +2638,7 @@ void LLAppearanceMgr::dumpItemArray(const LLInventoryModel::item_array_t& items, LLAppearanceMgr::LLAppearanceMgr(): mAttachmentInvLinkEnabled(false), mOutfitIsDirty(false), + mOutfitLocked(false), mIsInUpdateAppearanceFromCOF(false) { LLOutfitObserver& outfit_observer = LLOutfitObserver::instance(); @@ -2943,3 +2947,32 @@ void wear_multiple(const uuid_vec_t& ids, bool replace) } } +// SLapp for easy-wearing of a stock (library) avatar +// +class LLWearFolderHandler : public LLCommandHandler +{ +public: + // not allowed from outside the app + LLWearFolderHandler() : LLCommandHandler("wear_folder", UNTRUSTED_BLOCK) { } + + bool handle(const LLSD& tokens, const LLSD& query_map, + LLMediaCtrl* web) + { + LLPointer<LLInventoryCategory> category = new LLInventoryCategory(query_map["folder_id"], + LLUUID::null, + LLFolderType::FT_CLOTHING, + "Quick Appearance"); + LLSD::UUID folder_uuid = query_map["folder_id"].asUUID(); + if ( gInventory.getCategory( folder_uuid ) != NULL ) + { + LLAppearanceMgr::getInstance()->wearInventoryCategory(category, true, false); + + // *TODOw: This may not be necessary if initial outfit is chosen already -- josh + gAgent.setGenderChosen(TRUE); + } + + return true; + } +}; + +LLWearFolderHandler gWearFolderHandler; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ba14c248aa..e92042bcd4 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -30,6 +30,7 @@ // Viewer includes #include "llversioninfo.h" +#include "llversionviewer.h" #include "llfeaturemanager.h" #include "lluictrlfactory.h" #include "lltexteditor.h" @@ -79,9 +80,12 @@ #include "llfeaturemanager.h" #include "llurlmatch.h" #include "lltextutil.h" +#include "lllogininstance.h" +#include "llprogressview.h" #include "llweb.h" #include "llsecondlifeurls.h" +#include "llupdaterservice.h" // Linden library includes #include "llavatarnamecache.h" @@ -190,11 +194,14 @@ #include "llparcel.h" #include "llavatariconctrl.h" #include "llgroupiconctrl.h" +#include "llviewerassetstats.h" // Include for security api initialization #include "llsecapi.h" #include "llmachineid.h" +#include "llmainlooprepeater.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -332,6 +339,14 @@ static std::string gWindowTitle; LLAppViewer::LLUpdaterInfo *LLAppViewer::sUpdaterInfo = NULL ; +//---------------------------------------------------------------------------- +// Metrics logging control constants +//---------------------------------------------------------------------------- +static const F32 METRICS_INTERVAL_DEFAULT = 600.0; +static const F32 METRICS_INTERVAL_QA = 30.0; +static F32 app_metrics_interval = METRICS_INTERVAL_DEFAULT; +static bool app_metrics_qa_mode = false; + void idle_afk_check() { // check idle timers @@ -501,6 +516,9 @@ static void settings_modify() gSavedSettings.setBOOL("VectorizeEnable", FALSE ); gSavedSettings.setU32("VectorizeProcessor", 0 ); gSavedSettings.setBOOL("VectorizeSkin", FALSE); + + // disable fullscreen mode, unsupported + gSavedSettings.setBOOL("WindowFullScreen", FALSE); #endif } @@ -509,16 +527,10 @@ class LLFastTimerLogThread : public LLThread public: std::string mFile; - LLFastTimerLogThread() : LLThread("fast timer log") + LLFastTimerLogThread(std::string& test_name) : LLThread("fast timer log") { - if(LLFastTimer::sLog) - { - mFile = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance.slp"); - } - if(LLFastTimer::sMetricLog) - { - mFile = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric.slp"); - } + std::string file_name = test_name + std::string(".slp"); + mFile = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name); } void run() @@ -534,6 +546,7 @@ public: os.close(); } + }; //virtual @@ -580,7 +593,8 @@ LLAppViewer::LLAppViewer() : mAgentRegionLastAlive(false), mRandomizeFramerate(LLCachedControl<bool>(gSavedSettings,"Randomize Framerate", FALSE)), mPeriodicSlowFrame(LLCachedControl<bool>(gSavedSettings,"Periodic Slow Frame", FALSE)), - mFastTimerLogThread(NULL) + mFastTimerLogThread(NULL), + mUpdater(new LLUpdaterService()) { if(NULL != sInstance) { @@ -590,10 +604,14 @@ LLAppViewer::LLAppViewer() : setupErrorHandling(); sInstance = this; gLoggedInTime.stop(); + + LLLoginInstance::instance().setUpdaterService(mUpdater.get()); } LLAppViewer::~LLAppViewer() { + LLLoginInstance::instance().setUpdaterService(0); + destroyMainloopTimeout(); // If we got to this destructor somehow, the app didn't hang. @@ -652,14 +670,32 @@ bool LLAppViewer::init() // Called before threads are created. LLCurl::initClass(); LLMachineID::init(); + + { + // Viewer metrics initialization + static LLCachedControl<bool> metrics_submode(gSavedSettings, + "QAModeMetrics", + false, + "Enables QA features (logging, faster cycling) for metrics collector"); + + if (metrics_submode) + { + app_metrics_qa_mode = true; + app_metrics_interval = METRICS_INTERVAL_QA; + } + LLViewerAssetStatsFF::init(); + } initThreads(); writeSystemInfo(); - // Build a string representing the current version number. - gCurrentVersion = llformat("%s %s", - gSavedSettings.getString("VersionChannelName").c_str(), - LLVersionInfo::getVersion().c_str()); + // Initialize updater service (now that we have an io pump) + initUpdater(); + if(isQuitting()) + { + // Early out here because updater set the quitting flag. + return true; + } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -803,6 +839,9 @@ bool LLAppViewer::init() return 1; } + // Initialize the repeater service. + LLMainLoopRepeater::instance().start(); + // // Initialize the window // @@ -817,16 +856,22 @@ bool LLAppViewer::init() gGLManager.getGLInfo(gDebugInfo); gGLManager.printGLInfoString(); - //load key settings - bind_keyboard_functions(); - // Load Default bindings - if (!gViewerKeyboard.loadBindings(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keys.ini"))) + std::string key_bindings_file = gDirUtilp->findFile("keys.xml", + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + + + if (!gViewerKeyboard.loadBindingsXML(key_bindings_file)) { - LL_ERRS("InitInfo") << "Unable to open keys.ini" << LL_ENDL; + std::string key_bindings_file = gDirUtilp->findFile("keys.ini", + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + if (!gViewerKeyboard.loadBindings(key_bindings_file)) + { + LL_ERRS("InitInfo") << "Unable to open keys.ini" << LL_ENDL; + } } - // Load Custom bindings (override defaults) - gViewerKeyboard.loadBindings(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"custom_keys.ini")); // If we don't have the right GL requirements, exit. if (!gGLManager.mHasRequirements && !gNoRender) @@ -899,7 +944,8 @@ bool LLAppViewer::init() gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString(); // Save the current version to the prefs file - gSavedSettings.setString("LastRunVersion", gCurrentVersion); + gSavedSettings.setString("LastRunVersion", + LLVersionInfo::getChannelAndVersion()); gSimLastTime = gRenderStartTime.getElapsedTimeF32(); gSimFrames = (F32)gFrameCount; @@ -974,13 +1020,14 @@ bool LLAppViewer::mainLoop() gServicePump = new LLPumpIO(gAPRPoolp); LLHTTPClient::setPump(*gServicePump); LLCurl::setCAFile(gDirUtilp->getCAFile()); - + // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. LLVoiceChannel::initClass(); LLVoiceClient::getInstance()->init(gServicePump); LLTimer frameTimer,idleTimer; LLTimer debugTime; + LLFrameTimer memCheckTimer; LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); joystick->setNeedsReset(true); @@ -991,11 +1038,29 @@ bool LLAppViewer::mainLoop() // point of posting. LLSD newFrame; + const F32 memory_check_interval = 1.0f ; //second + // Handle messages while (!LLApp::isExiting()) { LLFastTimer::nextFrame(); // Should be outside of any timer instances + //clear call stack records + llclearcallstacks; + + //check memory availability information + { + if(memory_check_interval < memCheckTimer.getElapsedTimeF32()) + { + memCheckTimer.reset() ; + + //update the availability of memory + LLMemoryInfo::getAvailableMemoryKB(mAvailPhysicalMemInKB, mAvailVirtualMemInKB) ; + } + llcallstacks << "Available physical mem(KB): " << mAvailPhysicalMemInKB << llcallstacksendl ; + llcallstacks << "Available virtual mem(KB): " << mAvailVirtualMemInKB << llcallstacksendl ; + } + try { pingMainloopTimeout("Main:MiscNativeWindowEvents"); @@ -1222,11 +1287,20 @@ bool LLAppViewer::mainLoop() resumeMainloopTimeout(); pingMainloopTimeout("Main:End"); - } - + } } catch(std::bad_alloc) { + { + llinfos << "Availabe physical memory(KB) at the beginning of the frame: " << mAvailPhysicalMemInKB << llendl ; + llinfos << "Availabe virtual memory(KB) at the beginning of the frame: " << mAvailVirtualMemInKB << llendl ; + + LLMemoryInfo::getAvailableMemoryKB(mAvailPhysicalMemInKB, mAvailVirtualMemInKB) ; + + llinfos << "Current availabe physical memory(KB): " << mAvailPhysicalMemInKB << llendl ; + llinfos << "Current availabe virtual memory(KB): " << mAvailVirtualMemInKB << llendl ; + } + //stop memory leaking simulation LLFloaterMemLeak* mem_leak_instance = LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); @@ -1275,6 +1349,21 @@ bool LLAppViewer::mainLoop() return true; } +void LLAppViewer::flushVFSIO() +{ + while (1) + { + S32 pending = LLVFSThread::updateClass(0); + pending += LLLFSThread::updateClass(0); + if (!pending) + { + break; + } + llinfos << "Waiting for pending IO to finish: " << pending << llendflush; + ms_sleep(100); + } +} + bool LLAppViewer::cleanup() { // workaround for DEV-35406 crash on shutdown @@ -1329,11 +1418,14 @@ bool LLAppViewer::cleanup() llinfos << "Cleaning Up" << llendflush; // Must clean up texture references before viewer window is destroyed. - LLHUDManager::getInstance()->updateEffects(); - LLHUDObject::updateAll(); - LLHUDManager::getInstance()->cleanupEffects(); - LLHUDObject::cleanupHUDObjects(); - llinfos << "HUD Objects cleaned up" << llendflush; + if(LLHUDManager::instanceExists()) + { + LLHUDManager::getInstance()->updateEffects(); + LLHUDObject::updateAll(); + LLHUDManager::getInstance()->cleanupEffects(); + LLHUDObject::cleanupHUDObjects(); + llinfos << "HUD Objects cleaned up" << llendflush; + } LLKeyframeDataCache::clear(); @@ -1345,8 +1437,10 @@ bool LLAppViewer::cleanup() // Note: this is where gWorldMap used to be deleted. // Note: this is where gHUDManager used to be deleted. - LLHUDManager::getInstance()->shutdownClass(); - + if(LLHUDManager::instanceExists()) + { + LLHUDManager::getInstance()->shutdownClass(); + } delete gAssetStorage; gAssetStorage = NULL; @@ -1409,17 +1503,7 @@ bool LLAppViewer::cleanup() llinfos << "Cache files removed" << llendflush; // Wait for any pending VFS IO - while (1) - { - S32 pending = LLVFSThread::updateClass(0); - pending += LLLFSThread::updateClass(0); - if (!pending) - { - break; - } - llinfos << "Waiting for pending IO to finish: " << pending << llendflush; - ms_sleep(100); - } + flushVFSIO(); llinfos << "Shutting down Views" << llendflush; // Destroy the UI @@ -1614,22 +1698,16 @@ bool LLAppViewer::cleanup() { llinfos << "Analyzing performance" << llendl; - if(LLFastTimer::sLog) - { - LLFastTimerView::doAnalysis( - gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance_baseline.slp"), - gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance.slp"), - gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance_report.csv")); - } - if(LLFastTimer::sMetricLog) - { - LLFastTimerView::doAnalysis( - gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric_baseline.slp"), - gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric.slp"), - gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric_report.csv")); - } + std::string baseline_name = LLFastTimer::sLogName + "_baseline.slp"; + std::string current_name = LLFastTimer::sLogName + ".slp"; + std::string report_name = LLFastTimer::sLogName + "_report.csv"; + + LLFastTimerView::doAnalysis( + gDirUtilp->getExpandedFilename(LL_PATH_LOGS, baseline_name), + gDirUtilp->getExpandedFilename(LL_PATH_LOGS, current_name), + gDirUtilp->getExpandedFilename(LL_PATH_LOGS, report_name)); } - LLMetricPerformanceTester::cleanClass() ; + LLMetricPerformanceTesterBasic::cleanClass() ; llinfos << "Cleaning up Media and Textures" << llendflush; @@ -1648,7 +1726,10 @@ bool LLAppViewer::cleanup() #ifndef LL_RELEASE_FOR_DOWNLOAD llinfos << "Auditing VFS" << llendl; - gVFS->audit(); + if(gVFS) + { + gVFS->audit(); + } #endif llinfos << "Misc Cleanup" << llendflush; @@ -1666,6 +1747,8 @@ bool LLAppViewer::cleanup() LLWatchdog::getInstance()->cleanup(); + LLViewerAssetStatsFF::cleanup(); + llinfos << "Shutting down message system" << llendflush; end_messaging_system(); @@ -1689,6 +1772,8 @@ bool LLAppViewer::cleanup() llinfos << "File launched." << llendflush; } + LLMainLoopRepeater::instance().stop(); + ll_close_fail_log(); llinfos << "Goodbye!" << llendflush; @@ -1730,13 +1815,16 @@ bool LLAppViewer::initThreads() // Image decoding LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true); LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true); - LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), sImageDecodeThread, enable_threads && true); + LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), + sImageDecodeThread, + enable_threads && true, + app_metrics_qa_mode); LLImage::initClass(); if (LLFastTimer::sLog || LLFastTimer::sMetricLog) { LLFastTimer::sLogLock = new LLMutex(NULL); - mFastTimerLogThread = new LLFastTimerLogThread(); + mFastTimerLogThread = new LLFastTimerLogThread(LLFastTimer::sLogName); mFastTimerLogThread->start(); } @@ -1935,8 +2023,6 @@ bool LLAppViewer::initConfiguration() gSavedSettings.setString("ClientSettingsFile", gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global"))); - gSavedSettings.setString("VersionChannelName", LLVersionInfo::getChannel()); - #ifndef LL_RELEASE_FOR_DOWNLOAD // provide developer build only overrides for these control variables that are not // persisted to settings.xml @@ -2019,6 +2105,15 @@ bool LLAppViewer::initConfiguration() // - apply command line settings clp.notify(); + // Register the core crash option as soon as we can + // if we want gdb post-mortem on cores we need to be up and running + // ASAP or we might miss init issue etc. + if(clp.hasOption("disablecrashlogger")) + { + llwarns << "Crashes will be handled by system, stack trace logs and crash logger are both disabled" << llendl; + LLAppViewer::instance()->disableCrashlogger(); + } + // Handle initialization from settings. // Start up the debugging console before handling other options. if (gSavedSettings.getBOOL("ShowConsoleWindow")) @@ -2068,6 +2163,11 @@ bool LLAppViewer::initConfiguration() } } + if(clp.hasOption("channel")) + { + LLVersionInfo::resetChannel(clp.getOption("channel")[0]); + } + // If we have specified crash on startup, set the global so we'll trigger the crash at the right time if(clp.hasOption("crashonstartup")) @@ -2078,11 +2178,25 @@ bool LLAppViewer::initConfiguration() if (clp.hasOption("logperformance")) { LLFastTimer::sLog = TRUE; + LLFastTimer::sLogName = std::string("performance"); } - if(clp.hasOption("logmetrics")) + if (clp.hasOption("logmetrics")) { LLFastTimer::sMetricLog = TRUE ; + // '--logmetrics' can be specified with a named test metric argument so the data gathering is done only on that test + // In the absence of argument, every metric is gathered (makes for a rather slow run and hard to decipher report...) + std::string test_name = clp.getOption("logmetrics")[0]; + llinfos << "'--logmetrics' argument : " << test_name << llendl; + if (test_name == "") + { + llwarns << "No '--logmetrics' argument given, will output all metrics to " << DEFAULT_METRIC_NAME << llendl; + LLFastTimer::sLogName = DEFAULT_METRIC_NAME; + } + else + { + LLFastTimer::sLogName = test_name; + } } if (clp.hasOption("graphicslevel")) @@ -2131,7 +2245,7 @@ bool LLAppViewer::initConfiguration() if (clp.hasOption("nonotifications")) { - gSavedSettings.setBOOL("IgnoreAllNotifications", TRUE); + gSavedSettings.getControl("IgnoreAllNotifications")->setValue(true, false); } if (clp.hasOption("debugsession")) @@ -2178,8 +2292,8 @@ bool LLAppViewer::initConfiguration() if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) { // hack to force the skin to default. - //gDirUtilp->setSkinFolder(skinfolder->getValue().asString()); - gDirUtilp->setSkinFolder("default"); + gDirUtilp->setSkinFolder(skinfolder->getValue().asString()); + //gDirUtilp->setSkinFolder("default"); } mYieldTime = gSavedSettings.getS32("YieldTime"); @@ -2323,6 +2437,155 @@ bool LLAppViewer::initConfiguration() return true; // Config was successful. } +namespace { + // *TODO - decide if there's a better place for these functions. + // do we need a file llupdaterui.cpp or something? -brad + + void apply_update_callback(LLSD const & notification, LLSD const & response) + { + lldebugs << "LLUpdate user response: " << response << llendl; + if(response["OK_okcancelbuttons"].asBoolean()) + { + llinfos << "LLUpdate restarting viewer" << llendl; + static const bool install_if_ready = true; + // *HACK - this lets us launch the installer immediately for now + LLUpdaterService().startChecking(install_if_ready); + } + } + + void apply_update_ok_callback(LLSD const & notification, LLSD const & response) + { + llinfos << "LLUpdate restarting viewer" << llendl; + static const bool install_if_ready = true; + // *HACK - this lets us launch the installer immediately for now + LLUpdaterService().startChecking(install_if_ready); + } + + void on_update_downloaded(LLSD const & data) + { + std::string notification_name; + void (*apply_callback)(LLSD const &, LLSD const &) = NULL; + + if(data["required"].asBoolean()) + { + apply_callback = &apply_update_ok_callback; + if(LLStartUp::getStartupState() <= STATE_LOGIN_WAIT) + { + // The user never saw the progress bar. + notification_name = "RequiredUpdateDownloadedVerboseDialog"; + } + else + { + notification_name = "RequiredUpdateDownloadedDialog"; + } + } + else + { + apply_callback = &apply_update_callback; + if(LLStartUp::getStartupState() < STATE_STARTED) + { + // CHOP-262 we need to use a different notification + // method prior to login. + notification_name = "DownloadBackgroundDialog"; + } + else + { + notification_name = "DownloadBackgroundTip"; + } + } + + LLSD substitutions; + substitutions["VERSION"] = data["version"]; + + // truncate version at the rightmost '.' + std::string version_short(data["version"]); + size_t short_length = version_short.rfind('.'); + if (short_length != std::string::npos) + { + version_short.resize(short_length); + } + + LLUIString relnotes_url("[RELEASE_NOTES_BASE_URL][CHANNEL_URL]/[VERSION_SHORT]"); + relnotes_url.setArg("[VERSION_SHORT]", version_short); + + // *TODO thread the update service's response through to this point + std::string const & channel = LLVersionInfo::getChannel(); + boost::shared_ptr<char> channel_escaped(curl_escape(channel.c_str(), channel.size()), &curl_free); + + relnotes_url.setArg("[CHANNEL_URL]", channel_escaped.get()); + relnotes_url.setArg("[RELEASE_NOTES_BASE_URL]", LLTrans::getString("RELEASE_NOTES_BASE_URL")); + substitutions["RELEASE_NOTES_FULL_URL"] = relnotes_url.getString(); + + LLNotificationsUtil::add(notification_name, substitutions, LLSD(), apply_callback); + } + + void install_error_callback(LLSD const & notification, LLSD const & response) + { + LLAppViewer::instance()->forceQuit(); + } + + bool notify_update(LLSD const & evt) + { + std::string notification_name; + switch (evt["type"].asInteger()) + { + case LLUpdaterService::DOWNLOAD_COMPLETE: + on_update_downloaded(evt); + break; + case LLUpdaterService::INSTALL_ERROR: + if(evt["required"].asBoolean()) { + LLNotificationsUtil::add("FailedRequiredUpdateInstall", LLSD(), LLSD(), &install_error_callback); + } else { + LLNotificationsUtil::add("FailedUpdateInstall"); + } + break; + default: + break; + } + + // let others also handle this event by default + return false; + } + + bool on_bandwidth_throttle(LLUpdaterService * updater, LLSD const & evt) + { + updater->setBandwidthLimit(evt.asInteger() * (1024/8)); + return false; // Let others receive this event. + }; +}; + +void LLAppViewer::initUpdater() +{ + // Initialize the updater service. + // Generate URL to the udpater service + // Get Channel + // Get Version + std::string url = gSavedSettings.getString("UpdaterServiceURL"); + std::string channel = LLVersionInfo::getChannel(); + std::string version = LLVersionInfo::getVersion(); + std::string protocol_version = gSavedSettings.getString("UpdaterServiceProtocolVersion"); + std::string service_path = gSavedSettings.getString("UpdaterServicePath"); + U32 check_period = gSavedSettings.getU32("UpdaterServiceCheckPeriod"); + + mUpdater->setAppExitCallback(boost::bind(&LLAppViewer::forceQuit, this)); + mUpdater->initialize(protocol_version, + url, + service_path, + channel, + version); + mUpdater->setCheckPeriod(check_period); + mUpdater->setBandwidthLimit((int)gSavedSettings.getF32("UpdaterMaximumBandwidth") * (1024/8)); + gSavedSettings.getControl("UpdaterMaximumBandwidth")->getSignal()-> + connect(boost::bind(&on_bandwidth_throttle, mUpdater.get(), _2)); + if(gSavedSettings.getU32("UpdaterServiceSetting")) + { + bool install_if_ready = true; + mUpdater->startChecking(install_if_ready); + } + + LLEventPump & updater_pump = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()); + updater_pump.listen("notify_update", ¬ify_update); +} void LLAppViewer::checkForCrash(void) { @@ -2382,7 +2645,7 @@ bool LLAppViewer::initWindow() VIEWER_WINDOW_CLASSNAME, gSavedSettings.getS32("WindowX"), gSavedSettings.getS32("WindowY"), gSavedSettings.getS32("WindowWidth"), gSavedSettings.getS32("WindowHeight"), - FALSE, ignorePixelDepth); + gSavedSettings.getBOOL("WindowFullScreen"), ignorePixelDepth); // Need to load feature table before cheking to start watchdog. const S32 NEVER_SUBMIT_REPORT = 2; @@ -2483,15 +2746,18 @@ void LLAppViewer::cleanupSavedSettings() // save window position if not maximized // as we don't track it in callbacks - BOOL maximized = gViewerWindow->mWindow->getMaximized(); - if (!maximized) + if(NULL != gViewerWindow) { - LLCoordScreen window_pos; - - if (gViewerWindow->mWindow->getPosition(&window_pos)) + BOOL maximized = gViewerWindow->mWindow->getMaximized(); + if (!maximized) { - gSavedSettings.setS32("WindowX", window_pos.mX); - gSavedSettings.setS32("WindowY", window_pos.mY); + LLCoordScreen window_pos; + + if (gViewerWindow->mWindow->getPosition(&window_pos)) + { + gSavedSettings.setS32("WindowX", window_pos.mX); + gSavedSettings.setS32("WindowY", window_pos.mY); + } } } @@ -2514,7 +2780,7 @@ void LLAppViewer::writeSystemInfo() { gDebugInfo["SLLog"] = LLError::logFileName(); - gDebugInfo["ClientInfo"]["Name"] = gSavedSettings.getString("VersionChannelName"); + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); @@ -2595,6 +2861,11 @@ void LLAppViewer::handleViewerCrash() abort(); } + if (LLApp::isCrashloggerDisabled()) + { + abort(); + } + // Returns whether a dialog was shown. // Only do the logic in here once if (pApp->mReportedCrash) @@ -2612,7 +2883,7 @@ void LLAppViewer::handleViewerCrash() //We already do this in writeSystemInfo(), but we do it again here to make /sure/ we have a version //to check against no matter what - gDebugInfo["ClientInfo"]["Name"] = gSavedSettings.getString("VersionChannelName"); + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); @@ -2733,8 +3004,10 @@ void LLAppViewer::handleViewerCrash() pApp->removeMarkerFile(false); } +#if LL_SEND_CRASH_REPORTS // Call to pure virtual, handled by platform specific llappviewer instance. pApp->handleCrashReporting(); +#endif return; } @@ -2869,6 +3142,23 @@ void LLAppViewer::forceQuit() LLApp::setQuitting(); } +//TODO: remove +void LLAppViewer::fastQuit(S32 error_code) +{ + // finish pending transfers + flushVFSIO(); + // let sim know we're logging out + sendLogoutRequest(); + // flush network buffers by shutting down messaging system + end_messaging_system(); + // figure out the error code + S32 final_error_code = error_code ? error_code : (S32)isError(); + // this isn't a crash + removeMarkerFile(); + // get outta here + _exit(final_error_code); +} + void LLAppViewer::requestQuit() { llinfos << "requestQuit" << llendl; @@ -2877,11 +3167,21 @@ void LLAppViewer::requestQuit() if( (LLStartUp::getStartupState() < STATE_STARTED) || !region ) { + // If we have a region, make some attempt to send a logout request first. + // This prevents the halfway-logged-in avatar from hanging around inworld for a couple minutes. + if(region) + { + sendLogoutRequest(); + } + // Quit immediately forceQuit(); return; } + // Try to send metrics back to the grid + metricsSend(!gDisconnected); + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); effectp->setPositionGlobal(gAgent.getPositionGlobal()); effectp->setColor(LLColor4U(gAgent.getEffectColor())); @@ -2918,7 +3218,7 @@ static LLNotificationFunctorRegistration finish_quit_reg("ConfirmQuit", finish_q void LLAppViewer::userQuit() { - if (gDisconnected) + if (gDisconnected || gViewerWindow->getProgressView()->getVisible()) { requestQuit(); } @@ -2941,12 +3241,12 @@ void LLAppViewer::earlyExit(const std::string& name, const LLSD& substitutions) LLNotificationsUtil::add(name, substitutions, LLSD(), finish_early_exit); } -void LLAppViewer::forceExit(S32 arg) +// case where we need the viewer to exit without any need for notifications +void LLAppViewer::earlyExitNoNotify() { - removeMarkerFile(); - - // *FIX:Mani - This kind of exit hardly seems appropriate. - exit(arg); + llwarns << "app_early_exit with no notification: " << llendl; + gDoDisconnect = TRUE; + finish_early_exit( LLSD(), LLSD() ); } void LLAppViewer::abortQuit() @@ -2990,7 +3290,7 @@ void LLAppViewer::migrateCacheDirectory() S32 file_count = 0; std::string file_name; std::string mask = delimiter + "*.*"; - while (gDirUtilp->getNextFileInDir(old_cache_dir, mask, file_name, false)) + while (gDirUtilp->getNextFileInDir(old_cache_dir, mask, file_name)) { if (file_name == "." || file_name == "..") continue; std::string source_path = old_cache_dir + delimiter + file_name; @@ -3209,7 +3509,7 @@ bool LLAppViewer::initCache() dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""); std::string found_file; - if (gDirUtilp->getNextFileInDir(dir, mask, found_file, false)) + if (gDirUtilp->getNextFileInDir(dir, mask, found_file)) { old_vfs_data_file = dir + gDirUtilp->getDirDelimiter() + found_file; @@ -3434,6 +3734,7 @@ void LLAppViewer::loadNameCache() // display names cache std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); + LL_INFOS("AvNameCache") << filename << LL_ENDL; llifstream name_cache_stream(filename); if(name_cache_stream.is_open()) { @@ -3542,6 +3843,18 @@ void LLAppViewer::idle() } } + // debug setting to quit after N seconds of being AFK - 0 to never do this + F32 qas_afk = gSavedSettings.getF32("QuitAfterSecondsOfAFK"); + if (qas_afk > 0.f) + { + // idle time is more than setting + if ( gAwayTriggerTimer.getElapsedTimeF32() > qas_afk ) + { + // go ahead and just quit gracefully + LLAppViewer::instance()->requestQuit(); + } + } + // Must wait until both have avatar object and mute list, so poll // here. request_initial_instant_messages(); @@ -3647,6 +3960,11 @@ void LLAppViewer::idle() llinfos << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << llendl; gObjectList.mNumUnknownUpdates = 0; } + + // ViewerMetrics FPS piggy-backing on the debug timer. + // The 5-second interval is nice for this purpose. If the object debug + // bit moves or is disabled, please give this a suitable home. + LLViewerAssetStatsFF::record_fps_main(gFPSClamped); } } @@ -3689,6 +4007,18 @@ void LLAppViewer::idle() gInventory.idleNotifyObservers(); } + // Metrics logging (LLViewerAssetStats, etc.) + { + static LLTimer report_interval; + + // *TODO: Add configuration controls for this + if (report_interval.getElapsedTimeF32() >= app_metrics_interval) + { + metricsSend(! gDisconnected); + report_interval.reset(); + } + } + if (gDisconnected) { return; @@ -3978,7 +4308,10 @@ void LLAppViewer::sendLogoutRequest() gLogoutMaxTime = LOGOUT_REQUEST_TIME; mLogoutRequestSent = TRUE; - LLVoiceClient::getInstance()->leaveChannel(); + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->leaveChannel(); + } //Set internal status variables and marker files gLogoutInProgress = TRUE; @@ -4225,7 +4558,10 @@ void LLAppViewer::disconnectViewer() // This is where we used to call gObjectList.destroy() and then delete gWorldp. // Now we just ask the LLWorld singleton to cleanly shut down. - LLWorld::getInstance()->destroyClass(); + if(LLWorld::instanceExists()) + { + LLWorld::getInstance()->destroyClass(); + } // call all self-registered classes LLDestroyClassList::instance().fireCallbacks(); @@ -4339,7 +4675,7 @@ void LLAppViewer::handleLoginComplete() initMainloopTimeout("Mainloop Init"); // Store some data to DebugInfo in case of a freeze. - gDebugInfo["ClientInfo"]["Name"] = gSavedSettings.getString("VersionChannelName"); + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); @@ -4399,6 +4735,35 @@ void LLAppViewer::loadEventHostModule(S32 listen_port) return; } + LL_INFOS("eventhost") << "Found lleventhost at '" << dso_path << "'" << LL_ENDL; +#if ! defined(LL_WINDOWS) + { + std::string outfile("/tmp/lleventhost.file.out"); + std::string command("file '" + dso_path + "' > '" + outfile + "' 2>&1"); + int rc = system(command.c_str()); + if (rc != 0) + { + LL_WARNS("eventhost") << command << " ==> " << rc << ':' << LL_ENDL; + } + else + { + LL_INFOS("eventhost") << command << ':' << LL_ENDL; + } + { + std::ifstream reader(outfile.c_str()); + std::string line; + while (std::getline(reader, line)) + { + size_t len = line.length(); + if (len && line[len-1] == '\n') + line.erase(len-1); + LL_INFOS("eventhost") << line << LL_ENDL; + } + } + remove(outfile.c_str()); + } +#endif // LL_WINDOWS + apr_dso_handle_t * eventhost_dso_handle = NULL; apr_pool_t * eventhost_dso_memory_pool = NULL; @@ -4407,13 +4772,13 @@ void LLAppViewer::loadEventHostModule(S32 listen_port) apr_status_t rv = apr_dso_load(&eventhost_dso_handle, dso_path.c_str(), eventhost_dso_memory_pool); - ll_apr_assert_status(rv); + llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle)); llassert_always(eventhost_dso_handle != NULL); int (*ll_plugin_start_func)(LLSD const &) = NULL; rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_start_func, eventhost_dso_handle, "ll_plugin_start"); - ll_apr_assert_status(rv); + llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle)); llassert_always(ll_plugin_start_func != NULL); LLSD args; @@ -4445,7 +4810,7 @@ void LLAppViewer::launchUpdater() // *TODO change userserver to be grid on both viewer and sim, since // userserver no longer exists. query_map["userserver"] = LLGridManager::getInstance()->getGridLabel(); - query_map["channel"] = gSavedSettings.getString("VersionChannelName"); + query_map["channel"] = LLVersionInfo::getChannel(); // *TODO constantize this guy // *NOTE: This URL is also used in win_setup/lldownloader.cpp LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map); @@ -4513,6 +4878,8 @@ void LLAppViewer::launchUpdater() LLAppViewer::sUpdaterInfo->mUpdateExePath += update_url.asString(); LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -name \""; LLAppViewer::sUpdaterInfo->mUpdateExePath += LLAppViewer::instance()->getSecondLifeTitle(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -bundleid \""; + LLAppViewer::sUpdaterInfo->mUpdateExePath += LL_VERSION_BUNDLE_ID; LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" &"; LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; @@ -4583,3 +4950,75 @@ bool LLAppViewer::getMasterSystemAudioMute() { return gSavedSettings.getBOOL("MuteAudio"); } + +//---------------------------------------------------------------------------- +// Metrics-related methods (static and otherwise) +//---------------------------------------------------------------------------- + +/** + * LLViewerAssetStats collects data on a per-region (as defined by the agent's + * location) so we need to tell it about region changes which become a kind of + * hidden variable/global state in the collectors. For collectors not running + * on the main thread, we need to send a message to move the data over safely + * and cheaply (amortized over a run). + */ +void LLAppViewer::metricsUpdateRegion(U64 region_handle) +{ + if (0 != region_handle) + { + LLViewerAssetStatsFF::set_region_main(region_handle); + if (LLAppViewer::sTextureFetch) + { + // Send a region update message into 'thread1' to get the new region. + LLAppViewer::sTextureFetch->commandSetRegion(region_handle); + } + else + { + // No 'thread1', a.k.a. TextureFetch, so update directly + LLViewerAssetStatsFF::set_region_thread1(region_handle); + } + } +} + + +/** + * Attempts to start a multi-threaded metrics report to be sent back to + * the grid for consumption. + */ +void LLAppViewer::metricsSend(bool enable_reporting) +{ + if (! gViewerAssetStatsMain) + return; + + if (LLAppViewer::sTextureFetch) + { + LLViewerRegion * regionp = gAgent.getRegion(); + + if (enable_reporting && regionp) + { + std::string caps_url = regionp->getCapability("ViewerMetrics"); + + // Make a copy of the main stats to send into another thread. + // Receiving thread takes ownership. + LLViewerAssetStats * main_stats(new LLViewerAssetStats(*gViewerAssetStatsMain)); + + // Send a report request into 'thread1' to get the rest of the data + // and provide some additional parameters while here. + LLAppViewer::sTextureFetch->commandSendMetrics(caps_url, + gAgentSessionID, + gAgentID, + main_stats); + main_stats = 0; // Ownership transferred + } + else + { + LLAppViewer::sTextureFetch->commandDataBreak(); + } + } + + // Reset even if we can't report. Rather than gather up a huge chunk of + // data, we'll keep to our sampling interval and retain the data + // resolution in time. + gViewerAssetStatsMain->reset(); +} + diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index fdc3b9ef9e..a18e6cbb02 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -39,7 +39,7 @@ class LLTextureCache; class LLImageDecodeThread; class LLTextureFetch; class LLWatchdogTimeout; -class LLCommandLineParser; +class LLUpdaterService; struct apr_dso_handle_t; @@ -65,12 +65,14 @@ public: virtual bool mainLoop(); // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit. // Application control + void flushVFSIO(); // waits for vfs transfers to complete void forceQuit(); // Puts the viewer into 'shutting down without error' mode. + void fastQuit(S32 error_code = 0); // Shuts down the viewer immediately after sending a logout message void requestQuit(); // Request a quit. A kinder, gentler quit. void userQuit(); // The users asks to quit. Confirm, then requestQuit() void earlyExit(const std::string& name, const LLSD& substitutions = LLSD()); // Display an error dialog and forcibly quit. - void forceExit(S32 arg); // exit() immediately (after some cleanup). + void earlyExitNoNotify(); // Do not display error dialog then forcibly quit. void abortQuit(); // Called to abort a quit request. bool quitRequested() { return mQuitRequested; } @@ -167,6 +169,10 @@ public: // mute/unmute the system's master audio virtual void setMasterSystemAudioMute(bool mute); virtual bool getMasterSystemAudioMute(); + + // Metrics policy helper statics. + static void metricsUpdateRegion(U64 region_handle); + static void metricsSend(bool enable_reporting); protected: virtual bool initWindow(); // Initialize the viewer's window. @@ -186,7 +192,7 @@ private: bool initThreads(); // Initialize viewer threads, return false on failure. bool initConfiguration(); // Initialize settings from the command line/config file. - + void initUpdater(); // Initialize the updater service. bool initCache(); // Initialize local client cache. @@ -251,7 +257,9 @@ private: LLWatchdogTimeout* mMainloopTimeout; + // For performance and metric gathering LLThread* mFastTimerLogThread; + // for tracking viewer<->region circuit death bool mAgentRegionLastAlive; LLUUID mAgentRegionLastID; @@ -260,7 +268,16 @@ private: std::set<struct apr_dso_handle_t*> mPlugins; + U32 mAvailPhysicalMemInKB ; + U32 mAvailVirtualMemInKB ; + + boost::scoped_ptr<LLUpdaterService> mUpdater; + + //--------------------------------------------- + //*NOTE: Mani - legacy updater stuff + // Still useable? public: + //some information for updater typedef struct { @@ -270,6 +287,7 @@ public: static LLUpdaterInfo *sUpdaterInfo ; void launchUpdater(); + //--------------------------------------------- }; // consts from viewer.h diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 7629265730..898cc1c0ba 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -504,8 +504,7 @@ std::string LLAppViewerLinux::generateSerialNumber() // trawl /dev/disk/by-uuid looking for a good-looking UUID to grab std::string this_name; - BOOL wrap = FALSE; - while (gDirUtilp->getNextFileInDir(uuiddir, "*", this_name, wrap)) + while (gDirUtilp->getNextFileInDir(uuiddir, "*", this_name)) { if (this_name.length() > best.length() || (this_name.length() == best.length() && diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 066b4d8bc3..f3f0cde221 100644..100755 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -56,9 +56,11 @@ #include "llmutelist.h" #include "llnotificationsutil.h" // for LLNotificationsUtil #include "llpaneloutfitedit.h" +#include "llpanelprofile.h" #include "llrecentpeople.h" #include "llsidetray.h" #include "lltrans.h" +#include "llviewercontrol.h" #include "llviewerobjectlist.h" #include "llviewermessage.h" // for handle_lure #include "llviewerregion.h" @@ -306,6 +308,20 @@ void LLAvatarActions::showProfile(const LLUUID& id) params["id"] = id; params["open_tab_name"] = "panel_profile"; + // PROFILES: open in webkit window + std::string full_name; + if (gCacheName->getFullName(id,full_name)) + { + std::string agent_name = LLCacheName::buildUsername(full_name); + llinfos << "opening web profile for " << agent_name << llendl; + std::string url = getProfileURL(agent_name); + LLWeb::loadWebURLInternal(url); + } + else + { + llwarns << "no name info for agent id " << id << llendl; + } +#if 0 //Show own profile if(gAgent.getID() == id) { @@ -316,6 +332,7 @@ void LLAvatarActions::showProfile(const LLUUID& id) { LLSideTray::getInstance()->showPanel("panel_profile_view", params); } +#endif } } diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index a56dc129d4..30eecfe323 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -41,7 +41,7 @@ bool LLAvatarListItem::sStaticInitialized = false; S32 LLAvatarListItem::sLeftPadding = 0; -S32 LLAvatarListItem::sRightNamePadding = 0; +S32 LLAvatarListItem::sNameRightPadding = 0; S32 LLAvatarListItem::sChildrenWidths[LLAvatarListItem::ALIC_COUNT]; static LLWidgetNameRegistry::StaticRegistrar sRegisterAvatarListItemParams(&typeid(LLAvatarListItem::Params), "avatar_list_item"); @@ -52,7 +52,8 @@ LLAvatarListItem::Params::Params() voice_call_joined_style("voice_call_joined_style"), voice_call_left_style("voice_call_left_style"), online_style("online_style"), - offline_style("offline_style") + offline_style("offline_style"), + name_right_pad("name_right_pad", 0) {}; @@ -119,6 +120,9 @@ BOOL LLAvatarListItem::postBuild() // so that we can hide and show them again later. initChildrenWidths(this); + // Right padding between avatar name text box and nearest visible child. + sNameRightPadding = LLUICtrlFactory::getDefaultParams<LLAvatarListItem>().name_right_pad; + sStaticInitialized = true; } @@ -486,7 +490,6 @@ void LLAvatarListItem::initChildrenWidths(LLAvatarListItem* avatar_item) S32 icon_width = avatar_item->mAvatarName->getRect().mLeft - avatar_item->mAvatarIcon->getRect().mLeft; sLeftPadding = avatar_item->mAvatarIcon->getRect().mLeft; - sRightNamePadding = avatar_item->mLastInteractionTime->getRect().mLeft - avatar_item->mAvatarName->getRect().mRight; S32 index = ALIC_COUNT; sChildrenWidths[--index] = icon_width; @@ -565,7 +568,7 @@ void LLAvatarListItem::updateChildren() // apply paddings name_new_width -= sLeftPadding; - name_new_width -= sRightNamePadding; + name_new_width -= sNameRightPadding; name_view_rect.setLeftTopAndSize( name_new_left, diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index a069838ac3..c95ac39696 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -51,6 +51,8 @@ public: online_style, offline_style; + Optional<S32> name_right_pad; + Params(); }; @@ -215,7 +217,7 @@ private: static bool sStaticInitialized; // this variable is introduced to improve code readability static S32 sLeftPadding; // padding to first left visible child (icon or name) - static S32 sRightNamePadding; // right padding from name to next visible child + static S32 sNameRightPadding; // right padding from name to next visible child /** * Contains widths of each child specified by EAvatarListItemChildIndex diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 29c2b7565e..35e4548483 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -65,31 +65,42 @@ LLDefaultChildRegistry::Register<LLBottomtrayButton> bottomtray_button("bottomtr // virtual BOOL LLBottomtrayButton::handleHover(S32 x, S32 y, MASK mask) { + if (mCanDrag) + { S32 screenX, screenY; localPointToScreen(x, y, &screenX, &screenY); // pass hover to bottomtray LLBottomTray::getInstance()->onDraggableButtonHover(screenX, screenY); - return FALSE; + return TRUE; + } + else + { + return LLButton::handleHover(x, y, mask); + } } //virtual BOOL LLBottomtrayButton::handleMouseUp(S32 x, S32 y, MASK mask) { + if (mCanDrag) + { S32 screenX, screenY; localPointToScreen(x, y, &screenX, &screenY); // pass mouse up to bottomtray LLBottomTray::getInstance()->onDraggableButtonMouseUp(this, screenX, screenY); - LLButton::handleMouseUp(x, y, mask); - return FALSE; + } + return LLButton::handleMouseUp(x, y, mask); } //virtual BOOL LLBottomtrayButton::handleMouseDown(S32 x, S32 y, MASK mask) { + if (mCanDrag) + { S32 screenX, screenY; localPointToScreen(x, y, &screenX, &screenY); // pass mouse up to bottomtray LLBottomTray::getInstance()->onDraggableButtonMouseDown(this, screenX, screenY); - LLButton::handleMouseDown(x, y, mask); - return FALSE; + } + return LLButton::handleMouseDown(x, y, mask); } static void update_build_button_enable_state() @@ -150,8 +161,6 @@ public: { mFactoryMap["chat_bar"] = LLCallbackMap(LLBottomTray::createNearbyChatBar, NULL); buildFromFile("panel_bottomtray_lite.xml"); - // Necessary for focus movement among child controls - setFocusRoot(TRUE); } BOOL postBuild() @@ -218,9 +227,6 @@ LLBottomTray::LLBottomTray(const LLSD&) //destroyed LLBottomTray requires some subsystems that are long gone //LLUI::getRootView()->addChild(this); - // Necessary for focus movement among child controls - setFocusRoot(TRUE); - { mBottomTrayLite = new LLBottomTrayLite(); mBottomTrayLite->setFollowsAll(); @@ -513,6 +519,9 @@ void LLBottomTray::toggleCameraControls() BOOL LLBottomTray::postBuild() { + LLHints::registerHintTarget("bottom_tray", LLView::getHandle()); + LLHints::registerHintTarget("dest_guide_btn", getChild<LLUICtrl>("destination_btn")->getHandle()); + LLHints::registerHintTarget("avatar_picker_btn", getChild<LLUICtrl>("avatar_btn")->getHandle()); LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("NearbyChatBar.Action", boost::bind(&LLBottomTray::onContextMenuItemClicked, this, _2)); LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("NearbyChatBar.EnableMenuItem", boost::bind(&LLBottomTray::onContextMenuItemEnabled, this, _2)); @@ -1365,20 +1374,33 @@ void LLBottomTray::processExtendButtons(S32& available_width) processExtendButton(*it, available_width); } + const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); + static const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); + const S32 available_width_chiclet = chiclet_panel_width - chiclet_panel_min_width; + // then try to extend Speak button - if (available_width > 0) + if (available_width > 0 || available_width_chiclet > 0) { S32 panel_max_width = mObjectDefaultWidthMap[RS_BUTTON_SPEAK]; S32 panel_width = mSpeakPanel->getRect().getWidth(); S32 possible_extend_width = panel_max_width - panel_width; - if (possible_extend_width >= 0 && possible_extend_width <= available_width) // HACK: this button doesn't change size so possible_extend_width will be 0 + + if (possible_extend_width >= 0 && possible_extend_width <= available_width + available_width_chiclet) // HACK: this button doesn't change size so possible_extend_width will be 0 { mSpeakBtn->setLabelVisible(true); mSpeakPanel->reshape(panel_max_width, mSpeakPanel->getRect().getHeight()); log(mSpeakBtn, "speak button is extended"); - available_width -= possible_extend_width; - + if( available_width > possible_extend_width) + { + available_width -= possible_extend_width; + } + else + { + S32 required_width = possible_extend_width - available_width; + available_width = 0; + mChicletPanel->getParent()->reshape(mChicletPanel->getParent()->getRect().getWidth() - required_width, mChicletPanel->getParent()->getRect().getHeight()); + } lldebugs << "Extending Speak button panel: " << mSpeakPanel->getName() << ", extended width: " << possible_extend_width << ", rest width to process: " << available_width diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 8d8a42c553..dc98170049 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -54,7 +54,9 @@ class LLBottomtrayButton : public LLButton public: struct Params : public LLInitParam::Block<Params, LLButton::Params> { - Params(){} + Optional<bool> can_drag; + Params() + : can_drag("can_drag", true){} }; /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); @@ -62,11 +64,14 @@ public: protected: LLBottomtrayButton(const Params& p) - : LLButton(p) + : LLButton(p), + mCanDrag(p.can_drag) { } friend class LLUICtrlFactory; + + bool mCanDrag; }; class LLBottomTray diff --git a/indra/newview/llbrowsernotification.cpp b/indra/newview/llbrowsernotification.cpp index d6a813d608..6e77d1e336 100644 --- a/indra/newview/llbrowsernotification.cpp +++ b/indra/newview/llbrowsernotification.cpp @@ -29,8 +29,9 @@ #include "llnotificationhandler.h" #include "llnotifications.h" -#include "llfloaterreg.h" #include "llmediactrl.h" +#include "llviewermedia.h" +#include "llviewermediafocus.h" using namespace LLNotificationsUI; @@ -39,10 +40,19 @@ bool LLBrowserNotification::processNotification(const LLSD& notify) LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); if (!notification) return false; - LLMediaCtrl* media_instance = LLMediaCtrl::getInstance(notification->getPayload()["media_id"].asUUID()); + LLUUID media_id = notification->getPayload()["media_id"].asUUID(); + LLMediaCtrl* media_instance = LLMediaCtrl::getInstance(media_id); if (media_instance) { media_instance->showNotification(notification); } + else if (LLViewerMediaFocus::instance().getControlsMediaID() == media_id) + { + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(media_id); + if (impl) + { + impl->showNotification(notification); + } + } return false; } diff --git a/indra/newview/llbuycurrencyhtml.cpp b/indra/newview/llbuycurrencyhtml.cpp index d35c9ed853..e5a9be0203 100644 --- a/indra/newview/llbuycurrencyhtml.cpp +++ b/indra/newview/llbuycurrencyhtml.cpp @@ -33,6 +33,7 @@ #include "llfloaterreg.h" #include "llcommandhandler.h" #include "llviewercontrol.h" +#include "llstatusbar.h" // support for secondlife:///app/buycurrencyhtml/{ACTION}/{NEXT_ACTION}/{RETURN_CODE} SLapps class LLBuyCurrencyHTMLHandler : @@ -156,4 +157,7 @@ void LLBuyCurrencyHTML::closeDialog() { buy_currency_floater->closeFloater(); }; + + // Update L$ balance in the status bar in case L$ were purchased + LLStatusBar::sendMoneyBalanceRequest(); } diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index 078bd73379..328c326278 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -45,6 +45,7 @@ #include "llspeakers.h" #include "lltextutil.h" #include "lltransientfloatermgr.h" +#include "llviewercontrol.h" #include "llviewerdisplayname.h" #include "llviewerwindow.h" #include "llvoicechannel.h" @@ -166,6 +167,7 @@ BOOL LLCallFloater::postBuild() //chrome="true" hides floater caption if (mDragHandle) mDragHandle->setTitleVisible(TRUE); + updateTransparency(TT_ACTIVE); // force using active floater transparency (STORM-730) updateSession(); @@ -205,6 +207,17 @@ void LLCallFloater::draw() } // virtual +void LLCallFloater::setFocus( BOOL b ) +{ + LLTransientDockableFloater::setFocus(b); + + // Force using active floater transparency (STORM-730). + // We have to override setFocus() for LLCallFloater because selecting an item + // of the voice morphing combobox causes the floater to lose focus and thus become transparent. + updateTransparency(TT_ACTIVE); +} + +// virtual void LLCallFloater::onParticipantsChanged() { if (NULL == mParticipants) return; @@ -335,8 +348,9 @@ void LLCallFloater::refreshParticipantList() { mParticipants = new LLParticipantList(mSpeakerManager, mAvatarList, true, mVoiceType != VC_GROUP_CHAT && mVoiceType != VC_AD_HOC_CHAT, false); mParticipants->setValidateSpeakerCallback(boost::bind(&LLCallFloater::validateSpeaker, this, _1)); - mParticipants->setSortOrder(LLParticipantList::E_SORT_BY_RECENT_SPEAKERS); - + const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder"); + mParticipants->setSortOrder(LLParticipantList::EParticipantSortOrder(speaker_sort_order)); + if (LLLocalSpeakerMgr::getInstance() == mSpeakerManager) { mAvatarList->setNoItemsCommentText(getString("no_one_near")); diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 3bc7043353..00a3f76e56 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -64,6 +64,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ void draw(); + /*virtual*/ void setFocus( BOOL b ); /** * Is called by LLVoiceClient::notifyParticipantObservers when voice participant list is changed. diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 9153f7325f..c98bcbda45 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -586,7 +586,7 @@ void LLChatHistory::initFromParams(const LLChatHistory::Params& p) LLLayoutStack::Params layout_p; layout_p.rect = stack_rect; layout_p.follows.flags = FOLLOWS_ALL; - layout_p.orientation = "vertical"; + layout_p.orientation = LLLayoutStack::VERTICAL; layout_p.mouse_opaque = false; LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p, this); @@ -789,22 +789,26 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL // set the link for the object name to be the objectim SLapp // (don't let object names with hyperlinks override our objectim Url) LLStyle::Params link_params(style_params); - link_params.color.control = "HTMLLinkColor"; + LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + link_params.color = link_color; + link_params.readonly_color = link_color; + link_params.is_link = true; link_params.link_href = url; - mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter, + + mEditor->appendText(chat.mFromName + delimiter, false, link_params); } else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log) { LLStyle::Params link_params(style_params); link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID)); + // Add link to avatar's inspector and delimiter to message. - mEditor->appendText(link_params.link_href, false, style_params); - mEditor->appendText(delimiter, false, style_params); + mEditor->appendText(std::string(link_params.link_href) + delimiter, false, link_params); } else { - mEditor->appendText(chat.mFromName + delimiter, false, style_params); + mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter, false, style_params); } } } diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index ababa71348..899e0431e7 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -31,6 +31,7 @@ #include "llchatmsgbox.h" #include "llavatariconctrl.h" +#include "llcommandhandler.h" #include "llfloaterreg.h" #include "lllocalcliprect.h" #include "lltrans.h" @@ -45,6 +46,40 @@ static const S32 msg_right_offset = 10; static const S32 msg_height_pad = 5; //******************************************************************************************************************* +// LLObjectHandler +//******************************************************************************************************************* + +// handle secondlife:///app/object/<ID>/inspect SLURLs +class LLObjectHandler : public LLCommandHandler +{ +public: + LLObjectHandler() : LLCommandHandler("object", UNTRUSTED_BLOCK) { } + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + if (params.size() < 2) return false; + + LLUUID object_id; + if (!object_id.set(params[0], FALSE)) + { + return false; + } + + const std::string verb = params[1].asString(); + + if (verb == "inspect") + { + LLFloaterReg::showInstance("inspect_object", LLSD().with("object_id", object_id)); + return true; + } + + return false; + } +}; + +LLObjectHandler gObjectHandler; + +//******************************************************************************************************************* //LLNearbyChatToastPanel //******************************************************************************************************************* @@ -169,30 +204,44 @@ void LLNearbyChatToastPanel::init(LLSD& notification) { std::string str_sender; - str_sender = "<nolink>"; // disable parsing URLs in object names (STORM-358) - str_sender += fromName; - str_sender += "</nolink>"; + str_sender = fromName; str_sender+=" "; - //append user name + //append sender name + if (mSourceType == CHAT_SOURCE_AGENT || mSourceType == CHAT_SOURCE_OBJECT) { LLStyle::Params style_params_name; - LLColor4 userNameColor = LLUIColorTable::instance().getColor("ChatToastAgentNameColor"); + std::string href; + + if (mSourceType == CHAT_SOURCE_AGENT) + { + href = LLSLURL("agent", mFromID, "about").getSLURLString(); + } + else + { + href = LLSLURL("object", mFromID, "inspect").getSLURLString(); + } - style_params_name.color(userNameColor); + LLColor4 user_name_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params_name.color(user_name_color); std::string font_name = LLFontGL::nameFromFont(messageFont); std::string font_style_size = LLFontGL::sizeFromFont(messageFont); style_params_name.font.name(font_name); style_params_name.font.size(font_style_size); - style_params_name.link_href = LLSLURL("agent",mFromID,"about").getSLURLString(); + style_params_name.link_href = href; + style_params_name.is_link = true; msg_text->appendText(str_sender, FALSE, style_params_name); } + else + { + msg_text->appendText(str_sender, false); + } } //append text diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 8f385160e9..885d553524 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -1092,9 +1092,11 @@ LLChicletPanel::LLChicletPanel(const Params&p) LLChicletPanel::~LLChicletPanel() { - LLTransientFloaterMgr::getInstance()->removeControlView(mLeftScrollButton); - LLTransientFloaterMgr::getInstance()->removeControlView(mRightScrollButton); - + if(LLTransientFloaterMgr::instanceExists()) + { + LLTransientFloaterMgr::getInstance()->removeControlView(mLeftScrollButton); + LLTransientFloaterMgr::getInstance()->removeControlView(mRightScrollButton); + } } void im_chiclet_callback(LLChicletPanel* panel, const LLSD& data){ diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index c9a526a3be..4a1ba6f1b5 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -53,6 +53,7 @@ LLColorSwatchCtrl::Params::Params() alpha_background_image("alpha_background_image"), border_color("border_color"), label_width("label_width", -1), + label_height("label_height", -1), caption_text("caption_text"), border("border") { @@ -68,17 +69,20 @@ LLColorSwatchCtrl::LLColorSwatchCtrl(const Params& p) mOnCancelCallback(p.cancel_callback()), mOnSelectCallback(p.select_callback()), mBorderColor(p.border_color()), - mLabelWidth(p.label_width) + mLabelWidth(p.label_width), + mLabelHeight(p.label_height) { LLTextBox::Params tp = p.caption_text; + // use custom label height if it is provided + mLabelHeight = mLabelHeight != -1 ? mLabelHeight : BTN_HEIGHT_SMALL; // label_width is specified, not -1 if(mLabelWidth!= -1) { - tp.rect(LLRect( 0, BTN_HEIGHT_SMALL, mLabelWidth, 0 )); + tp.rect(LLRect( 0, mLabelHeight, mLabelWidth, 0 )); } else { - tp.rect(LLRect( 0, BTN_HEIGHT_SMALL, getRect().getWidth(), 0 )); + tp.rect(LLRect( 0, mLabelHeight, getRect().getWidth(), 0 )); } tp.initial_value(p.label()); @@ -88,7 +92,7 @@ LLColorSwatchCtrl::LLColorSwatchCtrl(const Params& p) LLRect border_rect = getLocalRect(); border_rect.mTop -= 1; border_rect.mRight -=1; - border_rect.mBottom += BTN_HEIGHT_SMALL; + border_rect.mBottom += mLabelHeight; LLViewBorder::Params params = p.border; params.rect(border_rect); @@ -191,10 +195,12 @@ BOOL LLColorSwatchCtrl::handleMouseUp(S32 x, S32 y, MASK mask) // assumes GL state is set for 2D void LLColorSwatchCtrl::draw() { - F32 alpha = getDrawContext().mAlpha; + // If we're in a focused floater, don't apply the floater's alpha to the color swatch (STORM-676). + F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); + mBorder->setKeyboardFocusHighlight(hasFocus()); // Draw border - LLRect border( 0, getRect().getHeight(), getRect().getWidth(), BTN_HEIGHT_SMALL ); + LLRect border( 0, getRect().getHeight(), getRect().getWidth(), mLabelHeight ); gl_rect_2d( border, mBorderColor.get(), FALSE ); LLRect interior = border; @@ -203,19 +209,29 @@ void LLColorSwatchCtrl::draw() // Check state if ( mValid ) { + if (!mColor.isOpaque()) + { + // Draw checker board. + gl_rect_2d_checkerboard(interior, alpha); + } + // Draw the color swatch - gl_rect_2d_checkerboard( interior ); - gl_rect_2d(interior, mColor, TRUE); - LLColor4 opaque_color = mColor; - opaque_color.mV[VALPHA] = 1.f; - gGL.color4fv(opaque_color.mV); - if (mAlphaGradientImage.notNull()) + gl_rect_2d(interior, mColor % alpha, TRUE); + + if (!mColor.isOpaque()) { - gGL.pushMatrix(); + // Draw semi-transparent center area in filled with mColor. + LLColor4 opaque_color = mColor; + opaque_color.mV[VALPHA] = alpha; + gGL.color4fv(opaque_color.mV); + if (mAlphaGradientImage.notNull()) { - mAlphaGradientImage->draw(interior, mColor); + gGL.pushMatrix(); + { + mAlphaGradientImage->draw(interior, mColor % alpha); + } + gGL.popMatrix(); } - gGL.popMatrix(); } } else diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h index a4ce1ca099..cd859ea128 100644 --- a/indra/newview/llcolorswatch.h +++ b/indra/newview/llcolorswatch.h @@ -61,6 +61,7 @@ public: Optional<commit_callback_t> select_callback; Optional<LLUIColor> border_color; Optional<S32> label_width; + Optional<S32> label_height; Optional<LLTextBox::Params> caption_text; Optional<LLViewBorder::Params> border; @@ -112,6 +113,7 @@ protected: commit_callback_t mOnCancelCallback; commit_callback_t mOnSelectCallback; S32 mLabelWidth; + S32 mLabelHeight; LLPointer<LLUIImage> mAlphaGradientImage; std::string mFallbackImageName; diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp index 360ba080ac..360ba080ac 100644..100755 --- a/indra/newview/llcommandhandler.cpp +++ b/indra/newview/llcommandhandler.cpp diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index ee8646aad0..65c61c4a8b 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -267,7 +267,11 @@ bool LLCommandLineParser::parseAndStoreResults(po::command_line_parser& clp) { clp.options(gOptionsDesc); clp.positional(gPositionalOptions); - clp.style(po::command_line_style::default_style + // SNOW-626: Boost 1.42 erroneously added allow_guessing to the default style + // (see http://groups.google.com/group/boost-list/browse_thread/thread/545d7bf98ff9bb16?fwc=2&pli=1) + // Remove allow_guessing from the default style, because that is not allowed + // when we have options that are a prefix of other options (aka, --help and --helperuri). + clp.style((po::command_line_style::default_style & ~po::command_line_style::allow_guessing) | po::command_line_style::allow_long_disguise); if(mExtraParser) { @@ -341,7 +345,10 @@ bool LLCommandLineParser::parseCommandLine(int argc, char **argv) bool LLCommandLineParser::parseCommandLineString(const std::string& str) { // Split the string content into tokens - boost::escaped_list_separator<char> sep("\\", "\r\n ", "\"'"); + const char* escape_chars = "\\"; + const char* separator_chars = "\r\n "; + const char* quote_chars = "\"'"; + boost::escaped_list_separator<char> sep(escape_chars, separator_chars, quote_chars); boost::tokenizer< boost::escaped_list_separator<char> > tok(str, sep); std::vector<std::string> tokens; // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens)); diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index 2b92b228b3..b4a1457f47 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -166,7 +166,7 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo() gAgent.getSecureSessionID().asString()); keywordArgs.appendString("language", LLUI::getLanguage()); keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy); - keywordArgs.appendString("viewerChannel", gSavedSettings.getString("VersionChannelName")); + keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel()); keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor()); keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor()); keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch()); @@ -241,7 +241,7 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password) { keywordArgs.appendString("password", password); } - keywordArgs.appendString("viewerChannel", gSavedSettings.getString("VersionChannelName")); + keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel()); keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor()); keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor()); keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch()); diff --git a/indra/newview/lldirpicker.cpp b/indra/newview/lldirpicker.cpp index 53101f0ce2..dd243397a1 100644 --- a/indra/newview/lldirpicker.cpp +++ b/indra/newview/lldirpicker.cpp @@ -35,6 +35,7 @@ #include "llframetimer.h" #include "lltrans.h" #include "llwindow.h" // beforeDialog() +#include "llviewercontrol.h" #if LL_LINUX || LL_SOLARIS # include "llfilepicker.h" @@ -53,6 +54,23 @@ LLDirPicker LLDirPicker::sInstance; // // Implementation // + +// utility function to check if access to local file system via file browser +// is enabled and if not, tidy up and indicate we're not allowed to do this. +bool LLDirPicker::check_local_file_access_enabled() +{ + // if local file browsing is turned off, return without opening dialog + bool local_file_system_browsing_enabled = gSavedSettings.getBOOL("LocalFileSystemBrowsingEnabled"); + if ( ! local_file_system_browsing_enabled ) + { + mDir.clear(); // Windows + mFileName = NULL; // Mac/Linux + return false; + } + + return true; +} + #if LL_WINDOWS LLDirPicker::LLDirPicker() : @@ -72,6 +90,13 @@ BOOL LLDirPicker::getDir(std::string* filename) { return FALSE; } + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + BOOL success = FALSE; // Modal, so pause agent @@ -231,7 +256,13 @@ BOOL LLDirPicker::getDir(std::string* filename) if( mLocked ) return FALSE; BOOL success = FALSE; OSStatus error = noErr; - + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + mFileName = filename; // mNavOptions.saveFileName @@ -289,6 +320,13 @@ void LLDirPicker::reset() BOOL LLDirPicker::getDir(std::string* filename) { reset(); + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + if (mFilePicker) { GtkWindow* picker = mFilePicker->buildFilePicker(false, true, diff --git a/indra/newview/lldirpicker.h b/indra/newview/lldirpicker.h index a360293fff..2188b7edd0 100644 --- a/indra/newview/lldirpicker.h +++ b/indra/newview/lldirpicker.h @@ -75,6 +75,7 @@ private: }; void buildDirname( void ); + bool check_local_file_access_enabled(); #if LL_DARWIN NavDialogCreationOptions mNavOptions; diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 583bb54160..8106fada11 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -358,6 +358,7 @@ void LLDrawable::makeActive() { U32 pcode = mVObjp->getPCode(); if (pcode == LLViewerObject::LL_VO_WATER || + pcode == LLViewerObject::LL_VO_VOID_WATER || pcode == LLViewerObject::LL_VO_SURFACE_PATCH || pcode == LLViewerObject::LL_VO_PART_GROUP || pcode == LLViewerObject::LL_VO_HUD_PART_GROUP || diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index cb651f9d3a..ba576ff97f 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -89,6 +89,7 @@ LLDrawPool *LLDrawPool::createPool(const U32 type, LLViewerTexture *tex0) case POOL_SKY: poolp = new LLDrawPoolSky(); break; + case POOL_VOIDWATER: case POOL_WATER: poolp = new LLDrawPoolWater(); break; diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h index 221f81ec25..1d6f99d346 100644 --- a/indra/newview/lldrawpool.h +++ b/indra/newview/lldrawpool.h @@ -57,6 +57,7 @@ public: POOL_BUMP, POOL_INVISIBLE, // see below * POOL_AVATAR, + POOL_VOIDWATER, POOL_WATER, POOL_GLOW, POOL_ALPHA, @@ -167,7 +168,6 @@ public: LLFacePool(const U32 type); virtual ~LLFacePool(); - virtual void renderForSelect() = 0; BOOL isDead() { return mReferences.empty(); } virtual LLViewerTexture *getTexture(); diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 8cf4dc1b95..dbd5da31a6 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -743,79 +743,6 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) } } -//----------------------------------------------------------------------------- -// renderForSelect() -//----------------------------------------------------------------------------- -void LLDrawPoolAvatar::renderForSelect() -{ - - - if (mDrawFace.empty()) - { - return; - } - - const LLFace *facep = mDrawFace[0]; - if (!facep->getDrawable()) - { - return; - } - LLVOAvatar *avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get(); - - if (avatarp->isDead() || avatarp->mIsDummy || avatarp->mDrawable.isNull()) - { - return; - } - - S32 curr_shader_level = getVertexShaderLevel(); - S32 name = avatarp->mDrawable->getVObj()->mGLName; - LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name); - - BOOL impostor = avatarp->isImpostor(); - if (impostor) - { - gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_VERT_COLOR); - gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_TEX_ALPHA, LLTexUnit::TBS_VERT_ALPHA); - - avatarp->renderImpostor(color); - - gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); - return; - } - - sVertexProgram = &gAvatarPickProgram; - if (curr_shader_level > 0) - { - gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX]; - } - gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.2f); - gGL.setSceneBlendType(LLRender::BT_REPLACE); - - glColor4ubv(color.mV); - - if (curr_shader_level > 0) // for hardware blending - { - sRenderingSkinned = TRUE; - sVertexProgram->bind(); - enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); - } - - avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE); - - // if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done - if (curr_shader_level > 0) - { - sRenderingSkinned = FALSE; - sVertexProgram->unbind(); - disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); - } - - gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - // restore texture mode - gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); -} //----------------------------------------------------------------------------- // getDebugTexture() diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h index c46fed824e..f536d3c911 100644 --- a/indra/newview/lldrawpoolavatar.h +++ b/indra/newview/lldrawpoolavatar.h @@ -64,7 +64,6 @@ public: /*virtual*/ void endRenderPass(S32 pass); /*virtual*/ void prerender(); /*virtual*/ void render(S32 pass = 0); - /*virtual*/ void renderForSelect(); /*virtual*/ S32 getNumDeferredPasses(); /*virtual*/ void beginDeferredPass(S32 pass); diff --git a/indra/newview/lldrawpoolclouds.cpp b/indra/newview/lldrawpoolclouds.cpp index f7da144671..5db1d8cfed 100644 --- a/indra/newview/lldrawpoolclouds.cpp +++ b/indra/newview/lldrawpoolclouds.cpp @@ -95,6 +95,3 @@ void LLDrawPoolClouds::render(S32 pass) } -void LLDrawPoolClouds::renderForSelect() -{ -} diff --git a/indra/newview/lldrawpoolclouds.h b/indra/newview/lldrawpoolclouds.h index 548720ed9c..019f11a795 100644 --- a/indra/newview/lldrawpoolclouds.h +++ b/indra/newview/lldrawpoolclouds.h @@ -49,7 +49,6 @@ public: /*virtual*/ void enqueue(LLFace *face); /*virtual*/ void beginRenderPass(S32 pass); /*virtual*/ void render(S32 pass = 0); - /*virtual*/ void renderForSelect(); }; #endif // LL_LLDRAWPOOLSKY_H diff --git a/indra/newview/lldrawpoolground.cpp b/indra/newview/lldrawpoolground.cpp index e950fbfa82..ce07e62122 100644 --- a/indra/newview/lldrawpoolground.cpp +++ b/indra/newview/lldrawpoolground.cpp @@ -68,7 +68,7 @@ void LLDrawPoolGround::render(S32 pass) LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - LLGLClampToFarClip far_clip(glh_get_current_projection()); + LLGLSquashToFarClip far_clip(glh_get_current_projection()); F32 water_height = gAgent.getRegion()->getWaterHeight(); glPushMatrix(); @@ -85,7 +85,3 @@ void LLDrawPoolGround::render(S32 pass) glPopMatrix(); } -void LLDrawPoolGround::renderForSelect() -{ -} - diff --git a/indra/newview/lldrawpoolground.h b/indra/newview/lldrawpoolground.h index c6c7cbf964..a4f8a3fcf5 100644 --- a/indra/newview/lldrawpoolground.h +++ b/indra/newview/lldrawpoolground.h @@ -47,7 +47,6 @@ public: /*virtual*/ void prerender(); /*virtual*/ void render(S32 pass = 0); - /*virtual*/ void renderForSelect(); }; #endif // LL_LLDRAWPOOLGROUND_H diff --git a/indra/newview/lldrawpoolsky.cpp b/indra/newview/lldrawpoolsky.cpp index d811ab8c54..6b45c5abb0 100644 --- a/indra/newview/lldrawpoolsky.cpp +++ b/indra/newview/lldrawpoolsky.cpp @@ -97,7 +97,7 @@ void LLDrawPoolSky::render(S32 pass) LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - LLGLClampToFarClip far_clip(glh_get_current_projection()); + LLGLSquashToFarClip far_clip(glh_get_current_projection()); LLGLEnable fog_enable( (mVertexShaderLevel < 1 && LLViewerCamera::getInstance()->cameraUnderWater()) ? GL_FOG : 0); @@ -143,10 +143,6 @@ void LLDrawPoolSky::renderSkyCubeFace(U8 side) } } -void LLDrawPoolSky::renderForSelect() -{ -} - void LLDrawPoolSky::endRenderPass( S32 pass ) { } diff --git a/indra/newview/lldrawpoolsky.h b/indra/newview/lldrawpoolsky.h index 15d643c886..098bd2134a 100644 --- a/indra/newview/lldrawpoolsky.h +++ b/indra/newview/lldrawpoolsky.h @@ -58,7 +58,6 @@ public: /*virtual*/ void prerender(); /*virtual*/ void render(S32 pass = 0); - /*virtual*/ void renderForSelect(); /*virtual*/ void endRenderPass(S32 pass); void setSkyTex(LLSkyTex* const st) { mSkyTex = st; } diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 3dede9d8fc..84eeace9c6 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -899,27 +899,6 @@ void LLDrawPoolTerrain::renderOwnership() } -void LLDrawPoolTerrain::renderForSelect() -{ - if (mDrawFace.empty()) - { - return; - } - - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - for (std::vector<LLFace*>::iterator iter = mDrawFace.begin(); - iter != mDrawFace.end(); iter++) - { - LLFace *facep = *iter; - if (!facep->getDrawable()->isDead() && (facep->getDrawable()->getVObj()->mGLName)) - { - facep->renderForSelect(LLVertexBuffer::MAP_VERTEX); - } - } -} - void LLDrawPoolTerrain::dirtyTextures(const std::set<LLViewerFetchedTexture*>& textures) { LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(mTexturep) ; diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h index 730298609d..3056da44d5 100644 --- a/indra/newview/lldrawpoolterrain.h +++ b/indra/newview/lldrawpoolterrain.h @@ -66,7 +66,6 @@ public: /*virtual*/ void prerender(); /*virtual*/ void beginRenderPass( S32 pass ); /*virtual*/ void endRenderPass( S32 pass ); - /*virtual*/ void renderForSelect(); /*virtual*/ void dirtyTextures(const std::set<LLViewerFetchedTexture*>& textures); /*virtual*/ LLViewerTexture *getTexture(); /*virtual*/ LLViewerTexture *getDebugTexture(); diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index 09cca8b73c..f1198c9a8d 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -183,68 +183,6 @@ void LLDrawPoolTree::endShadowPass(S32 pass) } -void LLDrawPoolTree::renderForSelect() -{ - if (mDrawFace.empty()) - { - return; - } - - LLOverrideFaceColor color(this, 1.f, 1.f, 1.f, 1.f); - - LLGLSObjectSelectAlpha gls_alpha; - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gGL.setSceneBlendType(LLRender::BT_REPLACE); - gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); - - gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR); - gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_TEX_ALPHA, LLTexUnit::TBS_VERT_ALPHA); - - if (gSavedSettings.getBOOL("RenderAnimateTrees")) - { - renderTree(TRUE); - } - else - { - gGL.getTexUnit(sDiffTex)->bind(mTexturep); - - for (std::vector<LLFace*>::iterator iter = mDrawFace.begin(); - iter != mDrawFace.end(); iter++) - { - LLFace *face = *iter; - LLDrawable *drawablep = face->getDrawable(); - - if (drawablep->isDead() || face->mVertexBuffer.isNull()) - { - continue; - } - - // Render each of the trees - LLVOTree *treep = (LLVOTree *)drawablep->getVObj().get(); - - LLColor4U color(255,255,255,255); - - if (treep->mGLName != 0) - { - S32 name = treep->mGLName; - color = LLColor4U((U8)(name >> 16), (U8)(name >> 8), (U8)name, 255); - - LLFacePool::LLOverrideFaceColor col(this, color); - - face->mVertexBuffer->setBuffer(LLDrawPoolTree::VERTEX_DATA_MASK); - face->mVertexBuffer->drawRange(LLRender::TRIANGLES, 0, face->mVertexBuffer->getRequestedVerts()-1, face->mVertexBuffer->getRequestedIndices(), 0); - gPipeline.addTrianglesDrawn(face->mVertexBuffer->getRequestedIndices()); - } - } - } - - gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); -} - void LLDrawPoolTree::renderTree(BOOL selecting) { LLGLState normalize(GL_NORMALIZE, TRUE); diff --git a/indra/newview/lldrawpooltree.h b/indra/newview/lldrawpooltree.h index cebe41f75f..ddb259bb82 100644 --- a/indra/newview/lldrawpooltree.h +++ b/indra/newview/lldrawpooltree.h @@ -62,7 +62,6 @@ public: /*virtual*/ void render(S32 pass = 0); /*virtual*/ void endRenderPass( S32 pass ); /*virtual*/ S32 getNumPasses() { return 1; } - /*virtual*/ void renderForSelect(); /*virtual*/ BOOL verify() const; /*virtual*/ LLViewerTexture *getTexture(); /*virtual*/ LLViewerTexture *getDebugTexture(); diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp index ce1b899d55..dc94924da4 100644 --- a/indra/newview/lldrawpoolwater.cpp +++ b/indra/newview/lldrawpoolwater.cpp @@ -48,7 +48,8 @@ #include "llviewershadermgr.h" #include "llwaterparammanager.h" -const LLUUID WATER_TEST("2bfd3884-7e27-69b9-ba3a-3e673f680004"); +const LLUUID TRANSPARENT_WATER_TEXTURE("2bfd3884-7e27-69b9-ba3a-3e673f680004"); +const LLUUID OPAQUE_WATER_TEXTURE("43c32285-d658-1793-c123-bf86315de055"); static float sTime; @@ -71,10 +72,14 @@ LLDrawPoolWater::LLDrawPoolWater() : gGL.getTexUnit(0)->bind(mHBTex[1]); mHBTex[1]->setAddressMode(LLTexUnit::TAM_CLAMP); - mWaterImagep = LLViewerTextureManager::getFetchedTexture(WATER_TEST); - mWaterImagep->setNoDelete() ; + + mWaterImagep = LLViewerTextureManager::getFetchedTexture(TRANSPARENT_WATER_TEXTURE); + llassert(mWaterImagep); + mWaterImagep->setNoDelete(); + mOpaqueWaterImagep = LLViewerTextureManager::getFetchedTexture(OPAQUE_WATER_TEXTURE); + llassert(mOpaqueWaterImagep); mWaterNormp = LLViewerTextureManager::getFetchedTexture(DEFAULT_WATER_NORMAL); - mWaterNormp->setNoDelete() ; + mWaterNormp->setNoDelete(); restoreGL(); } @@ -161,6 +166,14 @@ void LLDrawPoolWater::render(S32 pass) std::sort(mDrawFace.begin(), mDrawFace.end(), LLFace::CompareDistanceGreater()); + // See if we are rendering water as opaque or not + if (!gSavedSettings.getBOOL("RenderTransparentWater")) + { + // render water for low end hardware + renderOpaqueLegacyWater(); + return; + } + LLGLEnable blend(GL_BLEND); if ((mVertexShaderLevel > 0) && !sSkipScreenCopy) @@ -314,6 +327,87 @@ void LLDrawPoolWater::render(S32 pass) gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); } +// for low end hardware +void LLDrawPoolWater::renderOpaqueLegacyWater() +{ + LLVOSky *voskyp = gSky.mVOSkyp; + + stop_glerror(); + + // Depth sorting and write to depth buffer + // since this is opaque, we should see nothing + // behind the water. No blending because + // of no transparency. And no face culling so + // that the underside of the water is also opaque. + LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE); + LLGLDisable no_cull(GL_CULL_FACE); + LLGLDisable no_blend(GL_BLEND); + + gPipeline.disableLights(); + + mOpaqueWaterImagep->addTextureStats(1024.f*1024.f); + + // Activate the texture binding and bind one + // texture since all images will have the same texture + gGL.getTexUnit(0)->activate(); + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(0)->bind(mOpaqueWaterImagep); + + // Automatically generate texture coords for water texture + glEnable(GL_TEXTURE_GEN_S); //texture unit 0 + glEnable(GL_TEXTURE_GEN_T); //texture unit 0 + glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + + // Use the fact that we know all water faces are the same size + // to save some computation + + // Slowly move texture coordinates over time so the watter appears + // to be moving. + F32 movement_period_secs = 50.f; + + F32 offset = fmod(gFrameTimeSeconds, movement_period_secs); + + if (movement_period_secs != 0) + { + offset /= movement_period_secs; + } + else + { + offset = 0; + } + + F32 tp0[4] = { 16.f / 256.f, 0.0f, 0.0f, offset }; + F32 tp1[4] = { 0.0f, 16.f / 256.f, 0.0f, offset }; + + glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0); + glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1); + + glColor3f(1.f, 1.f, 1.f); + + for (std::vector<LLFace*>::iterator iter = mDrawFace.begin(); + iter != mDrawFace.end(); iter++) + { + LLFace *face = *iter; + if (voskyp->isReflFace(face)) + { + continue; + } + + face->renderIndexed(); + } + + stop_glerror(); + + // Reset the settings back to expected values + glDisable(GL_TEXTURE_GEN_S); //texture unit 0 + glDisable(GL_TEXTURE_GEN_T); //texture unit 0 + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); +} + + void LLDrawPoolWater::renderReflection(LLFace* face) { LLVOSky *voskyp = gSky.mVOSkyp; @@ -532,6 +626,7 @@ void LLDrawPoolWater::shade() glColor4fv(water_color.mV); { + LLGLEnable depth_clamp(gGLManager.mHasDepthClamp ? GL_DEPTH_CLAMP : 0); LLGLDisable cullface(GL_CULL_FACE); for (std::vector<LLFace*>::iterator iter = mDrawFace.begin(); iter != mDrawFace.end(); iter++) @@ -548,30 +643,19 @@ void LLDrawPoolWater::shade() sNeedsReflectionUpdate = TRUE; - if (water->getUseTexture()) + if (water->getUseTexture() || !water->getIsEdgePatch()) { sNeedsDistortionUpdate = TRUE; face->renderIndexed(); } + else if (gGLManager.mHasDepthClamp || deferred_render) + { + face->renderIndexed(); + } else - { //smash background faces to far clip plane - if (water->getIsEdgePatch()) - { - if (deferred_render) - { - face->renderIndexed(); - } - else - { - LLGLClampToFarClip far_clip(glh_get_current_projection()); - face->renderIndexed(); - } - } - else - { - sNeedsDistortionUpdate = TRUE; - face->renderIndexed(); - } + { + LLGLSquashToFarClip far_clip(glh_get_current_projection()); + face->renderIndexed(); } } } @@ -601,12 +685,6 @@ void LLDrawPoolWater::shade() } -void LLDrawPoolWater::renderForSelect() -{ - // Can't select water! - return; -} - LLViewerTexture *LLDrawPoolWater::getDebugTexture() { return LLViewerFetchedTexture::sSmokeImagep; diff --git a/indra/newview/lldrawpoolwater.h b/indra/newview/lldrawpoolwater.h index 3ab4bc5e2c..99b541ca5a 100644 --- a/indra/newview/lldrawpoolwater.h +++ b/indra/newview/lldrawpoolwater.h @@ -39,6 +39,7 @@ class LLDrawPoolWater: public LLFacePool protected: LLPointer<LLViewerTexture> mHBTex[2]; LLPointer<LLViewerTexture> mWaterImagep; + LLPointer<LLViewerTexture> mOpaqueWaterImagep; LLPointer<LLViewerTexture> mWaterNormp; public: @@ -74,13 +75,15 @@ public: /*virtual*/ S32 getNumPasses(); /*virtual*/ void render(S32 pass = 0); /*virtual*/ void prerender(); - /*virtual*/ void renderForSelect(); /*virtual*/ LLViewerTexture *getDebugTexture(); /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display void renderReflection(LLFace* face); void shade(); + +protected: + void renderOpaqueLegacyWater(); }; void cgErrorCallback(); diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index 41a299151e..eaa6aa7e37 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -260,7 +260,7 @@ void LLDrawPoolWLSky::render(S32 pass) LLGLDepthTest depth(GL_TRUE, GL_FALSE); LLGLDisable clip(GL_CLIP_PLANE0); - LLGLClampToFarClip far_clip(glh_get_current_projection()); + LLGLSquashToFarClip far_clip(glh_get_current_projection()); renderSkyHaze(camHeightLocal); diff --git a/indra/newview/llexternaleditor.cpp b/indra/newview/llexternaleditor.cpp new file mode 100644 index 0000000000..54968841ab --- /dev/null +++ b/indra/newview/llexternaleditor.cpp @@ -0,0 +1,192 @@ +/** + * @file llexternaleditor.cpp + * @brief A convenient class to run external editor. + * + * $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 "llviewerprecompiledheaders.h" +#include "llexternaleditor.h" + +#include "llui.h" + +// static +const std::string LLExternalEditor::sFilenameMarker = "%s"; + +// static +const std::string LLExternalEditor::sSetting = "ExternalEditor"; + +bool LLExternalEditor::setCommand(const std::string& env_var, const std::string& override) +{ + std::string cmd = findCommand(env_var, override); + if (cmd.empty()) + { + llwarns << "Empty editor command" << llendl; + return false; + } + + // Add the filename marker if missing. + if (cmd.find(sFilenameMarker) == std::string::npos) + { + cmd += " \"" + sFilenameMarker + "\""; + llinfos << "Adding the filename marker (" << sFilenameMarker << ")" << llendl; + } + + string_vec_t tokens; + if (tokenize(tokens, cmd) < 2) // 2 = bin + at least one arg (%s) + { + llwarns << "Error parsing editor command" << llendl; + return false; + } + + // Check executable for existence. + std::string bin_path = tokens[0]; + if (!LLFile::isfile(bin_path)) + { + llwarns << "Editor binary [" << bin_path << "] not found" << llendl; + return false; + } + + // Save command. + mProcess.setExecutable(bin_path); + mArgs.clear(); + for (size_t i = 1; i < tokens.size(); ++i) + { + if (i > 1) mArgs += " "; + mArgs += "\"" + tokens[i] + "\""; + } + llinfos << "Setting command [" << bin_path << " " << mArgs << "]" << llendl; + + return true; +} + +bool LLExternalEditor::run(const std::string& file_path) +{ + std::string args = mArgs; + if (mProcess.getExecutable().empty() || args.empty()) + { + llwarns << "Editor command not set" << llendl; + return false; + } + + // Substitute the filename marker in the command with the actual passed file name. + LLStringUtil::replaceString(args, sFilenameMarker, file_path); + + // Split command into separate tokens. + string_vec_t tokens; + tokenize(tokens, args); + + // Set process arguments taken from the command. + mProcess.clearArguments(); + for (string_vec_t::const_iterator arg_it = tokens.begin(); arg_it != tokens.end(); ++arg_it) + { + mProcess.addArgument(*arg_it); + } + + // Run the editor. + llinfos << "Running editor command [" << mProcess.getExecutable() + " " + args << "]" << llendl; + int result = mProcess.launch(); + if (result == 0) + { + // Prevent killing the process in destructor (will add it to the zombies list). + mProcess.orphan(); + } + + return result == 0; +} + +// static +size_t LLExternalEditor::tokenize(string_vec_t& tokens, const std::string& str) +{ + tokens.clear(); + + // Split the argument string into separate strings for each argument + typedef boost::tokenizer< boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("", "\" ", boost::drop_empty_tokens); + + tokenizer tokens_list(str, sep); + tokenizer::iterator token_iter; + BOOL inside_quotes = FALSE; + BOOL last_was_space = FALSE; + for (token_iter = tokens_list.begin(); token_iter != tokens_list.end(); ++token_iter) + { + if (!strncmp("\"",(*token_iter).c_str(),2)) + { + inside_quotes = !inside_quotes; + } + else if (!strncmp(" ",(*token_iter).c_str(),2)) + { + if(inside_quotes) + { + tokens.back().append(std::string(" ")); + last_was_space = TRUE; + } + } + else + { + std::string to_push = *token_iter; + if (last_was_space) + { + tokens.back().append(to_push); + last_was_space = FALSE; + } + else + { + tokens.push_back(to_push); + } + } + } + + return tokens.size(); +} + +// static +std::string LLExternalEditor::findCommand( + const std::string& env_var, + const std::string& override) +{ + std::string cmd; + + // Get executable path. + if (!override.empty()) // try the supplied override first + { + cmd = override; + llinfos << "Using override" << llendl; + } + else if (!LLUI::sSettingGroups["config"]->getString(sSetting).empty()) + { + cmd = LLUI::sSettingGroups["config"]->getString(sSetting); + llinfos << "Using setting" << llendl; + } + else // otherwise use the path specified by the environment variable + { + char* env_var_val = getenv(env_var.c_str()); + if (env_var_val) + { + cmd = env_var_val; + llinfos << "Using env var " << env_var << llendl; + } + } + + llinfos << "Found command [" << cmd << "]" << llendl; + return cmd; +} diff --git a/indra/newview/llexternaleditor.h b/indra/newview/llexternaleditor.h new file mode 100644 index 0000000000..6ea210d5e2 --- /dev/null +++ b/indra/newview/llexternaleditor.h @@ -0,0 +1,91 @@ +/** + * @file llexternaleditor.h + * @brief A convenient class to run external editor. + * + * $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_LLEXTERNALEDITOR_H +#define LL_LLEXTERNALEDITOR_H + +#include <llprocesslauncher.h> + +/** + * Usage: + * LLExternalEditor ed; + * ed.setCommand("MY_EXTERNAL_EDITOR_VAR"); + * ed.run("/path/to/file1"); + * ed.run("/other/path/to/file2"); + */ +class LLExternalEditor +{ + typedef std::vector<std::string> string_vec_t; + +public: + + /** + * Set editor command. + * + * @param env_var Environment variable of the same purpose. + * @param override Optional override. + * + * First tries the override, then a predefined setting (sSetting), + * then the environment variable. + * + * @return Command if found, empty string otherwise. + * + * @see sSetting + */ + bool setCommand(const std::string& env_var, const std::string& override = LLStringUtil::null); + + /** + * Run the editor with the given file. + * + * @param file_path File to edit. + * @return true on success, false on error. + */ + bool run(const std::string& file_path); + +private: + + static std::string findCommand( + const std::string& env_var, + const std::string& override); + + static size_t tokenize(string_vec_t& tokens, const std::string& str); + + /** + * Filename placeholder that gets replaced with an actual file name. + */ + static const std::string sFilenameMarker; + + /** + * Setting that can specify the editor command. + */ + static const std::string sSetting; + + + std::string mArgs; + LLProcessLauncher mProcess; +}; + +#endif // LL_LLEXTERNALEDITOR_H diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index d22950cad3..2471da9da5 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -399,84 +399,6 @@ void LLFace::updateCenterAgent() } } -void LLFace::renderForSelect(U32 data_mask) -{ - if(mDrawablep.isNull() || mVertexBuffer.isNull()) - { - return; - } - - LLSpatialGroup* group = mDrawablep->getSpatialGroup(); - if (!group || group->isState(LLSpatialGroup::GEOM_DIRTY)) - { - return; - } - - if (mVObjp->mGLName) - { - S32 name = mVObjp->mGLName; - - LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name); -#if 0 // *FIX: Postponing this fix until we have texcoord pick info... - if (mTEOffset != -1) - { - color.mV[VALPHA] = (U8)(getTextureEntry()->getColor().mV[VALPHA] * 255.f); - } -#endif - glColor4ubv(color.mV); - - if (!getPool()) - { - switch (getPoolType()) - { - case LLDrawPool::POOL_ALPHA: - gGL.getTexUnit(0)->bind(getTexture()); - break; - default: - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - break; - } - } - - mVertexBuffer->setBuffer(data_mask); -#if !LL_RELEASE_FOR_DOWNLOAD - LLGLState::checkClientArrays("", data_mask); -#endif - if (mTEOffset != -1) - { - // mask off high 4 bits (16 total possible faces) - color.mV[0] &= 0x0f; - color.mV[0] |= (mTEOffset & 0x0f) << 4; - glColor4ubv(color.mV); - } - - if (mIndicesCount) - { - if (isState(GLOBAL)) - { - if (mDrawablep->getVOVolume()) - { - glPushMatrix(); - glMultMatrixf((float*) mDrawablep->getRegion()->mRenderMatrix.mMatrix); - mVertexBuffer->draw(LLRender::TRIANGLES, mIndicesCount, mIndicesIndex); - glPopMatrix(); - } - else - { - mVertexBuffer->draw(LLRender::TRIANGLES, mIndicesCount, mIndicesIndex); - } - } - else - { - glPushMatrix(); - glMultMatrixf((float*)getRenderMatrix().mMatrix); - mVertexBuffer->draw(LLRender::TRIANGLES, mIndicesCount, mIndicesIndex); - glPopMatrix(); - } - } - } -} - void LLFace::renderSelected(LLViewerTexture *imagep, const LLColor4& color) { if (mDrawablep->getSpatialGroup() == NULL) diff --git a/indra/newview/llface.h b/indra/newview/llface.h index 0166e45bee..6c941bd092 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -173,7 +173,6 @@ public: void updateCenterAgent(); // Update center when xform has changed. void renderSelectedUV(); - void renderForSelect(U32 data_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0); void renderSelected(LLViewerTexture *image, const LLColor4 &color); F32 getKey() const { return mDistance; } diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index a09c0ea0f8..92a3b9b2f5 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -1086,14 +1086,22 @@ LLSD LLFastTimerView::analyzePerformanceLogDefault(std::istream& is) //static void LLFastTimerView::doAnalysisDefault(std::string baseline, std::string target, std::string output) { + // Open baseline and current target, exit if one is inexistent + std::ifstream base_is(baseline.c_str()); + std::ifstream target_is(target.c_str()); + if (!base_is.is_open() || !target_is.is_open()) + { + llwarns << "'-analyzeperformance' error : baseline or current target file inexistent" << llendl; + base_is.close(); + target_is.close(); + return; + } //analyze baseline - std::ifstream base_is(baseline.c_str()); LLSD base = analyzePerformanceLogDefault(base_is); base_is.close(); //analyze current - std::ifstream target_is(target.c_str()); LLSD current = analyzePerformanceLogDefault(target_is); target_is.close(); @@ -1154,15 +1162,15 @@ LLSD LLFastTimerView::analyzeMetricPerformanceLog(std::istream& is) { std::string label = iter->first; - LLMetricPerformanceTester* tester = LLMetricPerformanceTester::getTester(iter->second["Name"].asString()) ; + LLMetricPerformanceTesterBasic* tester = LLMetricPerformanceTesterBasic::getTester(iter->second["Name"].asString()) ; if(tester) { ret[label]["Name"] = iter->second["Name"] ; - S32 num_of_strings = tester->getNumOfMetricStrings() ; - for(S32 index = 0 ; index < num_of_strings ; index++) + S32 num_of_metrics = tester->getNumberOfMetrics() ; + for(S32 index = 0 ; index < num_of_metrics ; index++) { - ret[label][ tester->getMetricString(index) ] = iter->second[ tester->getMetricString(index) ] ; + ret[label][ tester->getMetricName(index) ] = iter->second[ tester->getMetricName(index) ] ; } } } @@ -1172,20 +1180,43 @@ LLSD LLFastTimerView::analyzeMetricPerformanceLog(std::istream& is) } //static +void LLFastTimerView::outputAllMetrics() +{ + if (LLMetricPerformanceTesterBasic::hasMetricPerformanceTesters()) + { + for (LLMetricPerformanceTesterBasic::name_tester_map_t::iterator iter = LLMetricPerformanceTesterBasic::sTesterMap.begin(); + iter != LLMetricPerformanceTesterBasic::sTesterMap.end(); ++iter) + { + LLMetricPerformanceTesterBasic* tester = ((LLMetricPerformanceTesterBasic*)iter->second); + tester->outputTestResults(); + } + } +} + +//static void LLFastTimerView::doAnalysisMetrics(std::string baseline, std::string target, std::string output) { - if(!LLMetricPerformanceTester::hasMetricPerformanceTesters()) + if(!LLMetricPerformanceTesterBasic::hasMetricPerformanceTesters()) { return ; } - //analyze baseline + // Open baseline and current target, exit if one is inexistent std::ifstream base_is(baseline.c_str()); + std::ifstream target_is(target.c_str()); + if (!base_is.is_open() || !target_is.is_open()) + { + llwarns << "'-analyzeperformance' error : baseline or current target file inexistent" << llendl; + base_is.close(); + target_is.close(); + return; + } + + //analyze baseline LLSD base = analyzeMetricPerformanceLog(base_is); base_is.close(); //analyze current - std::ifstream target_is(target.c_str()); LLSD current = analyzeMetricPerformanceLog(target_is); target_is.close(); @@ -1193,10 +1224,10 @@ void LLFastTimerView::doAnalysisMetrics(std::string baseline, std::string target std::ofstream os(output.c_str()); os << "Label, Metric, Base(B), Target(T), Diff(T-B), Percentage(100*T/B)\n"; - for(LLMetricPerformanceTester::name_tester_map_t::iterator iter = LLMetricPerformanceTester::sTesterMap.begin() ; - iter != LLMetricPerformanceTester::sTesterMap.end() ; ++iter) + for(LLMetricPerformanceTesterBasic::name_tester_map_t::iterator iter = LLMetricPerformanceTesterBasic::sTesterMap.begin() ; + iter != LLMetricPerformanceTesterBasic::sTesterMap.end() ; ++iter) { - LLMetricPerformanceTester* tester = ((LLMetricPerformanceTester*)iter->second) ; + LLMetricPerformanceTesterBasic* tester = ((LLMetricPerformanceTesterBasic*)iter->second) ; tester->analyzePerformance(&os, &base, ¤t) ; } diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h index 3788897cec..1a54a53f09 100644 --- a/indra/newview/llfasttimerview.h +++ b/indra/newview/llfasttimerview.h @@ -37,6 +37,7 @@ public: static BOOL sAnalyzePerformance; + static void outputAllMetrics(); static void doAnalysis(std::string baseline, std::string target, std::string output); private: diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 3981b887ad..0b17d64eb0 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -368,14 +368,15 @@ LLFavoritesBarCtrl::Params::Params() LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) : LLUICtrl(p), mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), - mPopupMenuHandle(), - mInventoryItemsPopupMenuHandle(), + mOverflowMenuHandle(), + mContextMenuHandle(), mImageDragIndication(p.image_drag_indication), mShowDragMarker(FALSE), mLandingTab(NULL), mLastTab(NULL), mTabsHighlightEnabled(TRUE) , mUpdateDropDownItems(true) +, mRestoreOverflowMenu(false) { // Register callback for menus with current registrar (will be parent panel's registrar) LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Favorites.DoToSelected", @@ -402,8 +403,8 @@ LLFavoritesBarCtrl::~LLFavoritesBarCtrl() { gInventory.removeObserver(this); - LLView::deleteViewByHandle(mPopupMenuHandle); - LLView::deleteViewByHandle(mInventoryItemsPopupMenuHandle); + LLView::deleteViewByHandle(mOverflowMenuHandle); + LLView::deleteViewByHandle(mContextMenuHandle); } BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, @@ -414,6 +415,9 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, { *accept = ACCEPT_NO; + LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); + if (LLToolDragAndDrop::SOURCE_AGENT != source && LLToolDragAndDrop::SOURCE_LIBRARY != source) return FALSE; + switch (cargo_type) { @@ -517,7 +521,7 @@ void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) gInventory.saveItemsOrder(mItems); - LLToggleableMenu* menu = (LLToggleableMenu*) mPopupMenuHandle.get(); + LLToggleableMenu* menu = (LLToggleableMenu*) mOverflowMenuHandle.get(); if (menu && menu->getVisible()) { @@ -603,6 +607,15 @@ void LLFavoritesBarCtrl::changed(U32 mask) } 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) + { + (*i)->getSLURL(); + } updateButtons(); } } @@ -773,7 +786,7 @@ void LLFavoritesBarCtrl::updateButtons() mChevronButton->setVisible(TRUE); } // Update overflow menu - LLToggleableMenu* overflow_menu = static_cast <LLToggleableMenu*> (mPopupMenuHandle.get()); + LLToggleableMenu* overflow_menu = static_cast <LLToggleableMenu*> (mOverflowMenuHandle.get()); if (overflow_menu && overflow_menu->getVisible()) { overflow_menu->setVisible(FALSE); @@ -847,7 +860,7 @@ BOOL LLFavoritesBarCtrl::postBuild() menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); } menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); - mInventoryItemsPopupMenuHandle = menu->getHandle(); + mContextMenuHandle = menu->getHandle(); return TRUE; } @@ -878,7 +891,7 @@ BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &it void LLFavoritesBarCtrl::showDropDownMenu() { - if (mPopupMenuHandle.isDead()) + if (mOverflowMenuHandle.isDead()) { LLToggleableMenu::Params menu_p; menu_p.name("favorites menu"); @@ -889,10 +902,10 @@ void LLFavoritesBarCtrl::showDropDownMenu() menu_p.preferred_width = DROP_DOWN_MENU_WIDTH; LLToggleableMenu* menu = LLUICtrlFactory::create<LLFavoriteLandmarkToggleableMenu>(menu_p); - mPopupMenuHandle = menu->getHandle(); + mOverflowMenuHandle = menu->getHandle(); } - LLToggleableMenu* menu = (LLToggleableMenu*)mPopupMenuHandle.get(); + LLToggleableMenu* menu = (LLToggleableMenu*)mOverflowMenuHandle.get(); if (menu) { @@ -970,11 +983,19 @@ void LLFavoritesBarCtrl::onButtonRightClick( LLUUID item_id,LLView* fav_button,S { mSelectedItemID = item_id; - LLMenuGL* menu = (LLMenuGL*)mInventoryItemsPopupMenuHandle.get(); + 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. @@ -1079,8 +1100,8 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) // Pop-up the overflow menu again (it gets hidden whenever the user clicks a context menu item). // See EXT-4217 and STORM-207. - LLToggleableMenu* menu = (LLToggleableMenu*) mPopupMenuHandle.get(); - if (menu && !menu->getVisible()) + LLToggleableMenu* menu = (LLToggleableMenu*) mOverflowMenuHandle.get(); + if (mRestoreOverflowMenu && menu && !menu->getVisible()) { showDropDownMenu(); } @@ -1146,11 +1167,11 @@ void LLFavoritesBarCtrl::pastFromClipboard() const 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) - // mInventoryItemsPopupMenuHandle.get() - is a pop-up menu (of items) in already opened dropdown menu. + // 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*)mInventoryItemsPopupMenuHandle.get(); + LLMenuGL* menu = (LLMenuGL*)mContextMenuHandle.get(); if(menu && menu->getVisible()) { menu->setVisible(FALSE); diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index 37645523f6..1a28731c4f 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -91,13 +91,14 @@ protected: void showDropDownMenu(); - LLHandle<LLView> mPopupMenuHandle; - LLHandle<LLView> mInventoryItemsPopupMenuHandle; + LLHandle<LLView> mOverflowMenuHandle; + LLHandle<LLView> mContextMenuHandle; LLUUID mFavoriteFolderId; const LLFontGL *mFont; S32 mFirstDropDownItem; bool mUpdateDropDownItems; + bool mRestoreOverflowMenu; LLUUID mSelectedItemID; diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index c14be89641..f0840774bd 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -33,6 +33,7 @@ #include "lldir.h" #include "llframetimer.h" #include "lltrans.h" +#include "llviewercontrol.h" #include "llwindow.h" // beforeDialog() #if LL_SDL @@ -104,6 +105,20 @@ LLFilePicker::~LLFilePicker() // nothing } +// utility function to check if access to local file system via file browser +// is enabled and if not, tidy up and indicate we're not allowed to do this. +bool LLFilePicker::check_local_file_access_enabled() +{ + // if local file browsing is turned off, return without opening dialog + bool local_file_system_browsing_enabled = gSavedSettings.getBOOL("LocalFileSystemBrowsingEnabled"); + if ( ! local_file_system_browsing_enabled ) + { + mFiles.clear(); + return false; + } + + return true; +} const std::string LLFilePicker::getFirstFile() { @@ -203,6 +218,12 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter) } BOOL success = FALSE; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + // don't provide default file selection mFilesW[0] = '\0'; @@ -241,6 +262,12 @@ BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter) } BOOL success = FALSE; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + // don't provide default file selection mFilesW[0] = '\0'; @@ -304,6 +331,12 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename) } BOOL success = FALSE; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + mOFN.lpstrFile = mFilesW; if (!filename.empty()) { @@ -581,6 +614,12 @@ OSStatus LLFilePicker::doNavChooseDialog(ELoadFilter filter) NavDialogRef navRef = NULL; NavReplyRecord navReply; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + memset(&navReply, 0, sizeof(navReply)); // NOTE: we are passing the address of a local variable here. @@ -809,6 +848,12 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter) BOOL success = FALSE; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + OSStatus error = noErr; reset(); @@ -845,6 +890,12 @@ BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter) BOOL success = FALSE; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + OSStatus error = noErr; reset(); @@ -876,6 +927,12 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename) BOOL success = FALSE; OSStatus error = noErr; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + reset(); mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; @@ -1100,6 +1157,12 @@ BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename { BOOL rtn = FALSE; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + gViewerWindow->mWindow->beforeDialog(); reset(); @@ -1189,6 +1252,12 @@ BOOL LLFilePicker::getOpenFile( ELoadFilter filter ) { BOOL rtn = FALSE; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + gViewerWindow->mWindow->beforeDialog(); reset(); @@ -1233,6 +1302,12 @@ BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter ) { BOOL rtn = FALSE; + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + gViewerWindow->mWindow->beforeDialog(); reset(); @@ -1263,6 +1338,13 @@ BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter ) BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename ) { + // if local file browsing is turned off, return without opening dialog + // (Even though this is a stub, I think we still should not return anything at all) + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + reset(); llinfos << "getSaveFile suggested filename is [" << filename @@ -1277,6 +1359,13 @@ BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename BOOL LLFilePicker::getOpenFile( ELoadFilter filter ) { + // if local file browsing is turned off, return without opening dialog + // (Even though this is a stub, I think we still should not return anything at all) + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + reset(); // HACK: Static filenames for 'open' until we implement filepicker @@ -1295,6 +1384,13 @@ BOOL LLFilePicker::getOpenFile( ELoadFilter filter ) BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter ) { + // if local file browsing is turned off, return without opening dialog + // (Even though this is a stub, I think we still should not return anything at all) + if ( check_local_file_access_enabled() == false ) + { + return FALSE; + } + reset(); return FALSE; } diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index 5819ac4fd8..596bfa3e69 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -140,6 +140,10 @@ private: //FILENAME_BUFFER_SIZE = 65536 FILENAME_BUFFER_SIZE = 65000 }; + + // utility function to check if access to local file system via file browser + // is enabled and if not, tidy up and indicate we're not allowed to do this. + bool check_local_file_access_enabled(); #if LL_WINDOWS OPENFILENAMEW mOFN; // for open and save dialogs diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp index b08c113923..4c17199895 100644 --- a/indra/newview/llfirstuse.cpp +++ b/indra/newview/llfirstuse.cpp @@ -100,9 +100,16 @@ void LLFirstUse::useSandbox() void LLFirstUse::notUsingDestinationGuide(bool enable) { // not doing this yet - //firstUseNotification("FirstNotUseDestinationGuide", enable, "HintDestinationGuide", LLSD(), LLSD().with("target", "dest_guide_btn").with("direction", "left")); + firstUseNotification("FirstNotUseDestinationGuide", enable, "HintDestinationGuide", LLSD(), LLSD().with("target", "dest_guide_btn").with("direction", "top")); } +void LLFirstUse::notUsingAvatarPicker(bool enable) +{ + // not doing this yet + firstUseNotification("FirstNotUseAvatarPicker", enable, "HintAvatarPicker", LLSD(), LLSD().with("target", "avatar_picker_btn").with("direction", "top")); +} + + // static void LLFirstUse::notUsingSidePanel(bool enable) { @@ -113,7 +120,15 @@ void LLFirstUse::notUsingSidePanel(bool enable) // static void LLFirstUse::notMoving(bool enable) { + // fire off 2 notifications and rely on filtering to select the relevant one firstUseNotification("FirstNotMoving", enable, "HintMove", LLSD(), LLSD().with("target", "move_btn").with("direction", "top")); + firstUseNotification("FirstNotMoving", enable, "HintMoveArrows", LLSD(), LLSD().with("target", "bottom_tray").with("direction", "top").with("hint_image", "arrow_keys.png").with("down_arrow", "")); +} + +// static +void LLFirstUse::viewPopup(bool enable) +{ + firstUseNotification("FirstViewPopup", enable, "HintView", LLSD(), LLSD().with("target", "view_popup").with("direction", "right")); } // static diff --git a/indra/newview/llfirstuse.h b/indra/newview/llfirstuse.h index 3b7ff6383b..81659988e6 100644 --- a/indra/newview/llfirstuse.h +++ b/indra/newview/llfirstuse.h @@ -87,8 +87,10 @@ public: static void otherAvatarChatFirst(bool enable = true); static void sit(bool enable = true); static void notUsingDestinationGuide(bool enable = true); + static void notUsingAvatarPicker(bool enable = true); static void notUsingSidePanel(bool enable = true); static void notMoving(bool enable = true); + static void viewPopup(bool enable = true); static void newInventory(bool enable = true); static void receiveLindens(bool enable = true); static void setDisplayName(bool enable = true); diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index 135137069c..2873bc0059 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -213,7 +213,7 @@ LLSD LLFloaterAbout::getInfo() info["VIEWER_VERSION_STR"] = LLVersionInfo::getVersion(); info["BUILD_DATE"] = __DATE__; info["BUILD_TIME"] = __TIME__; - info["CHANNEL"] = gSavedSettings.getString("VersionChannelName"); + info["CHANNEL"] = LLVersionInfo::getChannel(); info["VIEWER_RELEASE_NOTES_URL"] = get_viewer_release_notes_url(); @@ -272,7 +272,7 @@ LLSD LLFloaterAbout::getInfo() } // TODO: Implement media plugin version query - info["QT_WEBKIT_VERSION"] = "4.6 (version number hard-coded)"; + info["QT_WEBKIT_VERSION"] = "4.7.1 (version number hard-coded)"; if (gPacketsIn > 0) { @@ -291,7 +291,7 @@ static std::string get_viewer_release_notes_url() std::string url = LLTrans::getString("RELEASE_NOTES_BASE_URL"); if (! LLStringUtil::endsWith(url, "/")) url += "/"; - url += gSavedSettings.getString("VersionChannelName") + "/"; + url += LLVersionInfo::getChannel() + "/"; url += LLVersionInfo::getShortVersion(); return LLWeb::escapeURL(url); } diff --git a/indra/newview/llfloaterbuycurrency.cpp b/indra/newview/llfloaterbuycurrency.cpp index 58c79fdf15..e21a8594bc 100644 --- a/indra/newview/llfloaterbuycurrency.cpp +++ b/indra/newview/llfloaterbuycurrency.cpp @@ -267,17 +267,23 @@ void LLFloaterBuyCurrencyUI::onClickBuy() { mManager.buy(getString("buy_currency")); updateUI(); + // Update L$ balance + LLStatusBar::sendMoneyBalanceRequest(); } void LLFloaterBuyCurrencyUI::onClickCancel() { closeFloater(); + // Update L$ balance + LLStatusBar::sendMoneyBalanceRequest(); } void LLFloaterBuyCurrencyUI::onClickErrorWeb() { LLWeb::loadURLExternal(mManager.errorURI()); closeFloater(); + // Update L$ balance + LLStatusBar::sendMoneyBalanceRequest(); } // static diff --git a/indra/newview/llfloaterbuycurrencyhtml.cpp b/indra/newview/llfloaterbuycurrencyhtml.cpp index bde620d965..013cf74c7b 100644 --- a/indra/newview/llfloaterbuycurrencyhtml.cpp +++ b/indra/newview/llfloaterbuycurrencyhtml.cpp @@ -82,7 +82,7 @@ void LLFloaterBuyCurrencyHTML::navigateToFinalURL() LLStringUtil::format( buy_currency_url, replace ); // write final URL to debug console - llinfos << "Buy currency HTML prased URL is " << buy_currency_url << llendl; + llinfos << "Buy currency HTML parsed URL is " << buy_currency_url << llendl; // kick off the navigation mBrowser->navigateTo( buy_currency_url, "text/html" ); @@ -105,7 +105,7 @@ void LLFloaterBuyCurrencyHTML::handleMediaEvent( LLPluginClassMedia* self, EMedi // void LLFloaterBuyCurrencyHTML::onClose( bool app_quitting ) { - // update L$ balanace one more time + // Update L$ balance one more time LLStatusBar::sendMoneyBalanceRequest(); destroy(); diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp index ad24c6534a..1dfa904a19 100644 --- a/indra/newview/llfloatercamera.cpp +++ b/indra/newview/llfloatercamera.cpp @@ -40,6 +40,8 @@ #include "lltoolmgr.h" #include "lltoolfocus.h" #include "llslider.h" +#include "llfirstuse.h" +#include "llhints.h" static LLDefaultChildRegistry::Register<LLPanelCameraItem> r("panel_camera_item"); @@ -73,6 +75,8 @@ protected: void onZoomPlusHeldDown(); void onZoomMinusHeldDown(); void onSliderValueChanged(); + void onCameraTrack(); + void onCameraRotate(); F32 getOrbitRate(F32 time); private: @@ -162,6 +166,8 @@ LLPanelCameraZoom::LLPanelCameraZoom() mCommitCallbackRegistrar.add("Zoom.minus", boost::bind(&LLPanelCameraZoom::onZoomMinusHeldDown, this)); mCommitCallbackRegistrar.add("Zoom.plus", boost::bind(&LLPanelCameraZoom::onZoomPlusHeldDown, this)); mCommitCallbackRegistrar.add("Slider.value_changed", boost::bind(&LLPanelCameraZoom::onSliderValueChanged, this)); + mCommitCallbackRegistrar.add("Camera.track", boost::bind(&LLPanelCameraZoom::onCameraTrack, this)); + mCommitCallbackRegistrar.add("Camera.rotate", boost::bind(&LLPanelCameraZoom::onCameraRotate, this)); } BOOL LLPanelCameraZoom::postBuild() @@ -198,6 +204,18 @@ void LLPanelCameraZoom::onZoomMinusHeldDown() gAgentCamera.setOrbitOutKey(getOrbitRate(time)); } +void LLPanelCameraZoom::onCameraTrack() +{ + // EXP-202 when camera panning activated, remove the hint + LLFirstUse::viewPopup( false ); +} + +void LLPanelCameraZoom::onCameraRotate() +{ + // EXP-202 when camera rotation activated, remove the hint + LLFirstUse::viewPopup( false ); +} + F32 LLPanelCameraZoom::getOrbitRate(F32 time) { if( time < NUDGE_TIME ) @@ -294,6 +312,8 @@ LLFloaterCamera* LLFloaterCamera::findInstance() void LLFloaterCamera::onOpen(const LLSD& key) { + LLFirstUse::viewPopup(); + LLButton *anchor_panel = LLBottomTray::getInstance()->getChild<LLButton>("camera_btn"); setDockControl(new LLDockControl( @@ -336,6 +356,7 @@ LLFloaterCamera::LLFloaterCamera(const LLSD& val) mCurrMode(CAMERA_CTRL_MODE_PAN), mPrevMode(CAMERA_CTRL_MODE_PAN) { + LLHints::registerHintTarget("view_popup", LLView::getHandle()); } // virtual @@ -343,6 +364,7 @@ BOOL LLFloaterCamera::postBuild() { setIsChrome(TRUE); setTitleVisible(TRUE); // restore title visibility after chrome applying + updateTransparency(TT_ACTIVE); // force using active floater transparency (STORM-730) mRotate = getChild<LLJoystickCameraRotate>(ORBIT); mZoom = findChild<LLPanelCameraZoom>(ZOOM); diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp index 69f1774ff8..659e52271a 100644 --- a/indra/newview/llfloatercolorpicker.cpp +++ b/indra/newview/llfloatercolorpicker.cpp @@ -472,6 +472,12 @@ void LLFloaterColorPicker::onMouseCaptureLost() setMouseDownInLumRegion(FALSE); } +F32 LLFloaterColorPicker::getSwatchTransparency() +{ + // If the floater is focused, don't apply its alpha to the color swatch (STORM-676). + return getTransparencyType() == TT_ACTIVE ? 1.f : LLFloater::getCurrentTransparency(); +} + ////////////////////////////////////////////////////////////////////////////// // void LLFloaterColorPicker::draw() @@ -533,8 +539,10 @@ void LLFloaterColorPicker::draw() // base floater stuff LLFloater::draw (); + const F32 alpha = getSwatchTransparency(); + // draw image for RGB area (not really RGB but you'll see what I mean... - gl_draw_image ( mRGBViewerImageLeft, mRGBViewerImageTop - mRGBViewerImageHeight, mRGBImage, LLColor4::white ); + gl_draw_image ( mRGBViewerImageLeft, mRGBViewerImageTop - mRGBViewerImageHeight, mRGBImage, LLColor4::white % alpha); // update 'cursor' into RGB Section S32 xPos = ( S32 ) ( ( F32 )mRGBViewerImageWidth * getCurH () ) - 8; @@ -556,7 +564,7 @@ void LLFloaterColorPicker::draw() mRGBViewerImageTop - mRGBViewerImageHeight, mRGBViewerImageLeft + mRGBViewerImageWidth + 1, mRGBViewerImageTop, - LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ), + LLColor4 ( 0.0f, 0.0f, 0.0f, alpha ), FALSE ); // draw luminance slider @@ -569,7 +577,7 @@ void LLFloaterColorPicker::draw() mLumRegionTop - mLumRegionHeight + y, mLumRegionLeft + mLumRegionWidth, mLumRegionTop - mLumRegionHeight + y - 1, - LLColor4 ( rValSlider, gValSlider, bValSlider, 1.0f ) ); + LLColor4 ( rValSlider, gValSlider, bValSlider, alpha ) ); } @@ -594,7 +602,7 @@ void LLFloaterColorPicker::draw() mSwatchRegionTop - mSwatchRegionHeight, mSwatchRegionLeft + mSwatchRegionWidth, mSwatchRegionTop, - LLColor4 ( getCurR (), getCurG (), getCurB (), 1.0f ), + LLColor4 ( getCurR (), getCurG (), getCurB (), alpha ), TRUE ); // draw selected color swatch outline @@ -634,6 +642,7 @@ const LLColor4& LLFloaterColorPicker::getComplimentaryColor ( const LLColor4& ba void LLFloaterColorPicker::drawPalette () { S32 curEntry = 0; + const F32 alpha = getSwatchTransparency(); for ( S32 y = 0; y < numPaletteRows; ++y ) { @@ -648,7 +657,7 @@ void LLFloaterColorPicker::drawPalette () // draw palette entry color if ( mPalette [ curEntry ] ) { - gl_rect_2d ( x1 + 2, y1 - 2, x2 - 2, y2 + 2, *mPalette [ curEntry++ ], TRUE ); + gl_rect_2d ( x1 + 2, y1 - 2, x2 - 2, y2 + 2, *mPalette [ curEntry++ ] % alpha, TRUE ); gl_rect_2d ( x1 + 1, y1 - 1, x2 - 1, y2 + 1, LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ), FALSE ); } } diff --git a/indra/newview/llfloatercolorpicker.h b/indra/newview/llfloatercolorpicker.h index 110fa43b9c..8e387c4f7c 100644 --- a/indra/newview/llfloatercolorpicker.h +++ b/indra/newview/llfloatercolorpicker.h @@ -55,6 +55,7 @@ class LLFloaterColorPicker virtual BOOL handleMouseUp ( S32 x, S32 y, MASK mask ); virtual BOOL handleHover ( S32 x, S32 y, MASK mask ); virtual void onMouseCaptureLost(); + virtual F32 getSwatchTransparency(); // implicit methods void createUI (); diff --git a/indra/newview/llfloaterevent.cpp b/indra/newview/llfloaterevent.cpp index 0b5ac8e798..a6dafda3e6 100644 --- a/indra/newview/llfloaterevent.cpp +++ b/indra/newview/llfloaterevent.cpp @@ -117,8 +117,3 @@ void LLFloaterEvent::setEventID(const U32 event_id) } } - -void LLFloaterEvent::draw() -{ - LLPanel::draw(); -} diff --git a/indra/newview/llfloaterevent.h b/indra/newview/llfloaterevent.h index b1963309da..ed90055d95 100644 --- a/indra/newview/llfloaterevent.h +++ b/indra/newview/llfloaterevent.h @@ -43,7 +43,6 @@ public: /*virtual*/ ~LLFloaterEvent(); /*virtual*/ BOOL postBuild(); - /*virtual*/ void draw(); void setEventID(const U32 event_id); diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp index 662e1c4f42..a34e0353ec 100644 --- a/indra/newview/llfloatergodtools.cpp +++ b/indra/newview/llfloatergodtools.cpp @@ -209,13 +209,6 @@ void LLFloaterGodTools::processRegionInfo(LLMessageSystem* msg) llassert(msg); if (!msg) return; - LLHost host = msg->getSender(); - if (host != gAgent.getRegionHost()) - { - // update is for a different region than the one we're in - return; - } - //const S32 SIM_NAME_BUF = 256; U32 region_flags; U8 sim_access; @@ -233,6 +226,8 @@ void LLFloaterGodTools::processRegionInfo(LLMessageSystem* msg) S32 redirect_grid_y; LLUUID cache_id; + LLHost host = msg->getSender(); + msg->getStringFast(_PREHASH_RegionInfo, _PREHASH_SimName, sim_name); msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_EstateID, estate_id); msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_ParentEstateID, parent_estate_id); @@ -242,6 +237,15 @@ void LLFloaterGodTools::processRegionInfo(LLMessageSystem* msg) msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_ObjectBonusFactor, object_bonus_factor); msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_BillableFactor, billable_factor); msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_WaterHeight, water_height); + + if (host != gAgent.getRegionHost()) + { + // Update is for a different region than the one we're in. + // Just check for a waterheight change. + LLWorld::getInstance()->waterHeightRegionInfo(sim_name, water_height); + return; + } + msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainRaiseLimit, terrain_raise_limit); msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainLowerLimit, terrain_lower_limit); msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_PricePerMeter, price_per_meter); diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp index 234a09d157..d84364a68a 100644 --- a/indra/newview/llfloatergroups.cpp +++ b/indra/newview/llfloatergroups.cpp @@ -41,6 +41,7 @@ #include "llbutton.h" #include "llgroupactions.h" #include "llscrolllistctrl.h" +#include "llstartup.h" #include "lltextbox.h" #include "lluictrlfactory.h" #include "lltrans.h" @@ -171,7 +172,7 @@ void LLPanelGroups::reset() group_list->operateOnAll(LLCtrlListInterface::OP_DELETE); } getChild<LLUICtrl>("groupcount")->setTextArg("[COUNT]", llformat("%d",gAgent.mGroups.count())); - getChild<LLUICtrl>("groupcount")->setTextArg("[MAX]", llformat("%d",MAX_AGENT_GROUPS)); + getChild<LLUICtrl>("groupcount")->setTextArg("[MAX]", llformat("%d",gMaxAgentGroups)); init_group_list(getChild<LLScrollListCtrl>("group list"), gAgent.getGroupID()); enableButtons(); @@ -182,7 +183,7 @@ BOOL LLPanelGroups::postBuild() childSetCommitCallback("group list", onGroupList, this); getChild<LLUICtrl>("groupcount")->setTextArg("[COUNT]", llformat("%d",gAgent.mGroups.count())); - getChild<LLUICtrl>("groupcount")->setTextArg("[MAX]", llformat("%d",MAX_AGENT_GROUPS)); + getChild<LLUICtrl>("groupcount")->setTextArg("[MAX]", llformat("%d",gMaxAgentGroups)); LLScrollListCtrl *list = getChild<LLScrollListCtrl>("group list"); if (list) diff --git a/indra/newview/llfloaterhelpbrowser.cpp b/indra/newview/llfloaterhelpbrowser.cpp index cec98e9992..a650886d89 100644 --- a/indra/newview/llfloaterhelpbrowser.cpp +++ b/indra/newview/llfloaterhelpbrowser.cpp @@ -132,9 +132,10 @@ void LLFloaterHelpBrowser::onClickOpenWebBrowser(void* user_data) void LLFloaterHelpBrowser::openMedia(const std::string& media_url) { - mBrowser->setHomePageUrl(media_url); - //mBrowser->navigateTo("data:text/html;charset=utf-8,I'd really love to be going to:<br><b>" + media_url + "</b>"); // tofu HACK for debugging =:) - mBrowser->navigateTo(media_url); + // explicitly make the media mime type for this floater since it will + // only ever display one type of content (Web). + mBrowser->setHomePageUrl(media_url, "text/html"); + mBrowser->navigateTo(media_url, "text/html"); setCurrentURL(media_url); } diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp index a1d291fea6..1b94d8cbcd 100644 --- a/indra/newview/llfloatermap.cpp +++ b/indra/newview/llfloatermap.cpp @@ -43,6 +43,8 @@ #include "lldraghandle.h" #include "lltextbox.h" #include "llviewermenu.h" +#include "llfloaterworldmap.h" +#include "llagent.h" // // Constants @@ -81,7 +83,6 @@ LLFloaterMap::~LLFloaterMap() BOOL LLFloaterMap::postBuild() { mMap = getChild<LLNetMap>("Net Map"); - mMap->setScale(gSavedSettings.getF32("MiniMapScale")); mMap->setToolTipMsg(getString("ToolTipMsg")); sendChildToBack(mMap); @@ -122,11 +123,36 @@ BOOL LLFloaterMap::postBuild() return TRUE; } -BOOL LLFloaterMap::handleDoubleClick( S32 x, S32 y, MASK mask ) +BOOL LLFloaterMap::handleDoubleClick(S32 x, S32 y, MASK mask) { // If floater is minimized, minimap should be shown on doubleclick (STORM-299) - std::string floater_to_show = this->isMinimized() ? "mini_map" : "world_map"; - LLFloaterReg::showInstance(floater_to_show); + if (isMinimized()) + { + setMinimized(FALSE); + return TRUE; + } + + LLVector3d pos_global = mMap->viewPosToGlobal(x, y); + + // If we're not tracking a beacon already, double-click will set one + if (!LLTracker::isTracking(NULL)) + { + LLFloaterWorldMap* world_map = LLFloaterWorldMap::getInstance(); + if (world_map) + { + world_map->trackLocation(pos_global); + } + } + + if (gSavedSettings.getBOOL("DoubleClickTeleport")) + { + // If DoubleClickTeleport is on, double clicking the minimap will teleport there + gAgent.teleportViaLocationLookAt(pos_global); + } + else + { + LLFloaterReg::showInstance("world_map"); + } return TRUE; } @@ -261,7 +287,16 @@ void LLFloaterMap::handleZoom(const LLSD& userdata) std::string level = userdata.asString(); F32 scale = 0.0f; - if (level == std::string("close")) + if (level == std::string("default")) + { + LLControlVariable *pvar = gSavedSettings.getControl("MiniMapScale"); + if(pvar) + { + pvar->resetToDefault(); + scale = gSavedSettings.getF32("MiniMapScale"); + } + } + else if (level == std::string("close")) scale = LLNetMap::MAP_SCALE_MAX; else if (level == std::string("medium")) scale = LLNetMap::MAP_SCALE_MID; @@ -269,7 +304,6 @@ void LLFloaterMap::handleZoom(const LLSD& userdata) scale = LLNetMap::MAP_SCALE_MIN; if (scale != 0.0f) { - gSavedSettings.setF32("MiniMapScale", scale ); mMap->setScale(scale); } } diff --git a/indra/newview/llfloatermediabrowser.cpp b/indra/newview/llfloatermediabrowser.cpp index d20092e344..7a670dd90c 100644 --- a/indra/newview/llfloatermediabrowser.cpp +++ b/indra/newview/llfloatermediabrowser.cpp @@ -306,17 +306,14 @@ void LLFloaterMediaBrowser::setCurrentURL(const std::string& url) { mCurrentURL = url; - // redirects will navigate momentarily to about:blank, don't add to history - if (mCurrentURL != "about:blank") - { - mAddressCombo->remove(mCurrentURL); - mAddressCombo->add(mCurrentURL); - mAddressCombo->selectByValue(mCurrentURL); + mAddressCombo->remove(mCurrentURL); + mAddressCombo->add(mCurrentURL); + mAddressCombo->selectByValue(mCurrentURL); + + // Serialize url history + LLURLHistory::removeURL("browser", mCurrentURL); + LLURLHistory::addURL("browser", mCurrentURL); - // Serialize url history - LLURLHistory::removeURL("browser", mCurrentURL); - LLURLHistory::addURL("browser", mCurrentURL); - } getChildView("back")->setEnabled(mBrowser->canNavigateBack()); getChildView("forward")->setEnabled(mBrowser->canNavigateForward()); getChildView("reload")->setEnabled(TRUE); @@ -334,8 +331,15 @@ void LLFloaterMediaBrowser::onClickRefresh(void* user_data) { LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - self->mAddressCombo->remove(0); - self->mBrowser->navigateTo(self->mCurrentURL); + if( self->mBrowser->getMediaPlugin() && self->mBrowser->getMediaPlugin()->pluginSupportsMediaBrowser()) + { + bool ignore_cache = true; + self->mBrowser->getMediaPlugin()->browse_reload( ignore_cache ); + } + else + { + self->mBrowser->navigateTo(self->mCurrentURL); + } } //static diff --git a/indra/newview/llfloaternotificationsconsole.cpp b/indra/newview/llfloaternotificationsconsole.cpp index 42dc60f9e0..29af81d64c 100644 --- a/indra/newview/llfloaternotificationsconsole.cpp +++ b/indra/newview/llfloaternotificationsconsole.cpp @@ -174,6 +174,7 @@ BOOL LLFloaterNotificationConsole::postBuild() // these are in the order of processing addChannel("Unexpired"); addChannel("Ignore"); + addChannel("VisibilityRules"); addChannel("Visible", true); // all the ones below attach to the Visible channel addChannel("Persistent"); diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp index e8e9f76912..dd0b1d999c 100644 --- a/indra/newview/llfloaterpostcard.cpp +++ b/indra/newview/llfloaterpostcard.cpp @@ -112,11 +112,14 @@ LLFloaterPostcard* LLFloaterPostcard::showFromSnapshot(LLImageJPEG *jpeg, LLView // Take the images from the caller // It's now our job to clean them up LLFloaterPostcard* instance = LLFloaterReg::showTypedInstance<LLFloaterPostcard>("postcard", LLSD(img->getID())); - - instance->mJPEGImage = jpeg; - instance->mViewerImage = img; - instance->mImageScale = image_scale; - instance->mPosTakenGlobal = pos_taken_global; + + if (instance) // may be 0 if we're in mouselook mode + { + instance->mJPEGImage = jpeg; + instance->mViewerImage = img; + instance->mImageScale = image_scale; + instance->mPosTakenGlobal = pos_taken_global; + } return instance; } @@ -128,6 +131,8 @@ void LLFloaterPostcard::draw() if(!isMinimized() && mViewerImage.notNull() && mJPEGImage.notNull()) { + // Force the texture to be 100% opaque when the floater is focused. + F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); LLRect rect(getRect()); // first set the max extents of our preview @@ -149,7 +154,7 @@ void LLFloaterPostcard::draw() } { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f)); + gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f) % alpha); rect.stretch(-1); } { @@ -164,7 +169,7 @@ void LLFloaterPostcard::draw() rect.getWidth(), rect.getHeight(), mViewerImage.get(), - LLColor4::white); + LLColor4::white % alpha); } glMatrixMode(GL_TEXTURE); glPopMatrix(); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 2bea3d37ff..8c9dfe435a 100644..100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -105,6 +105,7 @@ #include "llteleporthistorystorage.h" #include "lllogininstance.h" // to check if logged in yet +#include "llsdserialize.h" const F32 MAX_USER_FAR_CLIP = 512.f; const F32 MIN_USER_FAR_CLIP = 64.f; @@ -282,8 +283,12 @@ std::string LLFloaterPreference::sSkin = ""; LLFloaterPreference::LLFloaterPreference(const LLSD& key) : LLFloater(key), mGotPersonalInfo(false), - mOriginalIMViaEmail(false) + mOriginalIMViaEmail(false), + mLanguageChanged(false), + mDoubleClickActionDirty(false), + mFavoritesRecordMayExist(false) { + //Build Floater is now Called from LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreference>); static bool registered_dialog = false; @@ -320,14 +325,61 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.getUIColor", boost::bind(&LLFloaterPreference::getUIColor, this ,_1, _2)); mCommitCallbackRegistrar.add("Pref.MaturitySettings", boost::bind(&LLFloaterPreference::onChangeMaturity, this)); mCommitCallbackRegistrar.add("Pref.BlockList", boost::bind(&LLFloaterPreference::onClickBlockList, this)); - - sSkin = gSavedSettings.getString("SkinCurrent"); + sSkin = gSavedSettings.getString("SkinCurrent"); + + mCommitCallbackRegistrar.add("Pref.CommitDoubleClickChekbox", boost::bind(&LLFloaterPreference::onDoubleClickCheckBox, this, _1)); + mCommitCallbackRegistrar.add("Pref.CommitRadioDoubleClick", boost::bind(&LLFloaterPreference::onDoubleClickRadio, this)); + gSavedSettings.getControl("NameTagShowUsernames")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); gSavedSettings.getControl("NameTagShowFriends")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); gSavedSettings.getControl("UseDisplayNames")->getCommitSignal()->connect(boost::bind(&handleDisplayNamesOptionChanged, _2)); + + LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this ); +} + +void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType type ) +{ + if ( APT_PROPERTIES == type ) + { + const LLAvatarData* pAvatarData = static_cast<const LLAvatarData*>( pData ); + if( pAvatarData && gAgent.getID() == pAvatarData->avatar_id ) + { + storeAvatarProperties( pAvatarData ); + processProfileProperties( pAvatarData ); + } + } +} + +void LLFloaterPreference::storeAvatarProperties( const LLAvatarData* pAvatarData ) +{ + mAvatarProperties.avatar_id = gAgent.getID(); + mAvatarProperties.image_id = pAvatarData->image_id; + mAvatarProperties.fl_image_id = pAvatarData->fl_image_id; + mAvatarProperties.about_text = pAvatarData->about_text; + mAvatarProperties.fl_about_text = pAvatarData->fl_about_text; + mAvatarProperties.profile_url = pAvatarData->profile_url; + mAvatarProperties.flags = pAvatarData->flags; + mAvatarProperties.allow_publish = pAvatarData->flags & AVATAR_ALLOW_PUBLISH; +} + +void LLFloaterPreference::processProfileProperties(const LLAvatarData* pAvatarData ) +{ + getChild<LLUICtrl>("online_searchresults")->setValue( (bool)(pAvatarData->flags & AVATAR_ALLOW_PUBLISH) ); +} + +void LLFloaterPreference::saveAvatarProperties( void ) +{ + mAvatarProperties.allow_publish = getChild<LLUICtrl>("online_searchresults")->getValue(); + if ( mAvatarProperties.allow_publish ) + { + mAvatarProperties.flags |= AVATAR_ALLOW_PUBLISH; + } + + LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesUpdate( &mAvatarProperties ); } + BOOL LLFloaterPreference::postBuild() { gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); @@ -338,14 +390,20 @@ BOOL LLFloaterPreference::postBuild() gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2)); + gSavedSettings.getControl("ChatBubbleOpacity")->getSignal()->connect(boost::bind(&LLFloaterPreference::onNameTagOpacityChange, this, _2)); + LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); if (!tabcontainer->selectTab(gSavedSettings.getS32("LastPrefTab"))) tabcontainer->selectFirstTab(); + updateDoubleClickControls(); + getChild<LLUICtrl>("cache_location")->setEnabled(FALSE); // make it read-only but selectable (STORM-227) std::string cache_location = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""); setCacheLocation(cache_location); + getChild<LLComboBox>("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this)); + // if floater is opened before login set default localized busy message if (LLStartUp::getStartupState() < STATE_STARTED) { @@ -405,6 +463,8 @@ void LLFloaterPreference::saveSettings() void LLFloaterPreference::apply() { + LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this ); + LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); if (sSkin != gSavedSettings.getString("SkinCurrent")) { @@ -475,6 +535,42 @@ void LLFloaterPreference::apply() gAgent.sendAgentUpdateUserInfo(new_im_via_email,mDirectoryVisibility); } } + + saveAvatarProperties(); + + if (mDoubleClickActionDirty) + { + updateDoubleClickSettings(); + mDoubleClickActionDirty = false; + } + + if (mFavoritesRecordMayExist && !gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) + { + removeFavoritesRecordOfUser(); + } +} + +void LLFloaterPreference::removeFavoritesRecordOfUser() +{ + mFavoritesRecordMayExist = false; + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; + LLSDSerialize::fromXML(fav_llsd, file); + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + if (fav_llsd.has(av_name.getLegacyName())) + { + fav_llsd.erase(av_name.getLegacyName()); + } + + llofstream out_file; + out_file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, out_file); + } void LLFloaterPreference::cancel() @@ -501,10 +597,17 @@ void LLFloaterPreference::cancel() // reverts any changes to current skin gSavedSettings.setString("SkinCurrent", sSkin); + + if (mDoubleClickActionDirty) + { + updateDoubleClickControls(); + mDoubleClickActionDirty = false; + } } void LLFloaterPreference::onOpen(const LLSD& key) { + // this variable and if that follows it are used to properly handle busy mode response message static bool initialized = FALSE; // if user is logged in and we haven't initialized busy_response yet, do it @@ -531,7 +634,7 @@ void LLFloaterPreference::onOpen(const LLSD& key) (gAgent.isMature() || gAgent.isGodlike()); LLComboBox* maturity_combo = getChild<LLComboBox>("maturity_desired_combobox"); - + LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest( gAgent.getID() ); if (can_choose_maturity) { // if they're not adult or a god, they shouldn't see the adult selection, so delete it @@ -553,6 +656,14 @@ void LLFloaterPreference::onOpen(const LLSD& key) getChildView("maturity_desired_combobox")->setVisible( false); } + if (LLStartUp::getStartupState() == STATE_STARTED) + { + mFavoritesRecordMayExist = gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin"); + } + + // Forget previous language changes. + mLanguageChanged = false; + // Display selected maturity icons. onChangeMaturity(); @@ -710,6 +821,28 @@ void LLFloaterPreference::onClickBrowserClearCache() LLNotificationsUtil::add("ConfirmClearBrowserCache", LLSD(), LLSD(), callback_clear_browser_cache); } +// Called when user changes language via the combobox. +void LLFloaterPreference::onLanguageChange() +{ + // Let the user know that the change will only take effect after restart. + // Do it only once so that we're not too irritating. + if (!mLanguageChanged) + { + LLNotificationsUtil::add("ChangeLanguage"); + mLanguageChanged = true; + } +} + +void LLFloaterPreference::onNameTagOpacityChange(const LLSD& newvalue) +{ + LLColorSwatchCtrl* color_swatch = findChild<LLColorSwatchCtrl>("background"); + if (color_swatch) + { + LLColor4 new_color = color_swatch->get(); + color_swatch->set( new_color.setAlpha(newvalue.asReal()) ); + } +} + void LLFloaterPreference::onClickSetCache() { std::string cur_name(gSavedSettings.getString("CacheLocation")); @@ -803,7 +936,7 @@ void LLFloaterPreference::buildPopupLists() LLScrollListItem* item = NULL; - bool show_popup = formp->getIgnored(); + bool show_popup = !formp->getIgnored(); if (!show_popup) { if (ignore == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE) @@ -1155,7 +1288,7 @@ void LLFloaterPreference::onClickDisablePopup() for (itor = items.begin(); itor != items.end(); ++itor) { LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate(*(std::string*)((*itor)->getUserdata())); - templatep->mForm->setIgnored(false); + templatep->mForm->setIgnored(true); } buildPopupLists(); @@ -1169,7 +1302,7 @@ void LLFloaterPreference::resetAllIgnored() { if (iter->second->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) { - iter->second->mForm->setIgnored(true); + iter->second->mForm->setIgnored(false); } } } @@ -1182,7 +1315,7 @@ void LLFloaterPreference::setAllIgnored() { if (iter->second->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) { - iter->second->mForm->setIgnored(false); + iter->second->mForm->setIgnored(true); } } } @@ -1241,12 +1374,13 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im // getChild<LLUICtrl>("busy_response")->setValue(gSavedSettings.getString("BusyModeResponse2")); + getChildView("favorites_on_login_check")->setEnabled(TRUE); getChildView("log_nearby_chat")->setEnabled(TRUE); getChildView("log_instant_messages")->setEnabled(TRUE); getChildView("show_timestamps_check_im")->setEnabled(TRUE); getChildView("log_path_string")->setEnabled(FALSE);// LineEditor becomes readonly in this case. getChildView("log_path_button")->setEnabled(TRUE); - + childEnable("logfile_name_datestamp"); std::string display_email(email); getChild<LLUICtrl>("email_address")->setValue(display_email); @@ -1318,6 +1452,68 @@ void LLFloaterPreference::onClickBlockList() } } +void LLFloaterPreference::onDoubleClickCheckBox(LLUICtrl* ctrl) +{ + if (!ctrl) return; + mDoubleClickActionDirty = true; + LLRadioGroup* radio_double_click_action = getChild<LLRadioGroup>("double_click_action"); + if (!radio_double_click_action) return; + // select default value("teleport") in radio-group. + radio_double_click_action->setSelectedIndex(0); + // set radio-group enabled depending on state of checkbox + radio_double_click_action->setEnabled(ctrl->getValue()); +} + +void LLFloaterPreference::onDoubleClickRadio() +{ + mDoubleClickActionDirty = true; +} + +void LLFloaterPreference::updateDoubleClickSettings() +{ + LLCheckBoxCtrl* double_click_action_cb = getChild<LLCheckBoxCtrl>("double_click_chkbox"); + if (!double_click_action_cb) return; + bool enable = double_click_action_cb->getValue().asBoolean(); + + LLRadioGroup* radio_double_click_action = getChild<LLRadioGroup>("double_click_action"); + if (!radio_double_click_action) return; + + // enable double click radio-group depending on state of checkbox + radio_double_click_action->setEnabled(enable); + + if (!enable) + { + // set double click action settings values to false if checkbox was unchecked + gSavedSettings.setBOOL("DoubleClickAutoPilot", false); + gSavedSettings.setBOOL("DoubleClickTeleport", false); + } + else + { + std::string selected = radio_double_click_action->getValue().asString(); + bool teleport_selected = selected == "radio_teleport"; + // set double click action settings values depending on chosen radio-button + gSavedSettings.setBOOL( "DoubleClickTeleport", teleport_selected ); + gSavedSettings.setBOOL( "DoubleClickAutoPilot", !teleport_selected ); + } +} + +void LLFloaterPreference::updateDoubleClickControls() +{ + // check is one of double-click actions settings enabled + bool double_click_action_enabled = gSavedSettings.getBOOL("DoubleClickAutoPilot") || gSavedSettings.getBOOL("DoubleClickTeleport"); + LLCheckBoxCtrl* double_click_action_cb = getChild<LLCheckBoxCtrl>("double_click_chkbox"); + if (double_click_action_cb) + { + // check checkbox if one of double-click actions settings enabled, uncheck otherwise + double_click_action_cb->setValue(double_click_action_enabled); + } + LLRadioGroup* double_click_action_radio = getChild<LLRadioGroup>("double_click_action"); + if (!double_click_action_radio) return; + // set radio-group enabled if one of double-click actions settings enabled + double_click_action_radio->setEnabled(double_click_action_enabled); + // select button in radio-group depending on setting + double_click_action_radio->setSelectedIndex(gSavedSettings.getBOOL("DoubleClickAutoPilot")); +} void LLFloaterPreference::applyUIColor(LLUICtrl* ctrl, const LLSD& param) { @@ -1394,6 +1590,10 @@ BOOL LLPanelPreference::postBuild() { getChild<LLCheckBoxCtrl>("voice_call_friends_only_check")->setCommitCallback(boost::bind(&showFriendsOnlyWarning, _1, _2)); } + if (hasChild("favorites_on_login_check")) + { + getChild<LLCheckBoxCtrl>("favorites_on_login_check")->setCommitCallback(boost::bind(&showFavoritesOnLoginWarning, _1, _2)); + } // Panel Advanced if (hasChild("modifier_combo")) @@ -1461,6 +1661,14 @@ void LLPanelPreference::showFriendsOnlyWarning(LLUICtrl* checkbox, const LLSD& v } } +void LLPanelPreference::showFavoritesOnLoginWarning(LLUICtrl* checkbox, const LLSD& value) +{ + if (checkbox && checkbox->getValue()) + { + LLNotificationsUtil::add("FavoritesOnLogin"); + } +} + void LLPanelPreference::cancel() { for (control_values_map_t::iterator iter = mSavedValues.begin(); diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index e99731b92e..784033ae95 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -34,6 +34,7 @@ #define LL_LLFLOATERPREFERENCE_H #include "llfloater.h" +#include "llavatarpropertiesprocessor.h" class LLPanelPreference; class LLPanelLCD; @@ -55,7 +56,7 @@ typedef enum // Floater to control preferences (display, audio, bandwidth, general. -class LLFloaterPreference : public LLFloater +class LLFloaterPreference : public LLFloater, public LLAvatarPropertiesObserver { public: LLFloaterPreference(const LLSD& key); @@ -77,12 +78,19 @@ public: // translate user's busy response message according to current locale if message is default, otherwise do nothing static void initBusyResponse(); + void processProperties( void* pData, EAvatarProcessorType type ); + void processProfileProperties(const LLAvatarData* pAvatarData ); + void storeAvatarProperties( const LLAvatarData* pAvatarData ); + void saveAvatarProperties( void ); + protected: void onBtnOK(); void onBtnCancel(); void onBtnApply(); void onClickBrowserClearCache(); + void onLanguageChange(); + void onNameTagOpacityChange(const LLSD& newvalue); // set value of "BusyResponseChanged" in account settings depending on whether busy response // string differs from default after user changes. @@ -95,6 +103,14 @@ protected: void setHardwareDefaults(); // callback for when client turns on shaders void onVertexShaderEnable(); + // callback for changing double click action checkbox + void onDoubleClickCheckBox(LLUICtrl* ctrl); + // callback for selecting double click action radio-button + void onDoubleClickRadio(); + // updates double-click action settings depending on controls from preferences + void updateDoubleClickSettings(); + // updates double-click action controls depending on values from settings.xml + void updateDoubleClickControls(); // This function squirrels away the current values of the controls so that // cancel() can restore them. @@ -143,13 +159,23 @@ public: void buildPopupLists(); static void refreshSkin(void* data); + // Remove record of current user's favorites from file on disk. + void removeFavoritesRecordOfUser(); private: static std::string sSkin; + // set true if state of double-click action checkbox or radio-group was changed by user + // (reset back to false on apply or cancel) + bool mDoubleClickActionDirty; bool mGotPersonalInfo; bool mOriginalIMViaEmail; + bool mLanguageChanged; bool mOriginalHideOnlineStatus; + // Record of current user's favorites may be stored in file on disk. + bool mFavoritesRecordMayExist; std::string mDirectoryVisibility; + + LLAvatarData mAvatarProperties; }; class LLPanelPreference : public LLPanel @@ -170,6 +196,8 @@ public: private: //for "Only friends and groups can call or IM me" static void showFriendsOnlyWarning(LLUICtrl*, const LLSD&); + //for "Show my Favorite Landmarks at Login" + static void showFavoritesOnLoginWarning(LLUICtrl* checkbox, const LLSD& value); typedef std::map<LLControlVariable*, LLSD> control_values_map_t; control_values_map_t mSavedValues; diff --git a/indra/newview/llfloaterregiondebugconsole.cpp b/indra/newview/llfloaterregiondebugconsole.cpp new file mode 100644 index 0000000000..b3b7645dd4 --- /dev/null +++ b/indra/newview/llfloaterregiondebugconsole.cpp @@ -0,0 +1,227 @@ +/** + * @file llfloaterregiondebugconsole.h + * @author Brad Kittenbrink <brad@lindenlab.com> + * @brief Quick and dirty console for region debug settings + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010-2010, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterregiondebugconsole.h" + +#include "llagent.h" +#include "llhttpclient.h" +#include "llhttpnode.h" +#include "lllineeditor.h" +#include "lltexteditor.h" +#include "llviewerregion.h" + +// Two versions of the sim console API are supported. +// +// SimConsole capability (deprecated): +// This is the initial implementation that is supported by some versions of the +// simulator. It is simple and straight forward, just POST a command and the +// body of the response has the result. This API is deprecated because it +// doesn't allow the sim to use any asynchronous API. +// +// SimConsoleAsync capability: +// This capability replaces the original SimConsole capability. It is similar +// in that the command is POSTed to the SimConsoleAsync cap, but the response +// comes in through the event poll, which gives the simulator more flexibility +// and allows it to perform complex operations without blocking any frames. +// +// We will assume the SimConsoleAsync capability is available, and fall back to +// the SimConsole cap if it is not. The simulator will only support one or the +// other. + +namespace +{ + // Signal used to notify the floater of responses from the asynchronous + // API. + typedef boost::signals2::signal< + void (const std::string& output)> console_reply_signal_t; + console_reply_signal_t sConsoleReplySignal; + + const std::string PROMPT("\n\n> "); + const std::string UNABLE_TO_SEND_COMMAND( + "ERROR: The last command was not received by the server."); + const std::string CONSOLE_UNAVAILABLE( + "ERROR: No console available for this region/simulator."); + const std::string CONSOLE_NOT_SUPPORTED( + "This region does not support the simulator console."); + + // This responder handles the initial response. Unless error() is called + // we assume that the simulator has received our request. Error will be + // called if this request times out. + class AsyncConsoleResponder : public LLHTTPClient::Responder + { + public: + /* virtual */ + void error(U32 status, const std::string& reason) + { + sConsoleReplySignal(UNABLE_TO_SEND_COMMAND); + } + }; + + class ConsoleResponder : public LLHTTPClient::Responder + { + public: + ConsoleResponder(LLTextEditor *output) : mOutput(output) + { + } + + /*virtual*/ + void error(U32 status, const std::string& reason) + { + if (mOutput) + { + mOutput->appendText( + UNABLE_TO_SEND_COMMAND + PROMPT, + false); + } + } + + /*virtual*/ + void result(const LLSD& content) + { + if (mOutput) + { + mOutput->appendText( + content.asString() + PROMPT, false); + } + } + + LLTextEditor * mOutput; + }; + + // This handles responses for console commands sent via the asynchronous + // API. + class ConsoleResponseNode : public LLHTTPNode + { + public: + /* virtual */ + void post( + LLHTTPNode::ResponsePtr reponse, + const LLSD& context, + const LLSD& input) const + { + llinfos << "Received response from the debug console: " + << input << llendl; + sConsoleReplySignal(input["body"].asString()); + } + }; +} + +LLFloaterRegionDebugConsole::LLFloaterRegionDebugConsole(LLSD const & key) +: LLFloater(key), mOutput(NULL) +{ + mReplySignalConnection = sConsoleReplySignal.connect( + boost::bind( + &LLFloaterRegionDebugConsole::onReplyReceived, + this, + _1)); +} + +LLFloaterRegionDebugConsole::~LLFloaterRegionDebugConsole() +{ + mReplySignalConnection.disconnect(); +} + +BOOL LLFloaterRegionDebugConsole::postBuild() +{ + LLLineEditor* input = getChild<LLLineEditor>("region_debug_console_input"); + input->setEnableLineHistory(true); + input->setCommitCallback(boost::bind(&LLFloaterRegionDebugConsole::onInput, this, _1, _2)); + input->setFocus(true); + input->setCommitOnFocusLost(false); + + mOutput = getChild<LLTextEditor>("region_debug_console_output"); + + std::string url = gAgent.getRegion()->getCapability("SimConsoleAsync"); + if (url.empty()) + { + // Fall back to see if the old API is supported. + url = gAgent.getRegion()->getCapability("SimConsole"); + if (url.empty()) + { + mOutput->appendText( + CONSOLE_NOT_SUPPORTED + PROMPT, + false); + return TRUE; + } + } + + mOutput->appendText("> ", false); + return TRUE; +} + +void LLFloaterRegionDebugConsole::onInput(LLUICtrl* ctrl, const LLSD& param) +{ + LLLineEditor* input = static_cast<LLLineEditor*>(ctrl); + std::string text = input->getText() + "\n"; + + std::string url = gAgent.getRegion()->getCapability("SimConsoleAsync"); + if (url.empty()) + { + // Fall back to the old API + url = gAgent.getRegion()->getCapability("SimConsole"); + if (url.empty()) + { + text += CONSOLE_UNAVAILABLE + PROMPT; + } + else + { + // Using SimConsole (deprecated) + LLHTTPClient::post( + url, + LLSD(input->getText()), + new ConsoleResponder(mOutput)); + } + } + else + { + // Using SimConsoleAsync + LLHTTPClient::post( + url, + LLSD(input->getText()), + new AsyncConsoleResponder); + } + + mOutput->appendText(text, false); + input->clear(); +} + +void LLFloaterRegionDebugConsole::onReplyReceived(const std::string& output) +{ + mOutput->appendText(output + PROMPT, false); +} + +LLHTTPRegistration<ConsoleResponseNode> + gHTTPRegistrationMessageDebugConsoleResponse( + "/message/SimConsoleResponse"); diff --git a/indra/newview/llfloaterregiondebugconsole.h b/indra/newview/llfloaterregiondebugconsole.h new file mode 100644 index 0000000000..4171a4da6b --- /dev/null +++ b/indra/newview/llfloaterregiondebugconsole.h @@ -0,0 +1,63 @@ +/** + * @file llfloaterregiondebugconsole.h + * @author Brad Kittenbrink <brad@lindenlab.com> + * @brief Quick and dirty console for region debug settings + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010-2010, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERREGIONDEBUGCONSOLE_H +#define LL_LLFLOATERREGIONDEBUGCONSOLE_H + +#include <boost/signals2.hpp> + +#include "llfloater.h" +#include "llhttpclient.h" + +class LLTextEditor; + +class LLFloaterRegionDebugConsole : public LLFloater, public LLHTTPClient::Responder +{ +public: + LLFloaterRegionDebugConsole(LLSD const & key); + virtual ~LLFloaterRegionDebugConsole(); + + // virtual + BOOL postBuild(); + + void onInput(LLUICtrl* ctrl, const LLSD& param); + + LLTextEditor * mOutput; + + private: + void onReplyReceived(const std::string& output); + + boost::signals2::connection mReplySignalConnection; +}; + +#endif // LL_LLFLOATERREGIONDEBUGCONSOLE_H diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index 3ed4aec89a..2041fac8d8 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -200,5 +200,5 @@ void LLFloaterSearch::search(const LLSD &key) url = LLWeb::expandURLSubstitutions(url, subs); // and load the URL in the web view - mBrowser->navigateTo(url); + mBrowser->navigateTo(url, "text/html"); } diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 36e8ad9dfc..0931f77281 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -1212,8 +1212,6 @@ LLViewerWindow::ESnapshotType LLFloaterSnapshot::Impl::getLayerType(LLFloaterSna type = LLViewerWindow::SNAPSHOT_TYPE_COLOR; else if (id == "depth") type = LLViewerWindow::SNAPSHOT_TYPE_DEPTH; - else if (id == "objects") - type = LLViewerWindow::SNAPSHOT_TYPE_OBJECT_ID; return type; } @@ -2191,9 +2189,11 @@ void LLFloaterSnapshot::draw() S32 offset_y = thumbnail_rect.mBottom + (thumbnail_rect.getHeight() - previewp->getThumbnailHeight()) / 2 ; glMatrixMode(GL_MODELVIEW); + // Apply floater transparency to the texture unless the floater is focused. + F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); gl_draw_scaled_image(offset_x, offset_y, previewp->getThumbnailWidth(), previewp->getThumbnailHeight(), - previewp->getThumbnailImage(), LLColor4::white); + previewp->getThumbnailImage(), LLColor4::white % alpha); previewp->drawPreviewRect(offset_x, offset_y) ; } diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp index e5c4547226..2aaf403d5f 100644 --- a/indra/newview/llfloatertopobjects.cpp +++ b/indra/newview/llfloatertopobjects.cpp @@ -147,17 +147,6 @@ void LLFloaterTopObjects::handle_land_reply(LLMessageSystem* msg, void** data) } -void LLFloaterTopObjects::onAvatarNameCache(const LLUUID& agent_id, - const LLAvatarName& av_name, - LLSD element) -{ - LLScrollListCtrl *list = getChild<LLScrollListCtrl>("objects_list"); - - element["columns"][2]["value"] = av_name.getCompleteName(); - - list->addElement(element); -} - void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) { U32 request_flags; @@ -182,7 +171,6 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) F32 mono_score = 0.f; bool have_extended_data = false; S32 public_urls = 0; - LLUUID owner_id; msg->getU32Fast(_PREHASH_ReportData, _PREHASH_TaskLocalID, task_local_id, block); msg->getUUIDFast(_PREHASH_ReportData, _PREHASH_TaskID, task_id, block); @@ -198,10 +186,8 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) msg->getU32("DataExtended", "TimeStamp", time_stamp, block); msg->getF32("DataExtended", "MonoScore", mono_score, block); msg->getS32(_PREHASH_ReportData,"PublicURLs",public_urls,block); - msg->getUUID("DataExtended","OwnerID",owner_id,block); } - LLSD element; element["id"] = task_id; @@ -252,16 +238,8 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) columns[6]["font"] = "SANSSERIF"; } element["columns"] = columns; + list->addElement(element); - if (!owner_id.isNull()) - { - LLAvatarNameCache::get(owner_id, boost::bind(&LLFloaterTopObjects::onAvatarNameCache, this, _1, _2, element)); - } - else - { - list->addElement(element); - } - mObjectListData.append(element); mObjectListIDs.push_back(task_id); diff --git a/indra/newview/llfloatertopobjects.h b/indra/newview/llfloatertopobjects.h index edd91c491f..a608ca20f1 100644 --- a/indra/newview/llfloatertopobjects.h +++ b/indra/newview/llfloatertopobjects.h @@ -29,11 +29,8 @@ #include "llfloater.h" -class LLAvatarName; class LLUICtrl; -#include <boost/signals2.hpp> // boost::signals2::trackable - class LLFloaterTopObjects : public LLFloater { friend class LLFloaterReg; @@ -54,8 +51,6 @@ public: static void setMode(U32 mode); - void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name, LLSD element); - private: LLFloaterTopObjects(const LLSD& key); ~LLFloaterTopObjects(); diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index 5dc8067648..11b3379814 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -36,6 +36,7 @@ // Internal utility #include "lleventtimer.h" +#include "llexternaleditor.h" #include "llrender.h" #include "llsdutil.h" #include "llxmltree.h" @@ -160,6 +161,8 @@ public: DiffMap mDiffsMap; // map, of filename to pair of list of changed element paths and list of errors private: + LLExternalEditor mExternalEditor; + // XUI elements for this floater LLScrollListCtrl* mFileList; // scroll list control for file list LLLineEditor* mEditorPathTextBox; // text field for path to editor executable @@ -185,7 +188,7 @@ private: std::string mSavedDiffPath; // stored diff file path so closing this floater doesn't reset it // Internal functionality - static void popupAndPrintWarning(std::string& warning); // pop up a warning + static void popupAndPrintWarning(const std::string& warning); // pop up a warning std::string getLocalizedDirectory(); // build and return the path to the XUI directory for the currently-selected localization void scanDiffFile(LLXmlTreeNode* file_node); // scan a given XML node for diff entries and highlight them in its associated file void highlightChangedElements(); // look up the list of elements to highlight and highlight them in the current floater @@ -480,7 +483,7 @@ BOOL LLFloaterUIPreview::postBuild() mLanguageSelection->removeall(); // clear out anything temporarily in list from XML while(found) // for every directory { - if((found = gDirUtilp->getNextFileInDir(xui_dir, "*", language_directory, FALSE))) // get next directory + if((found = gDirUtilp->getNextFileInDir(xui_dir, "*", language_directory))) // get next directory { std::string full_path = xui_dir + language_directory; if(LLFile::isfile(full_path.c_str())) // if it's not a directory, skip it @@ -597,7 +600,7 @@ void LLFloaterUIPreview::onClose(bool app_quitting) // Error handling (to avoid code repetition) // *TODO: this is currently unlocalized. Add to alerts/notifications.xml, someday, maybe. -void LLFloaterUIPreview::popupAndPrintWarning(std::string& warning) +void LLFloaterUIPreview::popupAndPrintWarning(const std::string& warning) { llwarns << warning << llendl; LLSD args; @@ -634,7 +637,7 @@ void LLFloaterUIPreview::refreshList() BOOL found = TRUE; while(found) // for every floater file that matches the pattern { - if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "floater_*.xml", name, FALSE))) // get next file matching pattern + if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "floater_*.xml", name))) // get next file matching pattern { addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path) } @@ -642,7 +645,7 @@ void LLFloaterUIPreview::refreshList() found = TRUE; while(found) // for every inspector file that matches the pattern { - if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "inspect_*.xml", name, FALSE))) // get next file matching pattern + if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "inspect_*.xml", name))) // get next file matching pattern { addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path) } @@ -650,7 +653,7 @@ void LLFloaterUIPreview::refreshList() found = TRUE; while(found) // for every menu file that matches the pattern { - if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "menu_*.xml", name, FALSE))) // get next file matching pattern + if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "menu_*.xml", name))) // get next file matching pattern { addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path) } @@ -658,7 +661,7 @@ void LLFloaterUIPreview::refreshList() found = TRUE; while(found) // for every panel file that matches the pattern { - if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "panel_*.xml", name, FALSE))) // get next file matching pattern + if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "panel_*.xml", name))) // get next file matching pattern { addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path) } @@ -667,7 +670,7 @@ void LLFloaterUIPreview::refreshList() found = TRUE; while(found) // for every sidepanel file that matches the pattern { - if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "sidepanel_*.xml", name, FALSE))) // get next file matching pattern + if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "sidepanel_*.xml", name))) // get next file matching pattern { addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path) } @@ -998,190 +1001,55 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) // Respond to button click to edit currently-selected floater void LLFloaterUIPreview::onClickEditFloater() { - std::string file_name = mFileList->getSelectedItemLabel(1); // get the file name of the currently-selected floater - if(std::string("") == file_name) // if no item is selected - { - return; // ignore click - } - std::string path = getLocalizedDirectory() + file_name; - - // stat file to see if it exists (some localized versions may not have it there are no diffs, and then we try to open an nonexistent file) - llstat dummy; - if(LLFile::stat(path.c_str(), &dummy)) // if the file does not exist - { - std::string warning = "No file for this floater exists in the selected localization. Opening the EN version instead."; - popupAndPrintWarning(warning); - - path = get_xui_dir() + mDelim + "en" + mDelim + file_name; // open the en version instead, by default - } - - // get executable path - const char* exe_path_char; - std::string path_in_textfield = mEditorPathTextBox->getText(); - if(std::string("") != path_in_textfield) // if the text field is not emtpy, use its path - { - exe_path_char = path_in_textfield.c_str(); - } - else if (!LLUI::sSettingGroups["config"]->getString("XUIEditor").empty()) - { - exe_path_char = LLUI::sSettingGroups["config"]->getString("XUIEditor").c_str(); - } - else // otherwise use the path specified by the environment variable + // Determine file to edit. + std::string file_path; { - exe_path_char = getenv("LL_XUI_EDITOR"); - } - - // error check executable path - if(NULL == exe_path_char) - { - std::string warning = "Select an editor by setting the environment variable LL_XUI_EDITOR or specifying its path in the \"Editor Path\" field."; - popupAndPrintWarning(warning); - return; - } - std::string exe_path = exe_path_char; // do this after error check, otherwise internal strlen call fails on bad char* - - // remove any quotes; they're added back in later where necessary - int found_at; - while((found_at = exe_path.find("\"")) != -1 || (found_at = exe_path.find("'")) != -1) - { - exe_path.erase(found_at,1); - } - - llstat s; - if(!LLFile::stat(exe_path.c_str(), &s)) // If the executable exists - { - // build paths and arguments - std::string quote = std::string("\""); - std::string args; - std::string custom_args = mEditorArgsTextBox->getText(); - int position_of_file = custom_args.find(std::string("%FILE%"), 0); // prepare to replace %FILE% with actual file path - std::string first_part_of_args = ""; - std::string second_part_of_args = ""; - if(-1 == position_of_file) // default: Executable.exe File.xml - { - args = quote + path + quote; // execute the command Program.exe "File.xml" - } - else // use advanced command-line arguments, e.g. "Program.exe -safe File.xml" -windowed for "-safe %FILE% -windowed" + std::string file_name = mFileList->getSelectedItemLabel(1); // get the file name of the currently-selected floater + if (file_name.empty()) // if no item is selected { - first_part_of_args = custom_args.substr(0,position_of_file); // get part of args before file name - second_part_of_args = custom_args.substr(position_of_file+6,custom_args.length()); // get part of args after file name - custom_args = first_part_of_args + std::string("\"") + path + std::string("\"") + second_part_of_args; // replace %FILE% with "<file path>" and put back together - args = custom_args; // and save in the variable that is actually used + llwarns << "No file selected" << llendl; + return; // ignore click } + file_path = getLocalizedDirectory() + file_name; - // find directory in which executable resides by taking everything after last slash - int last_slash_position = exe_path.find_last_of(mDelim); - if(-1 == last_slash_position) - { - std::string warning = std::string("Unable to find a valid path to the specified executable for XUI XML editing: ") + exe_path; - popupAndPrintWarning(warning); - return; - } - std::string exe_dir = exe_path.substr(0,last_slash_position); // strip executable off, e.g. get "C:\Program Files\TextPad 5" (with or without trailing slash) - -#if LL_WINDOWS - PROCESS_INFORMATION pinfo; - STARTUPINFOA sinfo; - memset(&sinfo, 0, sizeof(sinfo)); - memset(&pinfo, 0, sizeof(pinfo)); - - std::string exe_name = exe_path.substr(last_slash_position+1); - args = quote + exe_name + quote + std::string(" ") + args; // and prepend the executable name, so we get 'Program.exe "Arg1"' - - char *args2 = new char[args.size() + 1]; // Windows requires that the second parameter to CreateProcessA be a writable (non-const) string... - strcpy(args2, args.c_str()); - - // we don't want the current directory to be the executable directory, since the file path is now relative. By using - // NULL for the current directory instead of exe_dir.c_str(), the path to the target file will work. - if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) - { - // DWORD dwErr = GetLastError(); - std::string warning = "Creating editor process failed!"; - popupAndPrintWarning(warning); - } - else + // stat file to see if it exists (some localized versions may not have it there are no diffs, and then we try to open an nonexistent file) + llstat dummy; + if(LLFile::stat(file_path.c_str(), &dummy)) // if the file does not exist { - // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on - // sGatewayHandle = pinfo.hProcess; - CloseHandle(pinfo.hThread); // stops leaks - nothing else + popupAndPrintWarning("No file for this floater exists in the selected localization. Opening the EN version instead."); + file_path = get_xui_dir() + mDelim + "en" + mDelim + file_name; // open the en version instead, by default } + } - delete[] args2; -#else // if !LL_WINDOWS - // This code was copied from the code to run SLVoice, with some modification; should work in UNIX (Mac/Darwin or Linux) + // Set the editor command. + std::string cmd_override; + { + std::string bin = mEditorPathTextBox->getText(); + if (!bin.empty()) { - std::vector<std::string> arglist; - arglist.push_back(exe_path.c_str()); - - // Split the argument string into separate strings for each argument - typedef boost::tokenizer< boost::char_separator<char> > tokenizer; - boost::char_separator<char> sep("","\" ", boost::drop_empty_tokens); - - tokenizer tokens(args, sep); - tokenizer::iterator token_iter; - BOOL inside_quotes = FALSE; - BOOL last_was_space = FALSE; - for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - if(!strncmp("\"",(*token_iter).c_str(),2)) - { - inside_quotes = !inside_quotes; - } - else if(!strncmp(" ",(*token_iter).c_str(),2)) - { - if(inside_quotes) - { - arglist.back().append(std::string(" ")); - last_was_space = TRUE; - } - } - else - { - std::string to_push = *token_iter; - if(last_was_space) - { - arglist.back().append(to_push); - last_was_space = FALSE; - } - else - { - arglist.push_back(to_push); - } - } - } - - // create an argv vector for the child process - char **fakeargv = new char*[arglist.size() + 1]; - int i; - for(i=0; i < arglist.size(); i++) - fakeargv[i] = const_cast<char*>(arglist[i].c_str()); - - fakeargv[i] = NULL; - - fflush(NULL); // flush all buffers before the child inherits them - pid_t id = vfork(); - if(id == 0) + // surround command with double quotes for the case if the path contains spaces + if (bin.find("\"") == std::string::npos) { - // child - execv(exe_path.c_str(), fakeargv); - - // If we reach this point, the exec failed. - // Use _exit() instead of exit() per the vfork man page. - std::string warning = "Creating editor process failed (vfork/execv)!"; - popupAndPrintWarning(warning); - _exit(0); + bin = "\"" + bin + "\""; } - // parent - delete[] fakeargv; - // sGatewayPID = id; + std::string args = mEditorArgsTextBox->getText(); + cmd_override = bin + " " + args; } -#endif // LL_WINDOWS } - else + if (!mExternalEditor.setCommand("LL_XUI_EDITOR", cmd_override)) { - std::string warning = "Unable to find path to external XML editor for XUI preview tool"; + std::string warning = "Select an editor by setting the environment variable LL_XUI_EDITOR " + "or the ExternalEditor setting or specifying its path in the \"Editor Path\" field."; popupAndPrintWarning(warning); + return; + } + + // Run the editor. + if (!mExternalEditor.run(file_path)) + { + popupAndPrintWarning("Failed to run editor"); + return; } } diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp new file mode 100644 index 0000000000..51726112a0 --- /dev/null +++ b/indra/newview/llfloaterwebcontent.cpp @@ -0,0 +1,402 @@ +/**
+ * @file llfloaterwebcontent.cpp
+ * @brief floater for displaying web content - e.g. profiles and search (eventually)
+ *
+ * $LicenseInfo:firstyear=2006&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 "llcombobox.h"
+#include "lliconctrl.h"
+#include "llfloaterreg.h"
+#include "lllayoutstack.h"
+#include "llpluginclassmedia.h"
+#include "llprogressbar.h"
+#include "lltextbox.h"
+#include "llurlhistory.h"
+#include "llviewercontrol.h"
+#include "llweb.h"
+#include "llwindow.h"
+
+#include "llfloaterwebcontent.h"
+
+LLFloaterWebContent::LLFloaterWebContent( const LLSD& key )
+ : LLFloater( key )
+{
+ mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this ));
+ mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this ));
+ mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this ));
+ mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this ));
+ mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this ));
+ mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this ));
+}
+
+BOOL LLFloaterWebContent::postBuild()
+{
+ // these are used in a bunch of places so cache them
+ mWebBrowser = getChild< LLMediaCtrl >( "webbrowser" );
+ mAddressCombo = getChild< LLComboBox >( "address" );
+ mStatusBarText = getChild< LLTextBox >( "statusbartext" );
+ mStatusBarProgress = getChild<LLProgressBar>("statusbarprogress" );
+
+ // observe browser events
+ mWebBrowser->addObserver( this );
+
+ // these buttons are always enabled
+ getChildView("reload")->setEnabled( true );
+ getChildView("popexternal")->setEnabled( true );
+
+ // cache image for secure browsing
+ mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag");
+
+ // initialize the URL history using the system URL History manager
+ initializeURLHistory();
+
+ return TRUE;
+}
+
+void LLFloaterWebContent::initializeURLHistory()
+{
+ // start with an empty list
+ LLCtrlListInterface* url_list = childGetListInterface("address");
+ if (url_list)
+ {
+ url_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ }
+
+ // Get all of the entries in the "browser" collection
+ LLSD browser_history = LLURLHistory::getURLHistory("browser");
+ LLSD::array_iterator iter_history =
+ browser_history.beginArray();
+ LLSD::array_iterator end_history =
+ browser_history.endArray();
+ for(; iter_history != end_history; ++iter_history)
+ {
+ std::string url = (*iter_history).asString();
+ if(! url.empty())
+ url_list->addSimpleElement(url);
+ }
+}
+
+//static
+void LLFloaterWebContent::create( const std::string &url, const std::string& target, const std::string& uuid )
+{
+ lldebugs << "url = " << url << ", target = " << target << ", uuid = " << uuid << llendl;
+
+ std::string tag = target;
+
+ if(target.empty() || target == "_blank")
+ {
+ if(!uuid.empty())
+ {
+ tag = uuid;
+ }
+ else
+ {
+ // create a unique tag for this instance
+ LLUUID id;
+ id.generate();
+ tag = id.asString();
+ }
+ }
+
+ S32 browser_window_limit = gSavedSettings.getS32("WebContentWindowLimit");
+
+ if(LLFloaterReg::findInstance("web_content", tag) != NULL)
+ {
+ // There's already a web browser for this tag, so we won't be opening a new window.
+ }
+ else if(browser_window_limit != 0)
+ {
+ // showInstance will open a new window. Figure out how many web browsers are already open,
+ // and close the least recently opened one if this will put us over the limit.
+
+ LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList("web_content");
+ lldebugs << "total instance count is " << instances.size() << llendl;
+
+ for(LLFloaterReg::const_instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); iter++)
+ {
+ lldebugs << " " << (*iter)->getKey() << llendl;
+ }
+
+ if(instances.size() >= (size_t)browser_window_limit)
+ {
+ // Destroy the least recently opened instance
+ (*instances.begin())->closeFloater();
+ }
+ }
+
+ LLFloaterWebContent *browser = dynamic_cast<LLFloaterWebContent*> (LLFloaterReg::showInstance("web_content", tag));
+ llassert(browser);
+ if(browser)
+ {
+ browser->mUUID = uuid;
+
+ // tell the browser instance to load the specified URL
+ browser->open_media(url, target);
+ LLViewerMedia::proxyWindowOpened(target, uuid);
+ }
+}
+
+//static
+void LLFloaterWebContent::closeRequest(const std::string &uuid)
+{
+ LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content");
+ lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl;
+ for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
+ {
+ LLFloaterWebContent* i = dynamic_cast<LLFloaterWebContent*>(*iter);
+ lldebugs << " " << i->mUUID << llendl;
+ if (i && i->mUUID == uuid)
+ {
+ i->closeFloater(false);
+ return;
+ }
+ }
+}
+
+//static
+void LLFloaterWebContent::geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height)
+{
+ LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content");
+ lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl;
+ for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
+ {
+ LLFloaterWebContent* i = dynamic_cast<LLFloaterWebContent*>(*iter);
+ lldebugs << " " << i->mUUID << llendl;
+ if (i && i->mUUID == uuid)
+ {
+ i->geometryChanged(x, y, width, height);
+ return;
+ }
+ }
+}
+
+void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height)
+{
+ // Make sure the layout of the browser control is updated, so this calculation is correct.
+ LLLayoutStack::updateClass();
+
+ // TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc.
+ LLCoordWindow window_size;
+ getWindow()->getSize(&window_size);
+
+ // Adjust width and height for the size of the chrome on the web Browser window.
+ width += getRect().getWidth() - mWebBrowser->getRect().getWidth();
+ height += getRect().getHeight() - mWebBrowser->getRect().getHeight();
+
+ LLRect geom;
+ geom.setOriginAndSize(x, window_size.mY - (y + height), width, height);
+
+ lldebugs << "geometry change: " << geom << llendl;
+
+ handleReshape(geom,false);
+}
+
+void LLFloaterWebContent::open_media(const std::string& web_url, const std::string& target)
+{
+ // Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin.
+ mWebBrowser->setHomePageUrl(web_url, "text/html");
+ mWebBrowser->setTarget(target);
+ mWebBrowser->navigateTo(web_url, "text/html");
+ set_current_url(web_url);
+}
+
+//virtual
+void LLFloaterWebContent::onClose(bool app_quitting)
+{
+ LLViewerMedia::proxyWindowClosed(mUUID);
+ destroy();
+}
+
+// virtual
+void LLFloaterWebContent::draw()
+{
+ // this is asychronous so we need to keep checking
+ getChildView( "back" )->setEnabled( mWebBrowser->canNavigateBack() );
+ getChildView( "forward" )->setEnabled( mWebBrowser->canNavigateForward() );
+
+ LLFloater::draw();
+}
+
+// virtual
+void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
+{
+ if(event == MEDIA_EVENT_LOCATION_CHANGED)
+ {
+ const std::string url = self->getLocation();
+
+ if ( url.length() )
+ mStatusBarText->setText( url );
+
+ set_current_url( url );
+ }
+ else if(event == MEDIA_EVENT_NAVIGATE_BEGIN)
+ {
+ // flags are sent with this event
+ getChildView("back")->setEnabled( self->getHistoryBackAvailable() );
+ getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() );
+
+ // toggle visibility of these buttons based on browser state
+ getChildView("reload")->setVisible( false );
+ getChildView("stop")->setVisible( true );
+
+ // turn "on" progress bar now we're about to start loading
+ mStatusBarProgress->setVisible( true );
+ }
+ else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE)
+ {
+ // flags are sent with this event
+ getChildView("back")->setEnabled( self->getHistoryBackAvailable() );
+ getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() );
+
+ // toggle visibility of these buttons based on browser state
+ getChildView("reload")->setVisible( true );
+ getChildView("stop")->setVisible( false );
+
+ // turn "off" progress bar now we're loaded
+ mStatusBarProgress->setVisible( false );
+
+ // we populate the status bar with URLs as they change so clear it now we're done
+ const std::string end_str = "";
+ mStatusBarText->setText( end_str );
+
+ // decide if secure browsing icon should be displayed
+ std::string prefix = std::string("https://");
+ std::string test_prefix = mCurrentURL.substr(0, prefix.length());
+ LLStringUtil::toLower(test_prefix);
+ if(test_prefix == prefix)
+ {
+ mSecureLockIcon->setVisible(true);
+ }
+ else
+ {
+ mSecureLockIcon->setVisible(false);
+ }
+ }
+ else if(event == MEDIA_EVENT_CLOSE_REQUEST)
+ {
+ // The browser instance wants its window closed.
+ closeFloater();
+ }
+ else if(event == MEDIA_EVENT_GEOMETRY_CHANGE)
+ {
+ geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight());
+ }
+ else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED )
+ {
+ const std::string text = self->getStatusText();
+ if ( text.length() )
+ mStatusBarText->setText( text );
+ }
+ else if(event == MEDIA_EVENT_PROGRESS_UPDATED )
+ {
+ int percent = (int)self->getProgressPercent();
+ mStatusBarProgress->setValue( percent );
+ }
+ else if(event == MEDIA_EVENT_NAME_CHANGED )
+ {
+ std::string page_title = self->getMediaName();
+ // simulate browser behavior - title is empty, use the current URL
+ if ( page_title.length() > 0 )
+ setTitle( page_title );
+ else
+ setTitle( mCurrentURL );
+ }
+ else if(event == MEDIA_EVENT_LINK_HOVERED )
+ {
+ const std::string link = self->getHoverLink();
+ mStatusBarText->setText( link );
+ }
+}
+
+void LLFloaterWebContent::set_current_url(const std::string& url)
+{
+ mCurrentURL = url;
+
+ // serialize url history into the system URL History manager
+ LLURLHistory::removeURL("browser", mCurrentURL);
+ LLURLHistory::addURL("browser", mCurrentURL);
+
+ mAddressCombo->remove( mCurrentURL );
+ mAddressCombo->add( mCurrentURL );
+ mAddressCombo->selectByValue( mCurrentURL );
+}
+
+void LLFloaterWebContent::onClickForward()
+{
+ mWebBrowser->navigateForward();
+}
+
+void LLFloaterWebContent::onClickBack()
+{
+ mWebBrowser->navigateBack();
+}
+
+void LLFloaterWebContent::onClickReload()
+{
+
+ if( mWebBrowser->getMediaPlugin() )
+ {
+ bool ignore_cache = true;
+ mWebBrowser->getMediaPlugin()->browse_reload( ignore_cache );
+ }
+ else
+ {
+ mWebBrowser->navigateTo(mCurrentURL);
+ }
+}
+
+void LLFloaterWebContent::onClickStop()
+{
+ if( mWebBrowser->getMediaPlugin() )
+ mWebBrowser->getMediaPlugin()->browse_stop();
+
+ // still should happen when we catch the navigate complete event
+ // but sometimes (don't know why) that event isn't sent from Qt
+ // and we getto a point where the stop button stays active.
+ getChildView("reload")->setVisible( true );
+ getChildView("stop")->setVisible( false );
+}
+
+void LLFloaterWebContent::onEnterAddress()
+{
+ // make sure there is at least something there.
+ // (perhaps this test should be for minimum length of a URL)
+ std::string url = mAddressCombo->getValue().asString();
+ if ( url.length() > 0 )
+ {
+ mWebBrowser->navigateTo( url, "text/html");
+ };
+}
+
+void LLFloaterWebContent::onPopExternal()
+{
+ // make sure there is at least something there.
+ // (perhaps this test should be for minimum length of a URL)
+ std::string url = mAddressCombo->getValue().asString();
+ if ( url.length() > 0 )
+ {
+ LLWeb::loadURLExternal( url );
+ };
+}
diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h new file mode 100644 index 0000000000..001d822ada --- /dev/null +++ b/indra/newview/llfloaterwebcontent.h @@ -0,0 +1,82 @@ +/**
+ * @file llfloaterwebcontent.h
+ * @brief floater for displaying web content - e.g. profiles and search (eventually)
+ *
+ * $LicenseInfo:firstyear=2006&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_LLFLOATERWEBCONTENT_H
+#define LL_LLFLOATERWEBCONTENT_H
+
+#include "llfloater.h"
+#include "llmediactrl.h"
+
+class LLMediaCtrl;
+class LLComboBox;
+class LLTextBox;
+class LLProgressBar;
+class LLIconCtrl;
+
+class LLFloaterWebContent :
+ public LLFloater,
+ public LLViewerMediaObserver
+{
+public:
+ LOG_CLASS(LLFloaterWebContent);
+ LLFloaterWebContent(const LLSD& key);
+
+ void initializeURLHistory(); +
+ static void create(const std::string &url, const std::string& target, const std::string& uuid = LLStringUtil::null);
+
+ static void closeRequest(const std::string &uuid);
+ static void geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height);
+ void geometryChanged(S32 x, S32 y, S32 width, S32 height);
+
+ /* virtual */ BOOL postBuild();
+ /* virtual */ void onClose(bool app_quitting);
+ /* virtual */ void draw();
+
+ // inherited from LLViewerMediaObserver
+ /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event);
+
+ void onClickBack();
+ void onClickForward();
+ void onClickReload();
+ void onClickStop();
+ void onEnterAddress();
+ void onPopExternal();
+
+private:
+ void open_media(const std::string& media_url, const std::string& target);
+ void set_current_url(const std::string& url);
+
+ LLMediaCtrl* mWebBrowser;
+ LLComboBox* mAddressCombo;
+ LLIconCtrl *mSecureLockIcon;
+ LLTextBox* mStatusBarText;
+ LLProgressBar* mStatusBarProgress;
+ std::string mCurrentURL;
+ std::string mUUID;
+};
+
+#endif // LL_LLFLOATERWEBCONTENT_H
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 7236894542..017cd2fc49 100644..100755 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -72,7 +72,6 @@ #include "llweb.h" #include "llslider.h" #include "message.h" - #include "llwindow.h" // copyTextToClipboard() //--------------------------------------------------------------------------- @@ -106,8 +105,8 @@ class LLWorldMapHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_THROTTLE) { } - + LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_THROTTLE ) { } + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { @@ -117,21 +116,52 @@ public: LLFloaterReg::showInstance("world_map", "center"); return true; } - + // support the secondlife:///app/worldmap/{LOCATION}/{COORDS} SLapp const std::string region_name = LLURI::unescape(params[0].asString()); S32 x = (params.size() > 1) ? params[1].asInteger() : 128; S32 y = (params.size() > 2) ? params[2].asInteger() : 128; S32 z = (params.size() > 3) ? params[3].asInteger() : 0; - + LLFloaterWorldMap::getInstance()->trackURL(region_name, x, y, z); LLFloaterReg::showInstance("world_map", "center"); - + return true; } }; LLWorldMapHandler gWorldMapHandler; +// SocialMap handler secondlife:///app/maptrackavatar/id +class LLMapTrackAvatarHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLMapTrackAvatarHandler() : LLCommandHandler("maptrackavatar", UNTRUSTED_THROTTLE) + { + } + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + //Make sure we have some parameters + if (params.size() == 0) + { + return false; + } + + //Get the ID + LLUUID id; + if (!id.set( params[0], FALSE )) + { + return false; + } + + LLFloaterWorldMap::getInstance()->avatarTrackFromSlapp( id ); + LLFloaterReg::showInstance( "world_map", "center" ); + + return true; + } +}; +LLMapTrackAvatarHandler gMapTrackAvatar; LLFloaterWorldMap* gFloaterWorldMap = NULL; @@ -142,7 +172,7 @@ public: virtual ~LLMapInventoryObserver() {} virtual void changed(U32 mask); }; - + void LLMapInventoryObserver::changed(U32 mask) { // if there's a change we're interested in. @@ -184,16 +214,16 @@ const LLUUID LLFloaterWorldMap::sHomeID( "10000000-0000-0000-0000-000000000001" LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key) : LLFloater(key), - mInventory(NULL), - mInventoryObserver(NULL), - mFriendObserver(NULL), - mCompletingRegionName(), - mCompletingRegionPos(), - mWaitingForTracker(FALSE), - mIsClosing(FALSE), - mSetToUserPosition(TRUE), - mTrackedLocation(0,0,0), - mTrackedStatus(LLTracker::TRACKING_NOTHING) +mInventory(NULL), +mInventoryObserver(NULL), +mFriendObserver(NULL), +mCompletingRegionName(), +mCompletingRegionPos(), +mWaitingForTracker(FALSE), +mIsClosing(FALSE), +mSetToUserPosition(TRUE), +mTrackedLocation(0,0,0), +mTrackedStatus(LLTracker::TRACKING_NOTHING) { gFloaterWorldMap = this; @@ -210,7 +240,7 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key) mCommitCallbackRegistrar.add("WMap.ShowAgent", boost::bind(&LLFloaterWorldMap::onShowAgentBtn, this)); mCommitCallbackRegistrar.add("WMap.Clear", boost::bind(&LLFloaterWorldMap::onClearBtn, this)); mCommitCallbackRegistrar.add("WMap.CopySLURL", boost::bind(&LLFloaterWorldMap::onCopySLURL, this)); - + gSavedSettings.getControl("PreferredMaturity")->getSignal()->connect(boost::bind(&LLFloaterWorldMap::onChangeMaturity, this)); } @@ -223,32 +253,32 @@ void* LLFloaterWorldMap::createWorldMapView(void* data) BOOL LLFloaterWorldMap::postBuild() { mPanel = getChild<LLPanel>("objects_mapview"); - + LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo"); avatar_combo->selectFirstItem(); avatar_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this) ); avatar_combo->setTextEntryCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); - + LLSearchEditor *location_editor = getChild<LLSearchEditor>("location"); location_editor->setFocusChangedCallback(boost::bind(&LLFloaterWorldMap::onLocationFocusChanged, this, _1)); location_editor->setKeystrokeCallback( boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this)); getChild<LLScrollListCtrl>("search_results")->setDoubleClickCallback( boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this)); - + LLComboBox *landmark_combo = getChild<LLComboBox>( "landmark combo"); landmark_combo->selectFirstItem(); landmark_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this) ); landmark_combo->setTextEntryCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); - + mCurZoomVal = log(LLWorldMapView::sMapScale)/log(2.f); getChild<LLUICtrl>("zoom slider")->setValue(LLWorldMapView::sMapScale); - + setDefaultBtn(NULL); - + mZoomTimer.stop(); - + onChangeMaturity(); - + return TRUE; } @@ -257,11 +287,11 @@ LLFloaterWorldMap::~LLFloaterWorldMap() { // All cleaned up by LLView destructor mPanel = NULL; - + // Inventory deletes all observers on shutdown mInventory = NULL; mInventoryObserver = NULL; - + // avatar tracker will delete this for us. mFriendObserver = NULL; @@ -285,13 +315,13 @@ void LLFloaterWorldMap::onClose(bool app_quitting) void LLFloaterWorldMap::onOpen(const LLSD& key) { bool center_on_target = (key.asString() == "center"); - + mIsClosing = FALSE; - + LLWorldMapView* map_panel; map_panel = (LLWorldMapView*)gFloaterWorldMap->mPanel; map_panel->clearLastClick(); - + { // reset pan on show, so it centers on you again if (!center_on_target) @@ -299,27 +329,27 @@ void LLFloaterWorldMap::onOpen(const LLSD& key) LLWorldMapView::setPan(0, 0, TRUE); } map_panel->updateVisibleBlocks(); - + // Reload items as they may have changed LLWorldMap::getInstance()->reloadItems(); - + // We may already have a bounding box for the regions of the world, // so use that to adjust the view. adjustZoomSliderBounds(); - + // Could be first show //LLFirstUse::useMap(); - + // Start speculative download of landmarks const LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); LLInventoryModelBackgroundFetch::instance().start(landmark_folder_id); - + getChild<LLUICtrl>("location")->setFocus( TRUE); gFocusMgr.triggerFocusFlash(); - + buildAvatarIDList(); buildLandmarkIDLists(); - + // If nothing is being tracked, set flag so the user position will be found mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING ); } @@ -356,7 +386,7 @@ BOOL LLFloaterWorldMap::handleScrollWheel(S32 x, S32 y, S32 clicks) return TRUE; } } - + return LLFloater::handleScrollWheel(x, y, clicks); } @@ -381,7 +411,7 @@ void LLFloaterWorldMap::draw() bool agent_on_prelude = (regionp && regionp->isPrelude()); bool enable_go_home = gAgent.isGodlike() || !agent_on_prelude; getChildView("Go Home")->setEnabled(enable_go_home); - + updateLocation(); LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus(); @@ -393,7 +423,7 @@ void LLFloaterWorldMap::draw() { getChild<LLUICtrl>("avatar_icon")->setColor( map_track_disabled_color); } - + if (LLTracker::TRACKING_LANDMARK == tracking_status) { getChild<LLUICtrl>("landmark_icon")->setColor( map_track_color); @@ -402,7 +432,7 @@ void LLFloaterWorldMap::draw() { getChild<LLUICtrl>("landmark_icon")->setColor( map_track_disabled_color); } - + if (LLTracker::TRACKING_LOCATION == tracking_status) { getChild<LLUICtrl>("location_icon")->setColor( map_track_color); @@ -422,21 +452,21 @@ void LLFloaterWorldMap::draw() getChild<LLUICtrl>("location_icon")->setColor( map_track_disabled_color); } } - + // check for completion of tracking data if (mWaitingForTracker) { centerOnTarget(TRUE); } - + getChildView("Teleport")->setEnabled((BOOL)tracking_status); -// getChildView("Clear")->setEnabled((BOOL)tracking_status); + // getChildView("Clear")->setEnabled((BOOL)tracking_status); getChildView("Show Destination")->setEnabled((BOOL)tracking_status || LLWorldMap::getInstance()->isTracking()); getChildView("copy_slurl")->setEnabled((mSLURL.isValid()) ); - + setMouseOpaque(TRUE); getDragHandle()->setMouseOpaque(TRUE); - + //RN: snaps to zoom value because interpolation caused jitter in the text rendering if (!mZoomTimer.getStarted() && mCurZoomVal != (F32)getChild<LLUICtrl>("zoom slider")->getValue().asReal()) { @@ -451,7 +481,7 @@ void LLFloaterWorldMap::draw() mCurZoomVal = lerp(mCurZoomVal, (F32)getChild<LLUICtrl>("zoom slider")->getValue().asReal(), interp); F32 map_scale = 256.f*pow(2.f, mCurZoomVal); LLWorldMapView::setScale( map_scale ); - + // Enable/disable checkboxes depending on the zoom level // If above threshold level (i.e. low res) -> Disable all checkboxes // If under threshold level (i.e. high res) -> Enable all checkboxes @@ -477,7 +507,7 @@ void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string& { LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo"); if (!iface) return; - + buildAvatarIDList(); if(iface->setCurrentByID(avatar_id) || gAgent.isGodlike()) { @@ -507,7 +537,7 @@ void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id ) { LLCtrlSelectionInterface *iface = childGetSelectionInterface("landmark combo"); if (!iface) return; - + buildLandmarkIDLists(); BOOL found = FALSE; S32 idx; @@ -519,7 +549,7 @@ void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id ) break; } } - + if (found && iface->setCurrentByID( landmark_item_id ) ) { LLUUID asset_id = mLandmarkAssetIDList.get( idx ); @@ -528,17 +558,17 @@ void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id ) if (combo) name = combo->getSimple(); mTrackedStatus = LLTracker::TRACKING_LANDMARK; LLTracker::trackLandmark(mLandmarkAssetIDList.get( idx ), // assetID - mLandmarkItemIDList.get( idx ), // itemID - name); // name - + mLandmarkItemIDList.get( idx ), // itemID + name); // name + if( asset_id != sHomeID ) { // start the download process gLandmarkList.getAsset( asset_id); } - + // We have to download both region info and landmark data, so set busy. JC -// getWindow()->incBusyCount(); + // getWindow()->incBusyCount(); } else { @@ -574,10 +604,10 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global) S32 world_y = S32(pos_global.mdV[1] / 256); LLWorldMapMessage::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true); setDefaultBtn(""); - + // clicked on a non-region - turn off coord display enableTeleportCoordsDisplay( false ); - + return; } if (sim_info->isDown()) @@ -588,33 +618,33 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global) LLWorldMap::getInstance()->setTrackingInvalid(); LLTracker::stopTracking(NULL); setDefaultBtn(""); - + // clicked on a down region - turn off coord display enableTeleportCoordsDisplay( false ); - + return; } - + std::string sim_name = sim_info->getName(); F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS ); F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS ); std::string full_name = llformat("%s (%d, %d, %d)", - sim_name.c_str(), - llround(region_x), - llround(region_y), - llround((F32)pos_global.mdV[VZ])); - + sim_name.c_str(), + llround(region_x), + llround(region_y), + llround((F32)pos_global.mdV[VZ])); + std::string tooltip(""); mTrackedStatus = LLTracker::TRACKING_LOCATION; LLTracker::trackLocation(pos_global, full_name, tooltip); LLWorldMap::getInstance()->cancelTracking(); // The floater is taking over the tracking - + LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal(); updateTeleportCoordsDisplay( coord_pos ); - + // we have a valid region - turn on coord display enableTeleportCoordsDisplay( true ); - + setDefaultBtn("Teleport"); } @@ -631,11 +661,11 @@ void LLFloaterWorldMap::updateTeleportCoordsDisplay( const LLVector3d& pos ) { // if we're going to update their value, we should also enable them enableTeleportCoordsDisplay( true ); - + // convert global specified position to a local one F32 region_local_x = (F32)fmod( pos.mdV[VX], (F64)REGION_WIDTH_METERS ); F32 region_local_y = (F32)fmod( pos.mdV[VY], (F64)REGION_WIDTH_METERS ); - F32 region_local_z = (F32)fmod( pos.mdV[VZ], (F64)REGION_WIDTH_METERS ); + F32 region_local_z = (F32)llclamp( pos.mdV[VZ], 0.0, (F64)REGION_HEIGHT_METERS ); // write in the values childSetValue("teleport_coordinate_x", region_local_x ); @@ -646,16 +676,16 @@ void LLFloaterWorldMap::updateTeleportCoordsDisplay( const LLVector3d& pos ) void LLFloaterWorldMap::updateLocation() { bool gotSimName; - + LLTracker::ETrackingStatus status = LLTracker::getTrackingStatus(); - + // These values may get updated by a message, so need to check them every frame // The fields may be changed by the user, so only update them if the data changes LLVector3d pos_global = LLTracker::getTrackedPositionGlobal(); if (pos_global.isExactlyZero()) { LLVector3d agentPos = gAgent.getPositionGlobal(); - + // Set to avatar's current postion if nothing is selected if ( status == LLTracker::TRACKING_NOTHING && mSetToUserPosition ) { @@ -665,19 +695,19 @@ void LLFloaterWorldMap::updateLocation() if ( gotSimName ) { mSetToUserPosition = FALSE; - + // Fill out the location field getChild<LLUICtrl>("location")->setValue(agent_sim_name); - + // update the coordinate display with location of avatar in region updateTeleportCoordsDisplay( agentPos ); - + // Figure out where user is // Set the current SLURL mSLURL = LLSLURL(agent_sim_name, gAgent.getPositionGlobal()); } } - + return; // invalid location } std::string sim_name; @@ -699,17 +729,17 @@ void LLFloaterWorldMap::updateLocation() pos_global[2] = 200; } } - + getChild<LLUICtrl>("location")->setValue(sim_name); - + // refresh coordinate display to reflect where user clicked. LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal(); updateTeleportCoordsDisplay( coord_pos ); - + // simNameFromPosGlobal can fail, so don't give the user an invalid SLURL if ( gotSimName ) { - mSLURL = LLSLURL(sim_name, pos_global); + mSLURL = LLSLURL(sim_name, pos_global); } else { // Empty SLURL will disable the "Copy SLURL to clipboard" button @@ -736,12 +766,12 @@ void LLFloaterWorldMap::trackURL(const std::string& region_name, S32 x_coord, S3 { // fill in UI based on URL gFloaterWorldMap->getChild<LLUICtrl>("location")->setValue(region_name); - + // Save local coords to highlight position after region global // position is returned. gFloaterWorldMap->mCompletingRegionPos.set( - (F32)x_coord, (F32)y_coord, (F32)z_coord); - + (F32)x_coord, (F32)y_coord, (F32)z_coord); + // pass sim name to combo box gFloaterWorldMap->mCompletingRegionName = region_name; LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name); @@ -813,7 +843,7 @@ void LLFloaterWorldMap::buildAvatarIDList() { LLCtrlListInterface *list = childGetListInterface("friend combo"); if (!list) return; - + // Delete all but the "None" entry S32 list_size = list->getItemCount(); if (list_size > 1) @@ -821,7 +851,7 @@ void LLFloaterWorldMap::buildAvatarIDList() list->selectItemRange(1, -1); list->operateOnSelection(LLCtrlListInterface::OP_DELETE); } - + // Get all of the calling cards for avatar that are currently online LLCollectMappableBuddies collector; LLAvatarTracker::instance().applyFunctor(collector); @@ -833,7 +863,7 @@ void LLFloaterWorldMap::buildAvatarIDList() { list->addSimpleElement((*it).first, ADD_BOTTOM, (*it).second); } - + list->setCurrentByID( LLAvatarTracker::instance().getAvatarID() ); list->selectFirstItem(); } @@ -843,7 +873,7 @@ void LLFloaterWorldMap::buildLandmarkIDLists() { LLCtrlListInterface *list = childGetListInterface("landmark combo"); if (!list) return; - + // Delete all but the "None" entry S32 list_size = list->getItemCount(); if (list_size > 1) @@ -851,17 +881,17 @@ void LLFloaterWorldMap::buildLandmarkIDLists() list->selectItemRange(1, -1); list->operateOnSelection(LLCtrlListInterface::OP_DELETE); } - + mLandmarkItemIDList.reset(); mLandmarkAssetIDList.reset(); - + // Get all of the current landmarks mLandmarkAssetIDList.put( LLUUID::null ); mLandmarkItemIDList.put( LLUUID::null ); - + mLandmarkAssetIDList.put( sHomeID ); mLandmarkItemIDList.put( sHomeID ); - + LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; LLIsType is_landmark(LLAssetType::AT_LANDMARK); @@ -870,20 +900,20 @@ void LLFloaterWorldMap::buildLandmarkIDLists() items, LLInventoryModel::EXCLUDE_TRASH, is_landmark); - + std::sort(items.begin(), items.end(), LLViewerInventoryItem::comparePointers()); S32 count = items.count(); for(S32 i = 0; i < count; ++i) { LLInventoryItem* item = items.get(i); - + list->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID()); - + mLandmarkAssetIDList.put( item->getAssetUUID() ); mLandmarkItemIDList.put( item->getUUID() ); } - + list->selectFirstItem(); } @@ -949,31 +979,31 @@ void LLFloaterWorldMap::adjustZoomSliderBounds() // Currently (01/26/09), this value allows the whole grid to be visible in a 1024x1024 window. S32 world_width_regions = MAX_VISIBLE_REGIONS; S32 world_height_regions = MAX_VISIBLE_REGIONS; - + // Find how much space we have to display the world LLWorldMapView* map_panel; map_panel = (LLWorldMapView*)mPanel; LLRect view_rect = map_panel->getRect(); - + // View size in pixels S32 view_width = view_rect.getWidth(); S32 view_height = view_rect.getHeight(); - + // Pixels per region to display entire width/height F32 width_pixels_per_region = (F32) view_width / (F32) world_width_regions; F32 height_pixels_per_region = (F32) view_height / (F32) world_height_regions; - + F32 pixels_per_region = llmin(width_pixels_per_region, height_pixels_per_region); - + // Round pixels per region to an even number of slider increments S32 slider_units = llfloor(pixels_per_region / 0.2f); pixels_per_region = slider_units * 0.2f; - + // Make sure the zoom slider can be moved at least a little bit. // Likewise, less than the increment pixels per region is just silly. pixels_per_region = llclamp(pixels_per_region, 1.f, ZOOM_MAX); - + F32 min_power = log(pixels_per_region/256.f)/log(2.f); getChild<LLSlider>("zoom slider")->setMinValue(min_power); @@ -997,19 +1027,19 @@ void LLFloaterWorldMap::onLandmarkComboPrearrange( ) { return; } - + LLCtrlListInterface *list = childGetListInterface("landmark combo"); if (!list) return; - + LLUUID current_choice = list->getCurrentID(); - + buildLandmarkIDLists(); - + if( current_choice.isNull() || !list->setCurrentByID( current_choice ) ) { LLTracker::stopTracking(NULL); } - + } void LLFloaterWorldMap::onComboTextEntry() @@ -1033,18 +1063,18 @@ void LLFloaterWorldMap::onLandmarkComboCommit() { return; } - + LLCtrlListInterface *list = childGetListInterface("landmark combo"); if (!list) return; - + LLUUID asset_id; LLUUID item_id = list->getCurrentID(); - + LLTracker::stopTracking(NULL); - + //RN: stopTracking() clears current combobox selection, need to reassert it here list->setCurrentByID(item_id); - + if( item_id.isNull() ) { } @@ -1068,7 +1098,7 @@ void LLFloaterWorldMap::onLandmarkComboCommit() trackLandmark( item_id); onShowTargetBtn(); - + // Reset to user postion if nothing is tracked mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING ); } @@ -1080,19 +1110,19 @@ void LLFloaterWorldMap::onAvatarComboPrearrange( ) { return; } - + LLCtrlListInterface *list = childGetListInterface("friend combo"); if (!list) return; - + LLUUID current_choice; - + if( LLAvatarTracker::instance().haveTrackingInfo() ) { current_choice = LLAvatarTracker::instance().getAvatarID(); } - + buildAvatarIDList(); - + if( !list->setCurrentByID( current_choice ) || current_choice.isNull() ) { LLTracker::stopTracking(NULL); @@ -1105,10 +1135,10 @@ void LLFloaterWorldMap::onAvatarComboCommit() { return; } - + LLCtrlListInterface *list = childGetListInterface("friend combo"); if (!list) return; - + const LLUUID& new_avatar_id = list->getCurrentID(); if (new_avatar_id.notNull()) { @@ -1124,6 +1154,12 @@ void LLFloaterWorldMap::onAvatarComboCommit() } } +void LLFloaterWorldMap::avatarTrackFromSlapp( const LLUUID& id ) +{ + trackAvatar( id, "av" ); + onShowTargetBtn(); +} + void LLFloaterWorldMap::onLocationFocusChanged( LLFocusableElement* focus ) { updateSearchEnabled(); @@ -1148,13 +1184,13 @@ void LLFloaterWorldMap::onLocationCommit() { return; } - + clearLocationSelection(FALSE); mCompletingRegionName = ""; mLastRegionName = ""; - + std::string str = getChild<LLUICtrl>("location")->getValue().asString(); - + // Trim any leading and trailing spaces in the search target std::string saved_str = str; LLStringUtil::trim( str ); @@ -1162,7 +1198,7 @@ void LLFloaterWorldMap::onLocationCommit() { // Set the value in the UI if any spaces were removed getChild<LLUICtrl>("location")->setValue(str); } - + LLStringUtil::toLower(str); mCompletingRegionName = str; LLWorldMap::getInstance()->setTrackingCommit(); @@ -1183,13 +1219,13 @@ void LLFloaterWorldMap::onCoordinatesCommit() { return; } - + S32 x_coord = (S32)childGetValue("teleport_coordinate_x").asReal(); S32 y_coord = (S32)childGetValue("teleport_coordinate_y").asReal(); S32 z_coord = (S32)childGetValue("teleport_coordinate_z").asReal(); - + const std::string region_name = childGetValue("location").asString(); - + trackURL( region_name, x_coord, y_coord, z_coord ); } @@ -1225,7 +1261,7 @@ void LLFloaterWorldMap::onCopySLURL() LLSD args; args["SLURL"] = mSLURL.getSLURLString(); - + LLNotificationsUtil::add("CopySLURL", args); } @@ -1246,26 +1282,26 @@ void LLFloaterWorldMap::centerOnTarget(BOOL animate) else { // We've got the position finally, so we're no longer busy. JC -// getWindow()->decBusyCount(); + // getWindow()->decBusyCount(); pos_global = LLTracker::getTrackedPositionGlobal() - gAgentCamera.getCameraPositionGlobal(); } } else if(LLWorldMap::getInstance()->isTracking()) { pos_global = LLWorldMap::getInstance()->getTrackedPositionGlobal() - gAgentCamera.getCameraPositionGlobal();; - - - + + + } else { // default behavior = center on agent pos_global.clearVec(); } - + LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)), - -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)), - !animate); + -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)), + !animate); mWaitingForTracker = FALSE; } @@ -1273,7 +1309,7 @@ void LLFloaterWorldMap::centerOnTarget(BOOL animate) void LLFloaterWorldMap::fly() { LLVector3d pos_global = LLTracker::getTrackedPositionGlobal(); - + // Start the autopilot and close the floater, // so we can see where we're flying if (!pos_global.isExactlyZero()) @@ -1294,7 +1330,7 @@ void LLFloaterWorldMap::teleport() BOOL teleport_home = FALSE; LLVector3d pos_global; LLAvatarTracker& av_tracker = LLAvatarTracker::instance(); - + LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus(); if (LLTracker::TRACKING_AVATAR == tracking_status && av_tracker.haveTrackingInfo() ) @@ -1317,10 +1353,10 @@ void LLFloaterWorldMap::teleport() && landmark->getRegionID(region_id)) { LLLandmark::requestRegionHandle( - gMessageSystem, - gAgent.getRegionHost(), - region_id, - NULL); + gMessageSystem, + gAgent.getRegionHost(), + region_id, + NULL); } } } @@ -1332,7 +1368,7 @@ void LLFloaterWorldMap::teleport() { make_ui_sound("UISndInvalidOp"); } - + // Do the teleport, which will also close the floater if (teleport_home) { @@ -1367,7 +1403,7 @@ void LLFloaterWorldMap::teleportToLandmark() { BOOL has_destination = FALSE; LLUUID destination_id; // Null means "home" - + if( LLTracker::getTrackedLandmarkAssetID() == sHomeID ) { has_destination = TRUE; @@ -1388,14 +1424,14 @@ void LLFloaterWorldMap::teleportToLandmark() if(landmark->getRegionID(region_id)) { LLLandmark::requestRegionHandle( - gMessageSystem, - gAgent.getRegionHost(), - region_id, - NULL); + gMessageSystem, + gAgent.getRegionHost(), + region_id, + NULL); } } } - + if( has_destination ) { gAgent.teleportViaLandmark( destination_id ); @@ -1428,12 +1464,12 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim) { return; } - + LLScrollListCtrl *list = getChild<LLScrollListCtrl>("search_results"); list->operateOnAll(LLCtrlListInterface::OP_DELETE); - + S32 name_length = mCompletingRegionName.length(); - + LLSD match; S32 num_results = 0; @@ -1443,7 +1479,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim) LLSimInfo* info = it->second; std::string sim_name_lower = info->getName(); LLStringUtil::toLower(sim_name_lower); - + if (sim_name_lower.substr(0, name_length) == mCompletingRegionName) { if (sim_name_lower == mCompletingRegionName) @@ -1459,12 +1495,12 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim) num_results++; } } - + if (found_null_sim) { mCompletingRegionName = ""; } - + // if match found, highlight it and go if (!match.isUndefined()) { @@ -1472,7 +1508,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim) getChild<LLUICtrl>("search_results")->setFocus(TRUE); onCommitSearchResult(); } - + // if we found nothing, say "none" if (num_results == 0) { @@ -1486,7 +1522,7 @@ void LLFloaterWorldMap::onCommitSearchResult() { LLCtrlListInterface *list = childGetListInterface("search_results"); if (!list) return; - + LLSD selected_value = list->getSelectedValue(); std::string sim_name = selected_value.asString(); if (sim_name.empty()) @@ -1494,19 +1530,19 @@ void LLFloaterWorldMap::onCommitSearchResult() return; } LLStringUtil::toLower(sim_name); - + std::map<U64, LLSimInfo*>::const_iterator it; for (it = LLWorldMap::getInstance()->getRegionMap().begin(); it != LLWorldMap::getInstance()->getRegionMap().end(); ++it) { LLSimInfo* info = it->second; - + if (info->isName(sim_name)) { LLVector3d pos_global = info->getGlobalOrigin(); - + const F64 SIM_COORD_DEFAULT = 128.0; LLVector3 pos_local(SIM_COORD_DEFAULT, SIM_COORD_DEFAULT, 0.0f); - + // Did this value come from a trackURL() request? if (!mCompletingRegionPos.isExactlyZero()) { @@ -1516,14 +1552,14 @@ void LLFloaterWorldMap::onCommitSearchResult() pos_global.mdV[VX] += (F64)pos_local.mV[VX]; pos_global.mdV[VY] += (F64)pos_local.mV[VY]; pos_global.mdV[VZ] = (F64)pos_local.mV[VZ]; - + getChild<LLUICtrl>("location")->setValue(sim_name); trackLocation(pos_global); setDefaultBtn("Teleport"); break; } } - + onShowTargetBtn(); } @@ -1531,15 +1567,15 @@ void LLFloaterWorldMap::onChangeMaturity() { bool can_access_mature = gAgent.canAccessMature(); bool can_access_adult = gAgent.canAccessAdult(); - + getChildView("events_mature_icon")->setVisible( can_access_mature); getChildView("events_mature_label")->setVisible( can_access_mature); getChildView("events_mature_chk")->setVisible( can_access_mature); - + getChildView("events_adult_icon")->setVisible( can_access_adult); getChildView("events_adult_label")->setVisible( can_access_adult); getChildView("events_adult_chk")->setVisible( can_access_adult); - + // disable mature / adult events. if (!can_access_mature) { diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 1628a421ec..783d9f4819 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -106,6 +106,11 @@ public: // teleport to the tracked item, if there is one void teleport(); void onChangeMaturity(); + + + //Slapp instigated avatar tracking + void avatarTrackFromSlapp( const LLUUID& id ); + protected: void onGoHome(); diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index c38cd4d090..62ba746a02 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -2429,6 +2429,7 @@ S32 LLFolderView::notify(const LLSD& info) { setFocus(true); selectFirstItem(); + scrollToShowSelection(); return 1; } @@ -2436,6 +2437,7 @@ S32 LLFolderView::notify(const LLSD& info) { setFocus(true); selectLastItem(); + scrollToShowSelection(); return 1; } } diff --git a/indra/newview/llgiveinventory.cpp b/indra/newview/llgiveinventory.cpp index 260e15c714..f990b9294d 100644 --- a/indra/newview/llgiveinventory.cpp +++ b/indra/newview/llgiveinventory.cpp @@ -128,7 +128,7 @@ bool LLGiveInventory::isInventoryGiveAcceptable(const LLInventoryItem* item) switch(item->getType()) { case LLAssetType::AT_OBJECT: - if (gAgentAvatarp->isWearingAttachment(item->getUUID())) + if (get_is_item_worn(item->getUUID())) { acceptable = false; } @@ -139,7 +139,7 @@ bool LLGiveInventory::isInventoryGiveAcceptable(const LLInventoryItem* item) BOOL copyable = false; if (item->getPermissions().allowCopyBy(gAgentID)) copyable = true; - if (!copyable && gAgentWearables.isWearingItem(item->getUUID())) + if (!copyable && get_is_item_worn(item->getUUID())) { acceptable = false; } diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp index d43b3a5d6e..842911ecc0 100644 --- a/indra/newview/llglsandbox.cpp +++ b/indra/newview/llglsandbox.cpp @@ -663,35 +663,27 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV x2 = x1 + PARCEL_GRID_STEP_METERS; y2 = y1; - if (gRenderForSelect) - { - LLColor4U color((U8)(GL_NAME_PARCEL_WALL >> 16), (U8)(GL_NAME_PARCEL_WALL >> 8), (U8)GL_NAME_PARCEL_WALL); - gGL.color4ubv(color.mV); - } - else - { - dy = (pos_y - y1) + DIST_OFFSET; - - if (pos_x < x1) - dx = pos_x - x1; - else if (pos_x > x2) - dx = pos_x - x2; - else - dx = 0; + dy = (pos_y - y1) + DIST_OFFSET; - dist = dx*dx+dy*dy; - - if (dist < MIN_DIST_SQ) - alpha = MAX_ALPHA; - else if (dist > MAX_DIST_SQ) - alpha = 0.0f; - else - alpha = 30/dist; - - alpha = llclamp(alpha, 0.0f, MAX_ALPHA); - - gGL.color4f(1.f, 1.f, 1.f, alpha); - } + if (pos_x < x1) + dx = pos_x - x1; + else if (pos_x > x2) + dx = pos_x - x2; + else + dx = 0; + + dist = dx*dx+dy*dy; + + if (dist < MIN_DIST_SQ) + alpha = MAX_ALPHA; + else if (dist > MAX_DIST_SQ) + alpha = 0.0f; + else + alpha = 30/dist; + + alpha = llclamp(alpha, 0.0f, MAX_ALPHA); + + gGL.color4f(1.f, 1.f, 1.f, alpha); if ((pos_y - y1) < 0) direction = SOUTH_MASK; else direction = NORTH_MASK; @@ -709,35 +701,27 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV x2 = x1; y2 = y1 + PARCEL_GRID_STEP_METERS; - if (gRenderForSelect) - { - LLColor4U color((U8)(GL_NAME_PARCEL_WALL >> 16), (U8)(GL_NAME_PARCEL_WALL >> 8), (U8)GL_NAME_PARCEL_WALL); - gGL.color4ubv(color.mV); - } + dx = (pos_x - x1) + DIST_OFFSET; + + if (pos_y < y1) + dy = pos_y - y1; + else if (pos_y > y2) + dy = pos_y - y2; + else + dy = 0; + + dist = dx*dx+dy*dy; + + if (dist < MIN_DIST_SQ) + alpha = MAX_ALPHA; + else if (dist > MAX_DIST_SQ) + alpha = 0.0f; else - { - dx = (pos_x - x1) + DIST_OFFSET; - - if (pos_y < y1) - dy = pos_y - y1; - else if (pos_y > y2) - dy = pos_y - y2; - else - dy = 0; - - dist = dx*dx+dy*dy; - - if (dist < MIN_DIST_SQ) - alpha = MAX_ALPHA; - else if (dist > MAX_DIST_SQ) - alpha = 0.0f; - else - alpha = 30/dist; - - alpha = llclamp(alpha, 0.0f, MAX_ALPHA); + alpha = 30/dist; + + alpha = llclamp(alpha, 0.0f, MAX_ALPHA); - gGL.color4f(1.f, 1.f, 1.f, alpha); - } + gGL.color4f(1.f, 1.f, 1.f, alpha); if ((pos_x - x1) > 0) direction = WEST_MASK; else direction = EAST_MASK; diff --git a/indra/newview/llhints.cpp b/indra/newview/llhints.cpp index 3f0deb98cd..c4dcaf11f9 100644 --- a/indra/newview/llhints.cpp +++ b/indra/newview/llhints.cpp @@ -33,6 +33,7 @@ #include "lltextbox.h" #include "llviewerwindow.h" #include "llviewercontrol.h" +#include "lliconctrl.h" #include "llsdparam.h" class LLHintPopup : public LLPanel @@ -80,7 +81,8 @@ public: up_arrow, right_arrow, down_arrow, - lower_left_arrow; + lower_left_arrow, + hint_image; Optional<S32> left_arrow_offset, up_arrow_offset, @@ -96,6 +98,7 @@ public: right_arrow("right_arrow"), down_arrow("down_arrow"), lower_left_arrow("lower_left_arrow"), + hint_image("hint_image"), left_arrow_offset("left_arrow_offset"), up_arrow_offset("up_arrow_offset"), right_arrow_offset("right_arrow_offset"), @@ -166,7 +169,15 @@ LLHintPopup::LLHintPopup(const LLHintPopup::Params& p) mDirection = p.target_params.direction; mTarget = p.target_params.target; } - buildFromFile( "panel_hint.xml", NULL, p); + if (p.hint_image.isProvided()) + { + buildFromFile("panel_hint_image.xml", NULL, p); + getChild<LLIconCtrl>("hint_image")->setImage(p.hint_image()); + } + else + { + buildFromFile( "panel_hint.xml", NULL, p); + } } BOOL LLHintPopup::postBuild() diff --git a/indra/newview/llhudicon.cpp b/indra/newview/llhudicon.cpp index aea8c5928b..568b0ae585 100644 --- a/indra/newview/llhudicon.cpp +++ b/indra/newview/llhudicon.cpp @@ -201,11 +201,6 @@ void LLHUDIcon::render() renderIcon(FALSE); } -void LLHUDIcon::renderForSelect() -{ - renderIcon(TRUE); -} - BOOL LLHUDIcon::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3* intersection) { if (mHidden) diff --git a/indra/newview/llhudicon.h b/indra/newview/llhudicon.h index 8d7b8d4288..644daa0299 100644 --- a/indra/newview/llhudicon.h +++ b/indra/newview/llhudicon.h @@ -52,7 +52,6 @@ friend class LLHUDObject; public: /*virtual*/ void render(); - /*virtual*/ void renderForSelect(); /*virtual*/ void markDead(); /*virtual*/ F32 getDistance() const { return mDistance; } diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp index 09200ee5be..5e762ee037 100644 --- a/indra/newview/llhudobject.cpp +++ b/indra/newview/llhudobject.cpp @@ -284,27 +284,6 @@ void LLHUDObject::renderAll() } // static -void LLHUDObject::renderAllForSelect() -{ - LLHUDObject *hud_objp; - - hud_object_list_t::iterator object_it; - for (object_it = sHUDObjects.begin(); object_it != sHUDObjects.end(); ) - { - hud_object_list_t::iterator cur_it = object_it++; - hud_objp = (*cur_it); - if (hud_objp->getNumRefs() == 1) - { - sHUDObjects.erase(cur_it); - } - else if (hud_objp->isVisible()) - { - hud_objp->renderForSelect(); - } - } -} - -// static void LLHUDObject::renderAllForTimer() { LLHUDObject *hud_objp; diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h index 4282ade34d..33e6394445 100644 --- a/indra/newview/llhudobject.h +++ b/indra/newview/llhudobject.h @@ -104,7 +104,6 @@ protected: ~LLHUDObject(); virtual void render() = 0; - virtual void renderForSelect() {}; virtual void renderForTimer() {}; protected: diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp index 6a423e529a..24a876c59a 100644 --- a/indra/newview/llhudtext.cpp +++ b/indra/newview/llhudtext.cpp @@ -113,36 +113,21 @@ void LLHUDText::render() if (!mOnHUDAttachment && sDisplayText) { LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - renderText(FALSE); + renderText(); } } -void LLHUDText::renderForSelect() -{ - if (!mOnHUDAttachment) - { - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - renderText(TRUE); - } -} - -void LLHUDText::renderText(BOOL for_select) +void LLHUDText::renderText() { if (!mVisible || mHidden) { return; } - // don't pick text - if (for_select) - { - return; - } - gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); - LLGLState gls_blend(GL_BLEND, for_select ? FALSE : TRUE); - LLGLState gls_alpha(GL_ALPHA_TEST, for_select ? FALSE : TRUE); + LLGLState gls_blend(GL_BLEND, TRUE); + LLGLState gls_alpha(GL_ALPHA_TEST, TRUE); LLColor4 shadow_color(0.f, 0.f, 0.f, 1.f); F32 alpha_factor = 1.f; @@ -209,9 +194,6 @@ void LLHUDText::renderText(BOOL for_select) mRadius = (width_vec + height_vec).magVec() * 0.5f; - LLCoordGL screen_pos; - LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE); - LLVector2 screen_offset; screen_offset = mPositionOffset; @@ -594,7 +576,7 @@ void LLHUDText::renderAllHUD() for (text_it = sVisibleHUDTextObjects.begin(); text_it != sVisibleHUDTextObjects.end(); ++text_it) { - (*text_it)->renderText(FALSE); + (*text_it)->renderText(); } } diff --git a/indra/newview/llhudtext.h b/indra/newview/llhudtext.h index f05ee4d594..36015d51f0 100644 --- a/indra/newview/llhudtext.h +++ b/indra/newview/llhudtext.h @@ -128,8 +128,7 @@ protected: LLHUDText(const U8 type); /*virtual*/ void render(); - /*virtual*/ void renderForSelect(); - void renderText(BOOL for_select); + void renderText(); static void updateAll(); S32 getMaxLines(); diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index e000abda2a..f74ae92a7b 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -470,7 +470,7 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) } //static -bool LLIMFloater::resetAllowedRectPadding(const LLSD& newvalue) +bool LLIMFloater::resetAllowedRectPadding() { //reset allowed rect right padding if "SidebarCameraMovement" option //or sidebar state changed @@ -482,10 +482,10 @@ void LLIMFloater::getAllowedRect(LLRect& rect) { if (sAllowedRectRightPadding == RECT_PADDING_NOT_INIT) //wasn't initialized { - gSavedSettings.getControl("SidebarCameraMovement")->getSignal()->connect(boost::bind(&LLIMFloater::resetAllowedRectPadding, _2)); + gSavedSettings.getControl("SidebarCameraMovement")->getSignal()->connect(boost::bind(&LLIMFloater::resetAllowedRectPadding)); LLSideTray* side_bar = LLSideTray::getInstance(); - side_bar->getCollapseSignal().connect(boost::bind(&LLIMFloater::resetAllowedRectPadding, _2)); + side_bar->setVisibleWidthChangeCallback(boost::bind(&LLIMFloater::resetAllowedRectPadding)); sAllowedRectRightPadding = RECT_PADDING_NEED_RECALC; } @@ -500,10 +500,7 @@ void LLIMFloater::getAllowedRect(LLRect& rect) if (gSavedSettings.getBOOL("SidebarCameraMovement") == FALSE) { - LLSideTray* side_bar = LLSideTray::getInstance(); - - if (side_bar->getVisible() && !side_bar->getCollapsed()) - sAllowedRectRightPadding += side_bar->getRect().getWidth(); + sAllowedRectRightPadding += LLSideTray::getInstance()->getVisibleWidth(); } } rect.mRight -= sAllowedRectRightPadding; @@ -680,8 +677,6 @@ void LLIMFloater::updateMessages() if (messages.size()) { -// LLUIColor chat_color = LLUIColorTable::instance().getColor("IMChatColor"); - LLSD chat_args; chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index e80e45e64a..5158f6c1f7 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -156,7 +156,7 @@ private: static void closeHiddenIMToasts(); - static bool resetAllowedRectPadding(const LLSD& newvalue); + static bool resetAllowedRectPadding(); //need to keep this static for performance issues static S32 sAllowedRectRightPadding; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 02a693b9a0..9623554200 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -131,6 +131,20 @@ void toast_callback(const LLSD& msg){ return; } + // *NOTE Skip toasting if the user disable it in preferences/debug settings ~Alexandrea + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession( + msg["session_id"]); + if (!gSavedSettings.getBOOL("EnableGroupChatPopups") + && session->isGroupSessionType()) + { + return; + } + if (!gSavedSettings.getBOOL("EnableIMChatPopups") + && !session->isGroupSessionType()) + { + return; + } + // Skip toasting if we have open window of IM with this session id LLIMFloater* open_im_floater = LLIMFloater::findInstance(msg["session_id"]); if (open_im_floater && open_im_floater->getVisible()) @@ -265,9 +279,27 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& void LLIMModel::LLIMSession::onAdHocNameCache(const LLAvatarName& av_name) { + if (av_name.mIsTemporaryName) + { + S32 separator_index = mName.rfind(" "); + std::string name = mName.substr(0, separator_index); + ++separator_index; + std::string conference_word = mName.substr(separator_index, mName.length()); + + // additional check that session name is what we expected + if ("Conference" == conference_word) + { LLStringUtil::format_map_t args; - args["[AGENT_NAME]"] = av_name.getCompleteName(); + args["[AGENT_NAME]"] = name; LLTrans::findString(mName, "conference-title-incoming", args); + } + } + else + { + LLStringUtil::format_map_t args; + args["[AGENT_NAME]"] = av_name.getCompleteName(); + LLTrans::findString(mName, "conference-title-incoming", args); + } } void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction) @@ -523,15 +555,14 @@ bool LLIMModel::LLIMSession::isOtherParticipantAvaline() void LLIMModel::LLIMSession::onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name) { - if (av_name.mLegacyFirstName.empty()) + if (av_name.mUsername.empty()) { - // if mLegacyFirstName is empty it means display names is off and the - // data came from the gCacheName, mDisplayName will be the legacy name - mHistoryFileName = LLCacheName::cleanFullName(av_name.mDisplayName); + // display names is off, use mDisplayName which will be the legacy name + mHistoryFileName = LLCacheName::buildUsername(av_name.mDisplayName); } else { - mHistoryFileName = LLCacheName::cleanFullName(av_name.getLegacyName()); + mHistoryFileName = av_name.mUsername; } } @@ -542,7 +573,12 @@ void LLIMModel::LLIMSession::buildHistoryFileName() //ad-hoc requires sophisticated chat history saving schemes if (isAdHoc()) { - //in case of outgoing ad-hoc sessions + /* in case of outgoing ad-hoc sessions we need to make specilized names + * if this naming system is ever changed then the filtering definitions in + * lllogchat.cpp need to be change acordingly so that the filtering for the + * date stamp code introduced in STORM-102 will work properly and not add + * a date stamp to the Ad-hoc conferences. + */ if (mInitialTargetIDs.size()) { std::set<LLUUID> sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end()); @@ -1064,17 +1100,27 @@ void LLIMModel::sendMessage(const std::string& utf8_text, if( session == 0)//??? shouldn't really happen { LLRecentPeople::instance().add(other_participant_id); + return; } - else + // IM_SESSION_INVITE means that this is an Ad-hoc incoming chat + // (it can be also Group chat but it is checked above) + // In this case mInitialTargetIDs contains Ad-hoc session ID and it should not be added + // to Recent People to prevent showing of an item with (???)(???). See EXT-8246. + // Concrete participants will be added into this list once they sent message in chat. + if (IM_SESSION_INVITE == dialog) return; + + if (IM_SESSION_CONFERENCE_START == dialog) // outgoing ad-hoc session + { + // Add only online members of conference to recent list (EXT-8658) + addSpeakersToRecent(im_session_id); + } + else // outgoing P2P session { - // IM_SESSION_INVITE means that this is an Ad-hoc incoming chat - // (it can be also Group chat but it is checked above) - // In this case mInitialTargetIDs contains Ad-hoc session ID and it should not be added - // to Recent People to prevent showing of an item with (???)(???). See EXT-8246. - // Concrete participants will be added into this list once they sent message in chat. - if (IM_SESSION_INVITE == dialog) return; - // Add only online members to recent (EXT-8658) - addSpeakersToRecent(im_session_id); + // Add the recepient of the session. + if (!session->mInitialTargetIDs.empty()) + { + LLRecentPeople::instance().add(*(session->mInitialTargetIDs.begin())); + } } } } @@ -2061,7 +2107,7 @@ void LLIncomingCallDialog::onOpen(const LLSD& key) void LLIncomingCallDialog::onAccept(void* user_data) { LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data; - self->processCallResponse(0); + processCallResponse(0, self->mPayload); self->closeFloater(); } @@ -2069,7 +2115,7 @@ void LLIncomingCallDialog::onAccept(void* user_data) void LLIncomingCallDialog::onReject(void* user_data) { LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data; - self->processCallResponse(1); + processCallResponse(1, self->mPayload); self->closeFloater(); } @@ -2077,20 +2123,21 @@ void LLIncomingCallDialog::onReject(void* user_data) void LLIncomingCallDialog::onStartIM(void* user_data) { LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data; - self->processCallResponse(2); + processCallResponse(2, self->mPayload); self->closeFloater(); } -void LLIncomingCallDialog::processCallResponse(S32 response) +// static +void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload) { if (!gIMMgr || gDisconnected) return; - LLUUID session_id = mPayload["session_id"].asUUID(); - LLUUID caller_id = mPayload["caller_id"].asUUID(); - std::string session_name = mPayload["session_name"].asString(); - EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger(); - LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)mPayload["inv_type"].asInteger(); + LLUUID session_id = payload["session_id"].asUUID(); + LLUUID caller_id = payload["caller_id"].asUUID(); + std::string session_name = payload["session_name"].asString(); + EInstantMessage type = (EInstantMessage)payload["type"].asInteger(); + LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger(); bool voice = true; switch(response) { @@ -2107,8 +2154,8 @@ void LLIncomingCallDialog::processCallResponse(S32 response) session_id = gIMMgr->addP2PSession( session_name, caller_id, - mPayload["session_handle"].asString(), - mPayload["session_uri"].asString()); + payload["session_handle"].asString(), + payload["session_uri"].asString()); if (voice) { @@ -2142,7 +2189,7 @@ void LLIncomingCallDialog::processCallResponse(S32 response) LLAvatarName av_name; if (LLAvatarNameCache::get(caller_id, &av_name)) { - correct_session_name = av_name.mDisplayName + " (" + av_name.mUsername + ")"; + correct_session_name = av_name.getCompleteName(); correct_session_name.append(ADHOC_NAME_SUFFIX); } } @@ -2172,10 +2219,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response) inv_type)); // send notification message to the corresponding chat - if (mPayload["notify_box_type"].asString() == "VoiceInviteGroup" || mPayload["notify_box_type"].asString() == "VoiceInviteAdHoc") + if (payload["notify_box_type"].asString() == "VoiceInviteGroup" || payload["notify_box_type"].asString() == "VoiceInviteAdHoc") { LLStringUtil::format_map_t string_args; - string_args["[NAME]"] = mPayload["caller_name"].asString(); + string_args["[NAME]"] = payload["caller_name"].asString(); std::string message = LLTrans::getString("name_started_call", string_args); LLIMModel::getInstance()->addMessageSilently(session_id, SYSTEM_FROM, LLUUID::null, message); } @@ -2192,7 +2239,7 @@ void LLIncomingCallDialog::processCallResponse(S32 response) { if(LLVoiceClient::getInstance()) { - std::string s = mPayload["session_handle"].asString(); + std::string s = payload["session_handle"].asString(); LLVoiceClient::getInstance()->declineInvite(s); } } @@ -2599,16 +2646,19 @@ void LLIMMgr::inviteToSession( std::string question_type = "VoiceInviteQuestionDefault"; BOOL ad_hoc_invite = FALSE; + BOOL voice_invite = FALSE; if(type == IM_SESSION_P2P_INVITE) { //P2P is different...they only have voice invitations notify_box_type = "VoiceInviteP2P"; + voice_invite = TRUE; } else if ( gAgent.isInGroup(session_id) ) { //only really old school groups have voice invitations notify_box_type = "VoiceInviteGroup"; question_type = "VoiceInviteQuestionGroup"; + voice_invite = TRUE; } else if ( inv_type == INVITATION_TYPE_VOICE ) { @@ -2616,6 +2666,7 @@ void LLIMMgr::inviteToSession( //and a voice ad-hoc notify_box_type = "VoiceInviteAdHoc"; ad_hoc_invite = TRUE; + voice_invite = TRUE; } else if ( inv_type == INVITATION_TYPE_IMMEDIATE ) { @@ -2639,23 +2690,21 @@ void LLIMMgr::inviteToSession( if (channelp && channelp->callStarted()) { // you have already started a call to the other user, so just accept the invite - LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 0); + LLIncomingCallDialog::processCallResponse(0, payload); return; } - if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite) + if (voice_invite) { - // is the inviter a friend? - if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL) + if ( // if we're rejecting all incoming call requests + gSavedSettings.getBOOL("VoiceCallsRejectAll") + // or we're rejecting non-friend voice calls and this isn't a friend + || (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)) + ) { - // if not, and we are ignoring voice invites from non-friends - // then silently decline - if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly")) - { - // invite not from a friend, so decline - LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 1); - return; - } + // silently decline the call + LLIncomingCallDialog::processCallResponse(1, payload); + return; } } diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 650d329e18..e765a8da2f 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -62,7 +62,7 @@ class LLIMModel : public LLSingleton<LLIMModel> { public: - struct LLIMSession + struct LLIMSession : public boost::signals2::trackable { typedef enum e_session_type { // for now we have 4 predefined types for a session @@ -542,6 +542,7 @@ public: static void onReject(void* user_data); static void onStartIM(void* user_data); + static void processCallResponse(S32 response, const LLSD& payload); private: void setCallerName(const std::string& ui_title, const std::string& ui_label, @@ -551,7 +552,6 @@ private: const std::string& call_type); /*virtual*/ void onLifetimeExpired(); - void processCallResponse(S32 response); }; class LLOutgoingCallDialog : public LLCallDialog diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 29dcb2c4d3..4ad1934264 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -47,12 +47,14 @@ #include "llvoiceclient.h" #include "llviewerobjectlist.h" #include "lltransientfloatermgr.h" +#include "llnotificationsutil.h" // Linden libraries #include "llfloater.h" #include "llfloaterreg.h" #include "llmenubutton.h" #include "lltextbox.h" +#include "lltoggleablemenu.h" #include "lltooltip.h" // positionViewNearMouse() #include "lltrans.h" #include "lluictrl.h" @@ -125,16 +127,20 @@ private: void onClickReport(); void onClickFreeze(); void onClickEject(); + void onClickKick(); + void onClickCSR(); void onClickZoomIn(); void onClickFindOnMap(); bool onVisibleFindOnMap(); - bool onVisibleFreezeEject(); + bool onVisibleEject(); + bool onVisibleFreeze(); bool onVisibleZoomIn(); void onClickMuteVolume(); void onVolumeChange(const LLSD& data); bool enableMute(); bool enableUnmute(); bool enableTeleportOffer(); + bool godModeEnabled(); // Is used to determine if "Add friend" option should be enabled in gear menu bool isNotFriend(); @@ -213,20 +219,21 @@ LLInspectAvatar::LLInspectAvatar(const LLSD& sd) mCommitCallbackRegistrar.add("InspectAvatar.Pay", boost::bind(&LLInspectAvatar::onClickPay, this)); mCommitCallbackRegistrar.add("InspectAvatar.Share", boost::bind(&LLInspectAvatar::onClickShare, this)); mCommitCallbackRegistrar.add("InspectAvatar.ToggleMute", boost::bind(&LLInspectAvatar::onToggleMute, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Freeze", - boost::bind(&LLInspectAvatar::onClickFreeze, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Eject", - boost::bind(&LLInspectAvatar::onClickEject, this)); + mCommitCallbackRegistrar.add("InspectAvatar.Freeze", boost::bind(&LLInspectAvatar::onClickFreeze, this)); + mCommitCallbackRegistrar.add("InspectAvatar.Eject", boost::bind(&LLInspectAvatar::onClickEject, this)); + mCommitCallbackRegistrar.add("InspectAvatar.Kick", boost::bind(&LLInspectAvatar::onClickKick, this)); + mCommitCallbackRegistrar.add("InspectAvatar.CSR", boost::bind(&LLInspectAvatar::onClickCSR, this)); mCommitCallbackRegistrar.add("InspectAvatar.Report", boost::bind(&LLInspectAvatar::onClickReport, this)); mCommitCallbackRegistrar.add("InspectAvatar.FindOnMap", boost::bind(&LLInspectAvatar::onClickFindOnMap, this)); mCommitCallbackRegistrar.add("InspectAvatar.ZoomIn", boost::bind(&LLInspectAvatar::onClickZoomIn, this)); mCommitCallbackRegistrar.add("InspectAvatar.DisableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, false)); mCommitCallbackRegistrar.add("InspectAvatar.EnableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, true)); + + mEnableCallbackRegistrar.add("InspectAvatar.EnableGod", boost::bind(&LLInspectAvatar::godModeEnabled, this)); mEnableCallbackRegistrar.add("InspectAvatar.VisibleFindOnMap", boost::bind(&LLInspectAvatar::onVisibleFindOnMap, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleFreezeEject", - boost::bind(&LLInspectAvatar::onVisibleFreezeEject, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleZoomIn", - boost::bind(&LLInspectAvatar::onVisibleZoomIn, this)); + mEnableCallbackRegistrar.add("InspectAvatar.VisibleEject", boost::bind(&LLInspectAvatar::onVisibleEject, this)); + mEnableCallbackRegistrar.add("InspectAvatar.VisibleFreeze", boost::bind(&LLInspectAvatar::onVisibleFreeze, this)); + mEnableCallbackRegistrar.add("InspectAvatar.VisibleZoomIn", boost::bind(&LLInspectAvatar::onVisibleZoomIn, this)); mEnableCallbackRegistrar.add("InspectAvatar.Gear.Enable", boost::bind(&LLInspectAvatar::isNotFriend, this)); mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableCall", boost::bind(&LLAvatarActions::canCall)); mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableTeleportOffer", boost::bind(&LLInspectAvatar::enableTeleportOffer, this)); @@ -402,8 +409,8 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data) // if neither the gear menu or self gear menu are open void LLInspectAvatar::onMouseLeave(S32 x, S32 y, MASK mask) { - LLMenuGL* gear_menu = getChild<LLMenuButton>("gear_btn")->getMenu(); - LLMenuGL* gear_menu_self = getChild<LLMenuButton>("gear_self_btn")->getMenu(); + LLToggleableMenu* gear_menu = getChild<LLMenuButton>("gear_btn")->getMenu(); + LLToggleableMenu* gear_menu_self = getChild<LLMenuButton>("gear_self_btn")->getMenu(); if ( gear_menu && gear_menu->getVisible() && gear_menu_self && gear_menu_self->getVisible() ) { @@ -655,11 +662,18 @@ bool LLInspectAvatar::onVisibleFindOnMap() return gAgent.isGodlike() || is_agent_mappable(mAvatarID); } -bool LLInspectAvatar::onVisibleFreezeEject() +bool LLInspectAvatar::onVisibleEject() { return enable_freeze_eject( LLSD(mAvatarID) ); } +bool LLInspectAvatar::onVisibleFreeze() +{ + // either user is a god and can do long distance freeze + // or check for target proximity and permissions + return gAgent.isGodlike() || enable_freeze_eject(LLSD(mAvatarID)); +} + bool LLInspectAvatar::onVisibleZoomIn() { return gObjectList.findObject(mAvatarID); @@ -724,9 +738,41 @@ void LLInspectAvatar::onClickReport() closeFloater(); } +bool godlike_freeze(const LLSD& notification, const LLSD& response) +{ + LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + switch (option) + { + case 0: + LLAvatarActions::freeze(avatar_id); + break; + case 1: + LLAvatarActions::unfreeze(avatar_id); + break; + default: + break; + } + + return false; +} + void LLInspectAvatar::onClickFreeze() { - handle_avatar_freeze( LLSD(mAvatarID) ); + if (gAgent.isGodlike()) + { + // use godlike freeze-at-a-distance, with confirmation + LLNotificationsUtil::add("FreezeAvatar", + LLSD(), + LLSD().with("avatar_id", mAvatarID), + godlike_freeze); + } + else + { + // use default "local" version of freezing that requires avatar to be in range + handle_avatar_freeze( LLSD(mAvatarID) ); + } closeFloater(); } @@ -736,6 +782,20 @@ void LLInspectAvatar::onClickEject() closeFloater(); } +void LLInspectAvatar::onClickKick() +{ + LLAvatarActions::kick(mAvatarID); + closeFloater(); +} + +void LLInspectAvatar::onClickCSR() +{ + std::string name; + gCacheName->getFullName(mAvatarID, name); + LLAvatarActions::csr(mAvatarID, name); + closeFloater(); +} + void LLInspectAvatar::onClickZoomIn() { handle_zoom_to_object(mAvatarID); @@ -784,6 +844,11 @@ bool LLInspectAvatar::enableTeleportOffer() return LLAvatarActions::canOfferTeleport(mAvatarID); } +bool LLInspectAvatar::godModeEnabled() +{ + return gAgent.isGodlike(); +} + ////////////////////////////////////////////////////////////////////////////// // LLInspectAvatarUtil ////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp index 532ffca4be..ee076f68ea 100644 --- a/indra/newview/llinspectobject.cpp +++ b/indra/newview/llinspectobject.cpp @@ -47,6 +47,7 @@ #include "llsafehandle.h" #include "llsidetray.h" #include "lltextbox.h" // for description truncation +#include "lltoggleablemenu.h" #include "lltrans.h" #include "llui.h" // positionViewNearMouse() #include "lluictrl.h" @@ -568,7 +569,7 @@ void LLInspectObject::updateSecureBrowsing() // if the gear menu is not open void LLInspectObject::onMouseLeave(S32 x, S32 y, MASK mask) { - LLMenuGL* gear_menu = getChild<LLMenuButton>("gear_btn")->getMenu(); + LLToggleableMenu* gear_menu = getChild<LLMenuButton>("gear_btn")->getMenu(); if ( gear_menu && gear_menu->getVisible() ) { return; diff --git a/indra/newview/llinspecttoast.cpp b/indra/newview/llinspecttoast.cpp index 58b3f0309f..d7b82667d1 100644 --- a/indra/newview/llinspecttoast.cpp +++ b/indra/newview/llinspecttoast.cpp @@ -46,6 +46,7 @@ public: virtual ~LLInspectToast(); /*virtual*/ void onOpen(const LLSD& notification_id); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); private: void onToastDestroy(LLToast * toast); @@ -73,6 +74,7 @@ LLInspectToast::~LLInspectToast() LLTransientFloaterMgr::getInstance()->removeControlView(this); } +// virtual void LLInspectToast::onOpen(const LLSD& notification_id) { LLInspect::onOpen(notification_id); @@ -103,6 +105,15 @@ void LLInspectToast::onOpen(const LLSD& notification_id) LLUI::positionViewNearMouse(this); } +// virtual +BOOL LLInspectToast::handleToolTip(S32 x, S32 y, MASK mask) +{ + // We don't like the way LLInspect handles tooltips + // (black tooltips look weird), + // so force using the default implementation (STORM-511). + return LLFloater::handleToolTip(x, y, mask); +} + void LLInspectToast::onToastDestroy(LLToast * toast) { closeFloater(false); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index b15dcd993a..5108f68592 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -104,6 +104,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); void teleport_via_landmark(const LLUUID& asset_id); +static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); // +=================================================+ // | LLInvFVBridge | @@ -1643,16 +1644,19 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, const BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL) && (LLToolDragAndDrop::SOURCE_AGENT == source); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + BOOL accept = FALSE; if (is_agent_inventory) { const LLUUID &cat_id = inv_cat->getUUID(); const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); - const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, 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_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); //-------------------------------------------------------------------------------- // Determine if folder can be moved. @@ -1690,6 +1694,21 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } } } + if (move_is_into_landmarks) + { + for (S32 i=0; i < descendent_items.count(); ++i) + { + LLViewerInventoryItem* item = descendent_items[i]; + + // Don't move anything except landmarks and categories into Landmarks folder. + // We use getType() instead of getActua;Type() to allow links to landmarks and folders. + if (LLAssetType::AT_LANDMARK != item->getType() && LLAssetType::AT_CATEGORY != item->getType()) + { + is_movable = FALSE; + break; // It's generally movable, but not into Landmarks. + } + } + } // //-------------------------------------------------------------------------------- @@ -1777,6 +1796,17 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, LLUUID category_id = mUUID; accept = move_inv_category_world_to_agent(object_id, category_id, drop); } + else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) + { + // Accept folders that contain complete outfits. + accept = move_is_into_current_outfit && LLAppearanceMgr::instance().getCanMakeFolderIntoOutfit(inv_cat->getUUID()); + + if (accept && drop) + { + LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, true, false); + } + } + return accept; } @@ -2312,6 +2342,10 @@ void LLFolderBridge::pasteFromClipboard() LLInventoryModel* model = getInventoryModel(); if(model && isClipboardPasteable()) { + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const LLUUID parent_id(mUUID); LLDynamicArray<LLUUID> objects; @@ -2324,7 +2358,14 @@ void LLFolderBridge::pasteFromClipboard() LLInventoryItem *item = model->getItem(item_id); if (item) { - if(LLInventoryClipboard::instance().isCutMode()) + if (move_is_into_current_outfit || move_is_into_outfit) + { + if (can_move_to_outfit(item, move_is_into_current_outfit)) + { + dropToOutfit(item, move_is_into_current_outfit); + } + } + else if(LLInventoryClipboard::instance().isCutMode()) { // move_inventory_item() is not enough, //we have to update inventory locally too @@ -2352,9 +2393,13 @@ void LLFolderBridge::pasteFromClipboard() void LLFolderBridge::pasteLinkFromClipboard() { - const LLInventoryModel* model = getInventoryModel(); + LLInventoryModel* model = getInventoryModel(); if(model) { + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const LLUUID parent_id(mUUID); LLDynamicArray<LLUUID> objects; @@ -2364,7 +2409,15 @@ void LLFolderBridge::pasteLinkFromClipboard() ++iter) { const LLUUID &object_id = (*iter); - if (LLInventoryCategory *cat = model->getCategory(object_id)) + if (move_is_into_current_outfit || move_is_into_outfit) + { + LLInventoryItem *item = model->getItem(object_id); + if (item && can_move_to_outfit(item, move_is_into_current_outfit)) + { + dropToOutfit(item, move_is_into_current_outfit); + } + } + else if (LLInventoryCategory *cat = model->getCategory(object_id)) { const std::string empty_description = ""; link_inventory_item( @@ -2661,6 +2714,8 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data) { + LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + //llinfos << "LLFolderBridge::dragOrDrop()" << llendl; BOOL accept = FALSE; switch(cargo_type) @@ -2676,9 +2731,24 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, case DAD_BODYPART: case DAD_ANIMATION: case DAD_GESTURE: + accept = dragItemIntoFolder(inv_item, drop); + break; case DAD_LINK: - accept = dragItemIntoFolder((LLInventoryItem*)cargo_data, - drop); + // DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER. + // If we have an item of AT_LINK_FOLDER type we should process the linked + // category being dragged or dropped into folder. + if (inv_item && LLAssetType::AT_LINK_FOLDER == inv_item->getActualType()) + { + LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID()); + if (linked_category) + { + accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop); + } + } + else + { + accept = dragItemIntoFolder(inv_item, drop); + } break; case DAD_CATEGORY: if (LLFriendCardsManager::instance().isAnyFriendCategory(mUUID)) @@ -2862,6 +2932,7 @@ static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_curr { if ((inv_item->getInventoryType() != LLInventoryType::IT_WEARABLE) && (inv_item->getInventoryType() != LLInventoryType::IT_GESTURE) && + (inv_item->getInventoryType() != LLInventoryType::IT_ATTACHMENT) && (inv_item->getInventoryType() != LLInventoryType::IT_OBJECT)) { return FALSE; @@ -2875,6 +2946,24 @@ static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_curr return TRUE; } +// Returns TRUE if item is a landmark or a link to a landmark +// and can be moved to Favorites or Landmarks folder. +static BOOL can_move_to_landmarks(LLInventoryItem* inv_item) +{ + // Need to get the linked item to know its type because LLInventoryItem::getType() + // returns actual type AT_LINK for links, not the asset type of a linked item. + if (LLAssetType::AT_LINK == inv_item->getType()) + { + LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID()); + if (linked_item) + { + return LLAssetType::AT_LANDMARK == linked_item->getType(); + } + } + + return LLAssetType::AT_LANDMARK == inv_item->getType(); +} + void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item) { // use callback to rearrange favorite landmarks after adding @@ -2931,9 +3020,12 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, 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 BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_favorites = (mUUID == favorites_id); const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); BOOL accept = FALSE; @@ -2944,7 +3036,6 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); const BOOL move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID()); - const BOOL folder_allows_reorder = (mUUID == favorites_id); //-------------------------------------------------------------------------------- // Determine if item can be moved. @@ -2990,12 +3081,16 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, if (!is_movable) accept = FALSE; - if ((mUUID == inv_item->getParentUUID()) && !folder_allows_reorder) + if ((mUUID == inv_item->getParentUUID()) && !move_is_into_favorites) accept = FALSE; if (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) + { + accept = can_move_to_landmarks(inv_item); + } if(accept && drop) { @@ -3021,8 +3116,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // // REORDER - // (only reorder the item) - if ((mUUID == inv_item->getParentUUID()) && folder_allows_reorder) + // (only reorder the item in Favorites folder) + if ((mUUID == inv_item->getParentUUID()) && move_is_into_favorites) { LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; @@ -3036,7 +3131,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // FAVORITES folder // (copy the item) - else if (favorites_id == mUUID) + else if (move_is_into_favorites) { dropToFavorites(inv_item); } @@ -3101,6 +3196,13 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = FALSE; } + // 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) + && !can_move_to_landmarks(inv_item)) + { + accept = FALSE; + } if(drop && accept) { @@ -3146,12 +3248,18 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); } + // 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) + { + accept = can_move_to_landmarks(inv_item); + } if (accept && drop) { // FAVORITES folder // (copy the item) - if (favorites_id == mUUID) + if (move_is_into_favorites) { dropToFavorites(inv_item); } @@ -5236,11 +5344,6 @@ void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t disabled_items, items = getMenuItems(); - items.erase(std::remove(items.begin(), items.end(), std::string("New Body Parts")), items.end()); - items.erase(std::remove(items.begin(), items.end(), std::string("New Clothes")), items.end()); - items.erase(std::remove(items.begin(), items.end(), std::string("New Note")), items.end()); - items.erase(std::remove(items.begin(), items.end(), std::string("New Gesture")), items.end()); - items.erase(std::remove(items.begin(), items.end(), std::string("New Script")), items.end()); items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); hide_context_entries(menu, items, disabled_items); diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index f3d9639dee..ef20869114 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -487,12 +487,9 @@ bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(const LLInventoryIte return false; break; case LLAssetType::AT_OBJECT: - if (isAgentAvatarValid() && !gAgentAvatarp->isWearingAttachment(item->getUUID())) - return true; - break; case LLAssetType::AT_BODYPART: case LLAssetType::AT_CLOTHING: - if(!gAgentWearables.isWearingItem(item->getUUID())) + if (!get_is_item_worn(item->getUUID())) return true; break; default: diff --git a/indra/newview/llinventoryicon.cpp b/indra/newview/llinventoryicon.cpp index 7216d61e7f..3f4f33e88d 100644 --- a/indra/newview/llinventoryicon.cpp +++ b/indra/newview/llinventoryicon.cpp @@ -50,7 +50,7 @@ public: LLIconDictionary::LLIconDictionary() { addEntry(LLInventoryIcon::ICONNAME_TEXTURE, new IconEntry("Inv_Texture")); - addEntry(LLInventoryIcon::ICONNAME_SOUND, new IconEntry("Inv_Texture")); + addEntry(LLInventoryIcon::ICONNAME_SOUND, new IconEntry("Inv_Sound")); addEntry(LLInventoryIcon::ICONNAME_CALLINGCARD_ONLINE, new IconEntry("Inv_CallingCard")); addEntry(LLInventoryIcon::ICONNAME_CALLINGCARD_OFFLINE, new IconEntry("Inv_CallingCard")); addEntry(LLInventoryIcon::ICONNAME_LANDMARK, new IconEntry("Inv_Landmark")); @@ -83,7 +83,7 @@ LLIconDictionary::LLIconDictionary() addEntry(LLInventoryIcon::ICONNAME_GESTURE, new IconEntry("Inv_Gesture")); addEntry(LLInventoryIcon::ICONNAME_LINKITEM, new IconEntry("Inv_LinkItem")); - addEntry(LLInventoryIcon::ICONNAME_LINKFOLDER, new IconEntry("Inv_LinkItem")); + addEntry(LLInventoryIcon::ICONNAME_LINKFOLDER, new IconEntry("Inv_LinkFolder")); addEntry(LLInventoryIcon::ICONNAME_INVALID, new IconEntry("Inv_Invalid")); diff --git a/indra/newview/llinventorylistitem.cpp b/indra/newview/llinventorylistitem.cpp index 225d0288a9..3e0849a795 100644 --- a/indra/newview/llinventorylistitem.cpp +++ b/indra/newview/llinventorylistitem.cpp @@ -97,7 +97,8 @@ void LLPanelInventoryListItemBase::draw() LLRect separator_rect = getLocalRect(); separator_rect.mTop = separator_rect.mBottom; separator_rect.mBottom -= mSeparatorImage->getHeight(); - mSeparatorImage->draw(separator_rect); + F32 alpha = getCurrentTransparency(); + mSeparatorImage->draw(separator_rect, UI_VERTEX_COLOR % alpha); } LLPanel::draw(); diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 50adae09c0..5a9d1524f3 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -290,7 +290,10 @@ void LLInventoryPanel::modelChanged(U32 mask) const LLUUID& item_id = (*items_iter); const LLInventoryObject* model_item = model->getObject(item_id); LLFolderViewItem* view_item = mFolderRoot->getItemByID(item_id); - LLFolderViewFolder* view_folder = mFolderRoot->getFolderByID(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<LLFolderViewFolder*>(view_item); ////////////////////////////// // LABEL Operation @@ -918,6 +921,8 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open) { S32 z_min = S32_MAX; LLInventoryPanel* res = NULL; + LLFloater* active_inv_floaterp = NULL; + // A. If the inventory side panel is open, use that preferably. if (is_inventorysp_active()) { @@ -938,6 +943,7 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open) { res = inventorySP->getActivePanel(); z_min = gFloaterView->getZOrder(inv_floater); + active_inv_floaterp = inv_floater; } else { @@ -957,10 +963,19 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open) { res = iv->getPanel(); z_min = z_order; + active_inv_floaterp = iv; } } } - if (res) return res; + + if (res) + { + // Make sure the floater is not minimized (STORM-438). + if (active_inv_floaterp && active_inv_floaterp->isMinimized()) + active_inv_floaterp->setMinimized(FALSE); + + return res; + } // C. If no panels are open and we don't want to force open a panel, then just abort out. if (!auto_open) return NULL; diff --git a/indra/newview/lllistcontextmenu.cpp b/indra/newview/lllistcontextmenu.cpp index ea744072d2..6421ab42bf 100644 --- a/indra/newview/lllistcontextmenu.cpp +++ b/indra/newview/lllistcontextmenu.cpp @@ -36,7 +36,6 @@ #include "llviewermenu.h" // for LLViewerMenuHolderGL LLListContextMenu::LLListContextMenu() -: mMenu(NULL) { } @@ -51,23 +50,22 @@ LLListContextMenu::~LLListContextMenu() // of mMenu has already been deleted except of using LLHandle. EXT-4762. if (!mMenuHandle.isDead()) { - mMenu->die(); - mMenu = NULL; + mMenuHandle.get()->die(); } } void LLListContextMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y) { - if (mMenu) + LLContextMenu* menup = mMenuHandle.get(); + if (menup) { //preventing parent (menu holder) from deleting already "dead" context menus on exit - LLView* parent = mMenu->getParent(); + LLView* parent = menup->getParent(); if (parent) { - parent->removeChild(mMenu); + parent->removeChild(menup); } - delete mMenu; - mMenu = NULL; + delete menup; mUUIDs.clear(); } @@ -79,23 +77,23 @@ void LLListContextMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 mUUIDs.resize(uuids.size()); std::copy(uuids.begin(), uuids.end(), mUUIDs.begin()); - mMenu = createMenu(); - if (!mMenu) + menup = createMenu(); + if (!menup) { llwarns << "Context menu creation failed" << llendl; return; } - mMenuHandle = mMenu->getHandle(); - mMenu->show(x, y); - LLMenuGL::showPopup(spawning_view, mMenu, x, y); + mMenuHandle = menup->getHandle(); + menup->show(x, y); + LLMenuGL::showPopup(spawning_view, menup, x, y); } void LLListContextMenu::hide() { - if(mMenu) + if(mMenuHandle.get()) { - mMenu->hide(); + mMenuHandle.get()->hide(); } } diff --git a/indra/newview/lllistcontextmenu.h b/indra/newview/lllistcontextmenu.h index 5dedc30b0c..fabd68ee20 100644 --- a/indra/newview/lllistcontextmenu.h +++ b/indra/newview/lllistcontextmenu.h @@ -71,8 +71,7 @@ protected: static void handleMultiple(functor_t functor, const uuid_vec_t& ids); uuid_vec_t mUUIDs; - LLContextMenu* mMenu; - LLHandle<LLView> mMenuHandle; + LLHandle<LLContextMenu> mMenuHandle; }; #endif // LL_LLLISTCONTEXTMENU_H diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 8c70b1e973..0121bbb1ed 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -89,6 +89,16 @@ const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+ */ const static boost::regex NAME_AND_TEXT("([^:]+[:]{1})?(\\s*)(.*)"); +/**
+ * These are recognizers for matching the names of ad-hoc conferences when generating the log file name
+ * On invited side, an ad-hoc is named like "<first name> <last name> Conference 2010/11/19 03:43 f0f4"
+ * On initiating side, an ad-hoc is named like Ad-hoc Conference hash<hash>"
+ * If the naming system for ad-hoc conferences are change in LLIMModel::LLIMSession::buildHistoryFileName()
+ * then these definition need to be adjusted as well.
+ */
+const static boost::regex INBOUND_CONFERENCE("^[a-zA-Z]{1,31} [a-zA-Z]{1,31} Conference [0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2} [0-9a-f]{4}");
+const static boost::regex OUTBOUND_CONFERENCE("^Ad-hoc Conference hash[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}");
+ //is used to parse complex object names like "Xstreet SL Terminal v2.2.5 st" const static std::string NAME_TEXT_DIVIDER(": "); @@ -182,15 +192,43 @@ private: //static std::string LLLogChat::makeLogFileName(std::string filename) { + /** + * Testing for in bound and out bound ad-hoc file names + * if it is then skip date stamping. + **/ + //LL_INFOS("") << "Befor:" << filename << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ + boost::match_results<std::string::const_iterator> matches; + bool inboundConf = boost::regex_match(filename, matches, INBOUND_CONFERENCE); + bool outboundConf = boost::regex_match(filename, matches, OUTBOUND_CONFERENCE); + if (!(inboundConf || outboundConf)) + { + if( gSavedPerAccountSettings.getBOOL("LogFileNamewithDate") ) + { + time_t now; + time(&now); + char dbuffer[20]; /* Flawfinder: ignore */ + if (filename == "chat") + { + strftime(dbuffer, 20, "-%Y-%m-%d", localtime(&now)); + } + else + { + strftime(dbuffer, 20, "-%Y-%m", localtime(&now)); + } + filename += dbuffer; + } + } + //LL_INFOS("") << "After:" << filename << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ filename = cleanFileName(filename); filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS,filename); filename += ".txt"; + //LL_INFOS("") << "Full:" << filename << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ return filename; } std::string LLLogChat::cleanFileName(std::string filename) { - std::string invalidChars = "\"\'\\/?*:<>|"; + std::string invalidChars = "\"\'\\/?*:.<>|"; std::string::size_type position = filename.find_first_of(invalidChars); while (position != filename.npos) { @@ -354,10 +392,19 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& me llwarns << "Session name is Empty!" << llendl; return ; } - - LLFILE* fptr = LLFile::fopen(makeLogFileName(file_name), "r"); /*Flawfinder: ignore*/ - if (!fptr) return; //No previous conversation with this name. - + //LL_INFOS("") << "Loading:" << file_name << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ + //LL_INFOS("") << "Current:" << makeLogFileName(file_name) << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ + LLFILE* fptr = LLFile::fopen(makeLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + fptr = LLFile::fopen(oldLogFileName(file_name), "r");/*Flawfinder: ignore*/ + if (!fptr) + { + if (!fptr) return; //No previous conversation with this name. + } + } + + //LL_INFOS("") << "Reading:" << file_name << LL_ENDL; char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/ char *bptr; S32 len; @@ -544,3 +591,32 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im) im[IM_TEXT] = name_and_text[IDX_TEXT]; return true; //parsed name and message text, maybe have a timestamp too } +std::string LLLogChat::oldLogFileName(std::string filename) +{ + std::string scanResult; + std::string directory = gDirUtilp->getPerAccountChatLogsDir();/* get Users log directory */ + directory += gDirUtilp->getDirDelimiter();/* add final OS dependent delimiter */ + filename=cleanFileName(filename);/* lest make shure the file name has no invalad charecters befor making the pattern */ + std::string pattern = (filename+(( filename == "chat" ) ? "-???\?-?\?-??.txt" : "-???\?-??.txt"));/* create search pattern*/ + //LL_INFOS("") << "Checking:" << directory << " for " << pattern << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ + std::vector<std::string> allfiles; + + while (gDirUtilp->getNextFileInDir(directory, pattern, scanResult)) + { + //LL_INFOS("") << "Found :" << scanResult << LL_ENDL; + allfiles.push_back(scanResult); + } + + if (allfiles.size() == 0) // if no result from date search, return generic filename + { + scanResult = directory + filename + ".txt"; + } + else + { + std::sort(allfiles.begin(), allfiles.end()); + scanResult = directory + allfiles.back(); + // thisfile is now the most recent version of the file. + } + //LL_INFOS("") << "Reading:" << scanResult << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ + return scanResult; +} diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index 6958d56311..27752452c9 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -41,6 +41,10 @@ public: }; static std::string timestamp(bool withdate = false); static std::string makeLogFileName(std::string(filename)); + /** + *Add functions to get old and non date stamped file names when needed + */ + static std::string oldLogFileName(std::string(filename)); static void saveHistory(const std::string& filename, const std::string& from, const LLUUID& from_id, diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 029e700c4c..efb2e9c0fd 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -42,24 +42,432 @@ // newview #include "llviewernetwork.h" #include "llviewercontrol.h" +#include "llversioninfo.h" #include "llslurl.h" #include "llstartup.h" #include "llfloaterreg.h" #include "llnotifications.h" #include "llwindow.h" #include "llviewerwindow.h" +#include "llprogressview.h" #if LL_LINUX || LL_SOLARIS #include "lltrans.h" #endif #include "llsecapi.h" #include "llstartup.h" #include "llmachineid.h" +#include "llupdaterservice.h" +#include "llevents.h" +#include "llnotificationsutil.h" +#include "llappviewer.h" + +#include <boost/scoped_ptr.hpp> +#include <sstream> + +class LLLoginInstance::Disposable { +public: + virtual ~Disposable() {} +}; + +namespace { + class MandatoryUpdateMachine: + public LLLoginInstance::Disposable + { + public: + MandatoryUpdateMachine(LLLoginInstance & loginInstance, LLUpdaterService & updaterService); + + void start(void); + + private: + class State; + class CheckingForUpdate; + class Error; + class ReadyToInstall; + class StartingUpdaterService; + class WaitingForDownload; + + LLLoginInstance & mLoginInstance; + boost::scoped_ptr<State> mState; + LLUpdaterService & mUpdaterService; + + void setCurrentState(State * newState); + }; + + + class MandatoryUpdateMachine::State { + public: + virtual ~State() {} + virtual void enter(void) {} + virtual void exit(void) {} + }; + + + class MandatoryUpdateMachine::CheckingForUpdate: + public MandatoryUpdateMachine::State + { + public: + CheckingForUpdate(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + + private: + LLTempBoundListener mConnection; + MandatoryUpdateMachine & mMachine; + LLProgressView * mProgressView; + + bool onEvent(LLSD const & event); + }; + + + class MandatoryUpdateMachine::Error: + public MandatoryUpdateMachine::State + { + public: + Error(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + void onButtonClicked(const LLSD &, const LLSD &); + + private: + MandatoryUpdateMachine & mMachine; + }; + + + class MandatoryUpdateMachine::ReadyToInstall: + public MandatoryUpdateMachine::State + { + public: + ReadyToInstall(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + + private: + MandatoryUpdateMachine & mMachine; + }; + + + class MandatoryUpdateMachine::StartingUpdaterService: + public MandatoryUpdateMachine::State + { + public: + StartingUpdaterService(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + void onButtonClicked(const LLSD & uiform, const LLSD & result); + private: + MandatoryUpdateMachine & mMachine; + }; + + + class MandatoryUpdateMachine::WaitingForDownload: + public MandatoryUpdateMachine::State + { + public: + WaitingForDownload(MandatoryUpdateMachine & machine); + + virtual void enter(void); + virtual void exit(void); + + private: + LLTempBoundListener mConnection; + MandatoryUpdateMachine & mMachine; + LLProgressView * mProgressView; + + bool onEvent(LLSD const & event); + }; +} static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback"; static const char * const TOS_LISTENER_NAME = "lllogininstance_tos"; std::string construct_start_string(); + + +// MandatoryUpdateMachine +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::MandatoryUpdateMachine(LLLoginInstance & loginInstance, LLUpdaterService & updaterService): + mLoginInstance(loginInstance), + mUpdaterService(updaterService) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::start(void) +{ + llinfos << "starting manditory update machine" << llendl; + + if(mUpdaterService.isChecking()) { + switch(mUpdaterService.getState()) { + case LLUpdaterService::UP_TO_DATE: + mUpdaterService.stopChecking(); + mUpdaterService.startChecking(); + // Fall through. + case LLUpdaterService::INITIAL: + case LLUpdaterService::CHECKING_FOR_UPDATE: + setCurrentState(new CheckingForUpdate(*this)); + break; + case LLUpdaterService::TEMPORARY_ERROR: + setCurrentState(new Error(*this)); + break; + case LLUpdaterService::DOWNLOADING: + setCurrentState(new WaitingForDownload(*this)); + break; + case LLUpdaterService::TERMINAL: + if(LLUpdaterService::updateReadyToInstall()) { + setCurrentState(new ReadyToInstall(*this)); + } else { + setCurrentState(new Error(*this)); + } + break; + case LLUpdaterService::FAILURE: + setCurrentState(new Error(*this)); + break; + default: + llassert(!"unpossible case"); + break; + } + } else { + setCurrentState(new StartingUpdaterService(*this)); + } +} + + +void MandatoryUpdateMachine::setCurrentState(State * newStatePointer) +{ + { + boost::scoped_ptr<State> newState(newStatePointer); + if(mState != 0) mState->exit(); + mState.swap(newState); + + // Old state will be deleted on exit from this block before the new state + // is entered. + } + if(mState != 0) mState->enter(); +} + + + +// MandatoryUpdateMachine::CheckingForUpdate +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::CheckingForUpdate::CheckingForUpdate(MandatoryUpdateMachine & machine): + mMachine(machine) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::CheckingForUpdate::enter(void) +{ + llinfos << "entering checking for update" << llendl; + + mProgressView = gViewerWindow->getProgressView(); + mProgressView->setMessage("Looking for update..."); + mProgressView->setText("There is a required update for your Second Life installation."); + mProgressView->setPercent(0); + mProgressView->setVisible(true); + mConnection = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()). + listen("MandatoryUpdateMachine::CheckingForUpdate", boost::bind(&MandatoryUpdateMachine::CheckingForUpdate::onEvent, this, _1)); +} + + +void MandatoryUpdateMachine::CheckingForUpdate::exit(void) +{ +} + + +bool MandatoryUpdateMachine::CheckingForUpdate::onEvent(LLSD const & event) +{ + if(event["type"].asInteger() == LLUpdaterService::STATE_CHANGE) { + switch(event["state"].asInteger()) { + case LLUpdaterService::DOWNLOADING: + mMachine.setCurrentState(new WaitingForDownload(mMachine)); + break; + case LLUpdaterService::TEMPORARY_ERROR: + case LLUpdaterService::UP_TO_DATE: + case LLUpdaterService::TERMINAL: + case LLUpdaterService::FAILURE: + mProgressView->setVisible(false); + mMachine.setCurrentState(new Error(mMachine)); + break; + case LLUpdaterService::INSTALLING: + llassert(!"can't possibly be installing"); + break; + default: + break; + } + } else { + ; // Ignore. + } + + return false; +} + + + +// MandatoryUpdateMachine::Error +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::Error::Error(MandatoryUpdateMachine & machine): + mMachine(machine) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::Error::enter(void) +{ + llinfos << "entering error" << llendl; + LLNotificationsUtil::add("FailedRequiredUpdateInstall", LLSD(), LLSD(), boost::bind(&MandatoryUpdateMachine::Error::onButtonClicked, this, _1, _2)); +} + + +void MandatoryUpdateMachine::Error::exit(void) +{ + LLAppViewer::instance()->forceQuit(); +} + + +void MandatoryUpdateMachine::Error::onButtonClicked(const LLSD &, const LLSD &) +{ + mMachine.setCurrentState(0); +} + + + +// MandatoryUpdateMachine::ReadyToInstall +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::ReadyToInstall::ReadyToInstall(MandatoryUpdateMachine & machine): + mMachine(machine) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::ReadyToInstall::enter(void) +{ + llinfos << "entering ready to install" << llendl; + // Open update ready dialog. +} + + +void MandatoryUpdateMachine::ReadyToInstall::exit(void) +{ + // Restart viewer. +} + + + +// MandatoryUpdateMachine::StartingUpdaterService +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::StartingUpdaterService::StartingUpdaterService(MandatoryUpdateMachine & machine): + mMachine(machine) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::StartingUpdaterService::enter(void) +{ + llinfos << "entering start update service" << llendl; + LLNotificationsUtil::add("UpdaterServiceNotRunning", LLSD(), LLSD(), boost::bind(&MandatoryUpdateMachine::StartingUpdaterService::onButtonClicked, this, _1, _2)); +} + + +void MandatoryUpdateMachine::StartingUpdaterService::exit(void) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::StartingUpdaterService::onButtonClicked(const LLSD & uiform, const LLSD & result) +{ + if(result["OK_okcancelbuttons"].asBoolean()) { + mMachine.mUpdaterService.startChecking(false); + mMachine.setCurrentState(new CheckingForUpdate(mMachine)); + } else { + LLAppViewer::instance()->forceQuit(); + } +} + + + +// MandatoryUpdateMachine::WaitingForDownload +//----------------------------------------------------------------------------- + + +MandatoryUpdateMachine::WaitingForDownload::WaitingForDownload(MandatoryUpdateMachine & machine): + mMachine(machine), + mProgressView(0) +{ + ; // No op. +} + + +void MandatoryUpdateMachine::WaitingForDownload::enter(void) +{ + llinfos << "entering waiting for download" << llendl; + mProgressView = gViewerWindow->getProgressView(); + mProgressView->setMessage("Downloading update..."); + std::ostringstream stream; + stream << "There is a required update for your Second Life installation." << std::endl << + "Version " << mMachine.mUpdaterService.updatedVersion(); + mProgressView->setText(stream.str()); + mProgressView->setPercent(0); + mProgressView->setVisible(true); + mConnection = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()). + listen("MandatoryUpdateMachine::CheckingForUpdate", boost::bind(&MandatoryUpdateMachine::WaitingForDownload::onEvent, this, _1)); +} + + +void MandatoryUpdateMachine::WaitingForDownload::exit(void) +{ + mProgressView->setVisible(false); +} + + +bool MandatoryUpdateMachine::WaitingForDownload::onEvent(LLSD const & event) +{ + switch(event["type"].asInteger()) { + case LLUpdaterService::DOWNLOAD_COMPLETE: + mMachine.setCurrentState(new ReadyToInstall(mMachine)); + break; + case LLUpdaterService::DOWNLOAD_ERROR: + mMachine.setCurrentState(new Error(mMachine)); + break; + case LLUpdaterService::PROGRESS: { + double downloadSize = event["download_size"].asReal(); + double bytesDownloaded = event["bytes_downloaded"].asReal(); + mProgressView->setPercent(100. * bytesDownloaded / downloadSize); + break; + } + default: + break; + } + + return false; +} + + + +// LLLoginInstance +//----------------------------------------------------------------------------- + + LLLoginInstance::LLLoginInstance() : mLoginModule(new LLLogin()), mNotifications(NULL), @@ -68,7 +476,8 @@ LLLoginInstance::LLLoginInstance() : mSkipOptionalUpdate(false), mAttemptComplete(false), mTransferRate(0.0f), - mDispatcher("LLLoginInstance", "change") + mDispatcher("LLLoginInstance", "change"), + mUpdaterService(0) { mLoginModule->getEventPump().listen("lllogininstance", boost::bind(&LLLoginInstance::handleLoginEvent, this, _1)); @@ -149,6 +558,7 @@ void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credentia requested_options.append("newuser-config"); requested_options.append("ui-config"); #endif + requested_options.append("max-agent-groups"); requested_options.append("map-server-url"); requested_options.append("voice-config"); requested_options.append("tutorial_setting"); @@ -181,9 +591,10 @@ void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credentia request_params["read_critical"] = false; // handleTOSResponse request_params["last_exec_event"] = mLastExecEvent; request_params["mac"] = hashed_unique_id_string; - request_params["version"] = gCurrentVersion; // Includes channel name - request_params["channel"] = gSavedSettings.getString("VersionChannelName"); + request_params["version"] = LLVersionInfo::getChannelAndVersion(); // Includes channel name + request_params["channel"] = LLVersionInfo::getChannel(); request_params["id0"] = mSerialNumber; + request_params["host_id"] = gSavedSettings.getString("HostID"); mRequestData.clear(); mRequestData["method"] = "login_to_simulator"; @@ -351,6 +762,15 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key) void LLLoginInstance::updateApp(bool mandatory, const std::string& auth_msg) { + if(mandatory) + { + gViewerWindow->setShowProgress(false); + MandatoryUpdateMachine * machine = new MandatoryUpdateMachine(*this, *mUpdaterService); + mUpdateStateMachine.reset(machine); + machine->start(); + return; + } + // store off config state, as we might quit soon gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); LLUIColorTable::instance().saveUserSettings(); diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h index 159e05046c..b872d7d1b1 100644 --- a/indra/newview/lllogininstance.h +++ b/indra/newview/lllogininstance.h @@ -34,12 +34,15 @@ class LLLogin; class LLEventStream; class LLNotificationsInterface; +class LLUpdaterService; // This class hosts the login module and is used to // negotiate user authentication attempts. class LLLoginInstance : public LLSingleton<LLLoginInstance> { public: + class Disposable; + LLLoginInstance(); ~LLLoginInstance(); @@ -75,6 +78,7 @@ public: typedef boost::function<void()> UpdaterLauncherCallback; void setUpdaterLauncher(const UpdaterLauncherCallback& ulc) { mUpdaterLauncher = ulc; } + void setUpdaterService(LLUpdaterService * updaterService) { mUpdaterService = updaterService; } private: void constructAuthParams(LLPointer<LLCredential> user_credentials); void updateApp(bool mandatory, const std::string& message); @@ -104,6 +108,8 @@ private: int mLastExecEvent; UpdaterLauncherCallback mUpdaterLauncher; LLEventDispatcher mDispatcher; + LLUpdaterService * mUpdaterService; + boost::scoped_ptr<Disposable> mUpdateStateMachine; }; #endif diff --git a/indra/newview/llmainlooprepeater.cpp b/indra/newview/llmainlooprepeater.cpp new file mode 100644 index 0000000000..5c020e6d98 --- /dev/null +++ b/indra/newview/llmainlooprepeater.cpp @@ -0,0 +1,88 @@ +/** + * @file llmachineid.cpp + * @brief retrieves unique machine ids + * + * $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 "llapr.h" +#include "llevents.h" +#include "llmainlooprepeater.h" + + + +// LLMainLoopRepeater +//----------------------------------------------------------------------------- + + +LLMainLoopRepeater::LLMainLoopRepeater(void): + mQueue(0) +{ + ; // No op. +} + + +void LLMainLoopRepeater::start(void) +{ + if(mQueue != 0) return; + + mQueue = new LLThreadSafeQueue<LLSD>(gAPRPoolp, 1024); + mMainLoopConnection = LLEventPumps::instance(). + obtain("mainloop").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMainLoop, this, _1)); + mRepeaterConnection = LLEventPumps::instance(). + obtain("mainlooprepeater").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMessage, this, _1)); +} + + +void LLMainLoopRepeater::stop(void) +{ + mMainLoopConnection.release(); + mRepeaterConnection.release(); + + delete mQueue; + mQueue = 0; +} + + +bool LLMainLoopRepeater::onMainLoop(LLSD const &) +{ + LLSD message; + while(mQueue->tryPopBack(message)) { + std::string pump = message["pump"].asString(); + if(pump.length() == 0 ) continue; // No pump. + LLEventPumps::instance().obtain(pump).post(message["payload"]); + } + return false; +} + + +bool LLMainLoopRepeater::onMessage(LLSD const & event) +{ + try { + mQueue->pushFront(event); + } catch(LLThreadSafeQueueError & e) { + llwarns << "could not repeat message (" << e.what() << ")" << + event.asString() << LL_ENDL; + } + return false; +} diff --git a/indra/newview/llmainlooprepeater.h b/indra/newview/llmainlooprepeater.h new file mode 100644 index 0000000000..f84c0ca94c --- /dev/null +++ b/indra/newview/llmainlooprepeater.h @@ -0,0 +1,65 @@ +/** + * @file llmainlooprepeater.h + * @brief a service for repeating messages on the main loop. + * + * $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_LLMAINLOOPREPEATER_H +#define LL_LLMAINLOOPREPEATER_H + + +#include "llsd.h" +#include "llthreadsafequeue.h" + + +// +// A service which creates the pump 'mainlooprepeater' to which any thread can +// post a message that will be re-posted on the main loop. +// +// The posted message should contain two map elements: pump and payload. The +// pump value is a string naming the pump to which the message should be +// re-posted. The payload value is what will be posted to the designated pump. +// +class LLMainLoopRepeater: + public LLSingleton<LLMainLoopRepeater> +{ +public: + LLMainLoopRepeater(void); + + // Start the repeater service. + void start(void); + + // Stop the repeater service. + void stop(void); + +private: + LLTempBoundListener mMainLoopConnection; + LLTempBoundListener mRepeaterConnection; + LLThreadSafeQueue<LLSD> * mQueue; + + bool onMainLoop(LLSD const &); + bool onMessage(LLSD const & event); +}; + + +#endif diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp index 5eb3b789f2..f871df0c36 100644 --- a/indra/newview/llmaniptranslate.cpp +++ b/indra/newview/llmaniptranslate.cpp @@ -726,7 +726,7 @@ BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask) LLVector3d new_position_global = selectNode->mSavedPositionGlobal + clamped_relative_move; // Don't let object centers go too far underground - F64 min_height = LLWorld::getInstance()->getMinAllowedZ(object); + F64 min_height = LLWorld::getInstance()->getMinAllowedZ(object, object->getPositionGlobal()); if (new_position_global.mdV[VZ] < min_height) { new_position_global.mdV[VZ] = min_height; diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index e84c9152b1..9493fddf50 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -25,7 +25,7 @@ */ #include "llviewerprecompiledheaders.h" - +#include "lltooltip.h" #include "llmediactrl.h" @@ -54,6 +54,10 @@ #include "llbutton.h" #include "llcheckboxctrl.h" #include "llnotifications.h" +#include "lllineeditor.h" +#include "llfloatermediabrowser.h" +#include "llfloaterwebcontent.h" +#include "llwindowshade.h" extern BOOL gRestoreGL; @@ -70,7 +74,8 @@ LLMediaCtrl::Params::Params() caret_color("caret_color"), initial_mime_type("initial_mime_type"), media_id("media_id"), - trusted_content("trusted_content", false) + trusted_content("trusted_content", false), + focus_on_click("focus_on_click", true) { tab_stop(false); } @@ -86,7 +91,7 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : mIgnoreUIScale( true ), mAlwaysRefresh( false ), mMediaSource( 0 ), - mTakeFocusOnClick( true ), + mTakeFocusOnClick( p.focus_on_click ), mCurrentNavUrl( "" ), mStretchToFill( true ), mMaintainAspectRatio ( true ), @@ -97,7 +102,9 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : mTextureHeight ( 1024 ), mClearCache(false), mHomePageMimeType(p.initial_mime_type), - mTrusted(p.trusted_content) + mTrusted(p.trusted_content), + mWindowShade(NULL), + mHoverTextChanged(false) { { LLColor4 color = p.caret_color().get(); @@ -126,7 +133,7 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : setTextureSize(screen_width, screen_height); } - mMediaTextureID.generate(); + mMediaTextureID = getKey(); // We don't need to create the media source up front anymore unless we have a non-empty home URL to navigate to. if(!mHomePageUrl.empty()) @@ -140,8 +147,6 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : // addChild( mBorder ); } -//////////////////////////////////////////////////////////////////////////////// -// note: this is now a singleton and destruction happens via initClass() now LLMediaCtrl::~LLMediaCtrl() { @@ -181,6 +186,13 @@ BOOL LLMediaCtrl::handleHover( S32 x, S32 y, MASK mask ) mMediaSource->mouseMove(x, y, mask); gViewerWindow->setCursor(mMediaSource->getLastSetCursor()); } + + // TODO: Is this the right way to handle hover text changes driven by the plugin? + if(mHoverTextChanged) + { + mHoverTextChanged = false; + handleToolTip(x, y, mask); + } return TRUE; } @@ -197,6 +209,35 @@ BOOL LLMediaCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks ) } //////////////////////////////////////////////////////////////////////////////// +// virtual +BOOL LLMediaCtrl::handleToolTip(S32 x, S32 y, MASK mask) +{ + std::string hover_text; + + if (mMediaSource && mMediaSource->hasMedia()) + hover_text = mMediaSource->getMediaPlugin()->getHoverText(); + + if(hover_text.empty()) + { + return FALSE; + } + else + { + S32 screen_x, screen_y; + + localPointToScreen(x, y, &screen_x, &screen_y); + LLRect sticky_rect_screen; + sticky_rect_screen.setCenterAndSize(screen_x, screen_y, 20, 20); + + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(hover_text) + .sticky_rect(sticky_rect_screen)); + } + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////// // BOOL LLMediaCtrl::handleMouseUp( S32 x, S32 y, MASK mask ) { @@ -206,14 +247,6 @@ BOOL LLMediaCtrl::handleMouseUp( S32 x, S32 y, MASK mask ) if (mMediaSource) { mMediaSource->mouseUp(x, y, mask); - - // *HACK: LLMediaImplLLMozLib automatically takes focus on mouseup, - // in addition to the onFocusReceived() call below. Undo this. JC - if (!mTakeFocusOnClick) - { - mMediaSource->focus(false); - gViewerWindow->focusClient(); - } } gFocusMgr.setMouseCapture( NULL ); @@ -345,85 +378,6 @@ void LLMediaCtrl::onFocusLost() // BOOL LLMediaCtrl::postBuild () { - LLLayoutStack::Params layout_p; - layout_p.name = "notification_stack"; - layout_p.rect = LLRect(0,getLocalRect().mTop,getLocalRect().mRight, 30); - layout_p.follows.flags = FOLLOWS_ALL; - layout_p.mouse_opaque = false; - layout_p.orientation = "vertical"; - - LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p); - addChild(stackp); - - LLLayoutPanel::Params panel_p; - panel_p.rect = LLRect(0, 30, 800, 0); - panel_p.min_height = 30; - panel_p.name = "notification_area"; - panel_p.visible = false; - panel_p.user_resize = false; - panel_p.background_visible = true; - panel_p.bg_alpha_image.name = "Yellow_Gradient"; - panel_p.auto_resize = false; - LLLayoutPanel* notification_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); - stackp->addChild(notification_panel); - - panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); - panel_p.auto_resize = true; - panel_p.mouse_opaque = false; - LLLayoutPanel* dummy_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); - stackp->addChild(dummy_panel); - - layout_p = LLUICtrlFactory::getDefaultParams<LLLayoutStack>(); - layout_p.rect = LLRect(0, 30, 800, 0); - layout_p.follows.flags = FOLLOWS_ALL; - layout_p.orientation = "horizontal"; - stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p); - notification_panel->addChild(stackp); - - panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); - panel_p.rect.height = 30; - LLLayoutPanel* panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); - stackp->addChild(panel); - - LLIconCtrl::Params icon_p; - icon_p.name = "notification_icon"; - icon_p.rect = LLRect(5, 23, 21, 8); - panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p)); - - LLTextBox::Params text_p; - text_p.rect = LLRect(31, 20, 430, 0); - text_p.text_color = LLColor4::black; - text_p.font = LLFontGL::getFontSansSerif(); - text_p.font.style = "BOLD"; - text_p.name = "notification_text"; - text_p.use_ellipses = true; - panel->addChild(LLUICtrlFactory::create<LLTextBox>(text_p)); - - panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); - panel_p.auto_resize = false; - panel_p.user_resize = false; - panel_p.name="form_elements"; - panel_p.rect = LLRect(0, 30, 130, 0); - LLLayoutPanel* form_elements_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); - stackp->addChild(form_elements_panel); - - panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>(); - panel_p.auto_resize = false; - panel_p.user_resize = false; - panel_p.rect = LLRect(0, 30, 25, 0); - LLLayoutPanel* close_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p); - stackp->addChild(close_panel); - - LLButton::Params button_p; - button_p.name = "close_notification"; - button_p.rect = LLRect(5, 23, 21, 7); - button_p.image_color=LLUIColorTable::instance().getColor("DkGray_66"); - button_p.image_unselected.name="Icon_Close_Foreground"; - button_p.image_selected.name="Icon_Close_Press"; - button_p.click_callback.function = boost::bind(&LLMediaCtrl::onCloseNotification, this); - - close_panel->addChild(LLUICtrlFactory::create<LLButton>(button_p)); - setVisibleCallback(boost::bind(&LLMediaCtrl::onVisibilityChange, this, _2)); return TRUE; } @@ -432,13 +386,15 @@ BOOL LLMediaCtrl::postBuild () // BOOL LLMediaCtrl::handleKeyHere( KEY key, MASK mask ) { - if (LLPanel::handleKeyHere(key, mask)) return TRUE; BOOL result = FALSE; if (mMediaSource) { result = mMediaSource->handleKeyHere(key, mask); } + + if ( ! result ) + result = LLPanel::handleKeyHere(key, mask); return result; } @@ -458,7 +414,6 @@ void LLMediaCtrl::handleVisibilityChange ( BOOL new_visibility ) // BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char) { - if (LLPanel::handleUnicodeCharHere(uni_char)) return TRUE; BOOL result = FALSE; if (mMediaSource) @@ -466,6 +421,9 @@ BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char) result = mMediaSource->handleUnicodeCharHere(uni_char); } + if ( ! result ) + result = LLPanel::handleUnicodeCharHere(uni_char); + return result; } @@ -921,11 +879,6 @@ void LLMediaCtrl::draw() if ( mBorder && mBorder->getVisible() ) mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) ); - if (mCurNotification && !mCurNotification->isActive()) - { - hideNotification(); - } - LLPanel::draw(); // Restore the previous values @@ -1033,7 +986,7 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) LLNotification::Params notify_params; notify_params.name = "PopupAttempt"; - notify_params.payload = LLSD().with("target", target).with("url", url).with("uuid", uuid).with("media_id", getKey()); + notify_params.payload = LLSD().with("target", target).with("url", url).with("uuid", uuid).with("media_id", mMediaTextureID); notify_params.functor.function = boost::bind(&LLMediaCtrl::onPopup, this, _1, _2); if (mTrusted) @@ -1088,6 +1041,31 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_GEOMETRY_CHANGE, uuid is " << self->getClickUUID() << LL_ENDL; } break; + + case MEDIA_EVENT_AUTH_REQUEST: + { + LLNotification::Params auth_request_params; + auth_request_params.name = "AuthRequest"; + + // pass in host name and realm for site (may be zero length but will always exist) + LLSD args; + LLURL raw_url( self->getAuthURL().c_str() ); + args["HOST_NAME"] = raw_url.getAuthority(); + args["REALM"] = self->getAuthRealm(); + auth_request_params.substitutions = args; + + auth_request_params.payload = LLSD().with("media_id", mMediaTextureID); + auth_request_params.functor.function = boost::bind(&LLViewerMedia::onAuthSubmit, _1, _2); + LLNotifications::instance().add(auth_request_params); + }; + break; + + case MEDIA_EVENT_LINK_HOVERED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << LL_ENDL; + mHoverTextChanged = true; + }; + break; }; // chain all events to any potential observers of this object. @@ -1105,109 +1083,85 @@ void LLMediaCtrl::onPopup(const LLSD& notification, const LLSD& response) { if (response["open"]) { - LLWeb::loadURL(notification["payload"]["url"], notification["payload"]["target"], notification["payload"]["uuid"]); + // name of default floater to open + std::string floater_name = "media_browser"; + + // look for parent floater name + if ( gFloaterView ) + { + if ( gFloaterView->getParentFloater(this) ) + { + floater_name = gFloaterView->getParentFloater(this)->getInstanceName(); + } + else + { + lldebugs << "No gFloaterView->getParentFloater(this) for onPopuup()" << llendl; + }; + } + else + { + lldebugs << "No gFloaterView for onPopuup()" << llendl; + }; + + // (for now) open web content floater if that's our parent, otherwise, open the current media floater + // (this will change soon) + if ( floater_name == "web_content" ) + { + LLWeb::loadWebURL(notification["payload"]["url"], notification["payload"]["target"], notification["payload"]["uuid"]); + } + else + { + LLWeb::loadURL(notification["payload"]["url"], notification["payload"]["target"], notification["payload"]["uuid"]); + } } else { // Make sure the opening instance knows its window open request was denied, so it can clean things up. LLViewerMedia::proxyWindowClosed(notification["payload"]["uuid"]); } - } -void LLMediaCtrl::onCloseNotification() -{ - LLNotifications::instance().cancel(mCurNotification); -} - -void LLMediaCtrl::onClickIgnore(LLUICtrl* ctrl) +void LLMediaCtrl::showNotification(LLNotificationPtr notify) { - bool check = ctrl->getValue().asBoolean(); - if (mCurNotification && mCurNotification->getForm()->getIgnoreType() == LLNotificationForm::IGNORE_SHOW_AGAIN) + delete mWindowShade; + + LLWindowShade::Params params; + params.name = "notification_shade"; + params.rect = getLocalRect(); + params.follows.flags = FOLLOWS_ALL; + params.notification = notify; + params.modal = true; + //HACK: don't hardcode this + if (notify->getIcon() == "Popup_Caution") { - // question was "show again" so invert value to get "ignore" - check = !check; + params.bg_image.name = "Yellow_Gradient"; + params.text_color = LLColor4::black; } - mCurNotification->setIgnored(check); -} - -void LLMediaCtrl::onClickNotificationButton(const std::string& name) -{ - if (!mCurNotification) return; - - LLSD response = mCurNotification->getResponseTemplate(); - response[name] = true; - - mCurNotification->respond(response); -} - -void LLMediaCtrl::showNotification(LLNotificationPtr notify) -{ - mCurNotification = notify; - - // add popup here - LLSD payload = notify->getPayload(); - - LLNotificationFormPtr formp = notify->getForm(); - LLLayoutPanel& panel = getChildRef<LLLayoutPanel>("notification_area"); - panel.setVisible(true); - panel.getChild<LLUICtrl>("notification_icon")->setValue(notify->getIcon()); - panel.getChild<LLUICtrl>("notification_text")->setValue(notify->getMessage()); - panel.getChild<LLUICtrl>("notification_text")->setToolTip(notify->getMessage()); - LLNotificationForm::EIgnoreType ignore_type = formp->getIgnoreType(); - LLLayoutPanel& form_elements = panel.getChildRef<LLLayoutPanel>("form_elements"); - form_elements.deleteAllChildren(); - - const S32 FORM_PADDING_HORIZONTAL = 10; - const S32 FORM_PADDING_VERTICAL = 3; - S32 cur_x = FORM_PADDING_HORIZONTAL; - - if (ignore_type != LLNotificationForm::IGNORE_NO) + else + //HACK: another one since XUI doesn't support what we need right now + if (notify->getName() == "AuthRequest") { - LLCheckBoxCtrl::Params checkbox_p; - checkbox_p.name = "ignore_check"; - checkbox_p.rect = LLRect(cur_x, form_elements.getRect().getHeight() - FORM_PADDING_VERTICAL, cur_x, FORM_PADDING_VERTICAL); - checkbox_p.label = formp->getIgnoreMessage(); - checkbox_p.label_text.text_color = LLColor4::black; - checkbox_p.commit_callback.function = boost::bind(&LLMediaCtrl::onClickIgnore, this, _1); - checkbox_p.initial_value = formp->getIgnored(); - - LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p); - check->setRect(check->getBoundingRect()); - form_elements.addChild(check); - cur_x = check->getRect().mRight + FORM_PADDING_HORIZONTAL; + params.bg_image.name = "Yellow_Gradient"; + params.text_color = LLColor4::black; + params.can_close = false; } - - for (S32 i = 0; i < formp->getNumElements(); i++) + else { - LLSD form_element = formp->getElement(i); - if (form_element["type"].asString() == "button") - { - LLButton::Params button_p; - button_p.name = form_element["name"]; - button_p.label = form_element["text"]; - button_p.rect = LLRect(cur_x, form_elements.getRect().getHeight() - FORM_PADDING_VERTICAL, cur_x, FORM_PADDING_VERTICAL); - button_p.click_callback.function = boost::bind(&LLMediaCtrl::onClickNotificationButton, this, form_element["name"].asString()); - button_p.auto_resize = true; - - LLButton* button = LLUICtrlFactory::create<LLButton>(button_p); - button->autoResize(); - form_elements.addChild(button); - - cur_x = button->getRect().mRight + FORM_PADDING_HORIZONTAL; - } + //HACK: make this a property of the notification itself, "cancellable" + params.can_close = false; + params.text_color.control = "LabelTextColor"; } + mWindowShade = LLUICtrlFactory::create<LLWindowShade>(params); - form_elements.reshape(cur_x, form_elements.getRect().getHeight()); - - //LLWeb::loadURL(payload["url"], payload["target"]); + addChild(mWindowShade); + mWindowShade->show(); } void LLMediaCtrl::hideNotification() { - LLLayoutPanel& panel = getChildRef<LLLayoutPanel>("notification_area"); - panel.setVisible(FALSE); - - mCurNotification.reset(); + if (mWindowShade) + { + mWindowShade->hide(); + } } diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index 65dfbbff78..38a74f90d3 100644 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -53,7 +53,8 @@ public: ignore_ui_scale, hide_loading, decouple_texture_size, - trusted_content; + trusted_content, + focus_on_click; Optional<S32> texture_width, texture_height; @@ -89,6 +90,7 @@ public: virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); + virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); // navigation void navigateTo( std::string url_in, std::string mime_type = ""); @@ -167,9 +169,6 @@ public: private: void onVisibilityChange ( const LLSD& new_visibility ); void onPopup(const LLSD& notification, const LLSD& response); - void onCloseNotification(); - void onClickNotificationButton(const std::string& name); - void onClickIgnore(LLUICtrl* ctrl); const S32 mTextureDepthBytes; LLUUID mMediaTextureID; @@ -193,7 +192,8 @@ public: S32 mTextureWidth; S32 mTextureHeight; bool mClearCache; - boost::shared_ptr<class LLNotification> mCurNotification; + class LLWindowShade* mWindowShade; + bool mHoverTextChanged; }; #endif // LL_LLMediaCtrl_H diff --git a/indra/newview/llmetricperformancetester.cpp b/indra/newview/llmetricperformancetester.cpp deleted file mode 100644 index 903c97378e..0000000000 --- a/indra/newview/llmetricperformancetester.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/** - * @file llmetricperformancetester.cpp - * @brief LLMetricPerformanceTester class implementation - * - * $LicenseInfo:firstyear=2004&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 "indra_constants.h" -#include "llerror.h" -#include "llmath.h" -#include "llfontgl.h" -#include "llsdserialize.h" -#include "llstat.h" -#include "lltreeiterators.h" -#include "llmetricperformancetester.h" - -LLMetricPerformanceTester::name_tester_map_t LLMetricPerformanceTester::sTesterMap ; - -//static -void LLMetricPerformanceTester::initClass() -{ -} -//static -void LLMetricPerformanceTester::cleanClass() -{ - for(name_tester_map_t::iterator iter = sTesterMap.begin() ; iter != sTesterMap.end() ; ++iter) - { - delete iter->second ; - } - sTesterMap.clear() ; -} - -//static -void LLMetricPerformanceTester::addTester(LLMetricPerformanceTester* tester) -{ - if(!tester) - { - llerrs << "invalid tester!" << llendl ; - return ; - } - - std::string name = tester->getName() ; - if(getTester(name)) - { - llerrs << "Tester name is used by some other tester: " << name << llendl ; - return ; - } - - sTesterMap.insert(std::make_pair(name, tester)); - - return ; -} - -//static -LLMetricPerformanceTester* LLMetricPerformanceTester::getTester(std::string label) -{ - name_tester_map_t::iterator found_it = sTesterMap.find(label) ; - if(found_it != sTesterMap.end()) - { - return found_it->second ; - } - - return NULL ; -} - -LLMetricPerformanceTester::LLMetricPerformanceTester(std::string name, BOOL use_default_performance_analysis) - : mName(name), - mBaseSessionp(NULL), - mCurrentSessionp(NULL), - mCount(0), - mUseDefaultPerformanceAnalysis(use_default_performance_analysis) -{ - if(mName == std::string()) - { - llerrs << "invalid name." << llendl ; - } - - LLMetricPerformanceTester::addTester(this) ; -} - -/*virtual*/ -LLMetricPerformanceTester::~LLMetricPerformanceTester() -{ - if(mBaseSessionp) - { - delete mBaseSessionp ; - mBaseSessionp = NULL ; - } - if(mCurrentSessionp) - { - delete mCurrentSessionp ; - mCurrentSessionp = NULL ; - } -} - -void LLMetricPerformanceTester::incLabel() -{ - mCurLabel = llformat("%s-%d", mName.c_str(), mCount++) ; -} -void LLMetricPerformanceTester::preOutputTestResults(LLSD* sd) -{ - incLabel() ; - (*sd)[mCurLabel]["Name"] = mName ; -} -void LLMetricPerformanceTester::postOutputTestResults(LLSD* sd) -{ - LLMutexLock lock(LLFastTimer::sLogLock); - LLFastTimer::sLogQueue.push((*sd)); -} - -void LLMetricPerformanceTester::outputTestResults() -{ - LLSD sd ; - preOutputTestResults(&sd) ; - - outputTestRecord(&sd) ; - - postOutputTestResults(&sd) ; -} - -void LLMetricPerformanceTester::addMetricString(std::string str) -{ - mMetricStrings.push_back(str) ; -} - -const std::string& LLMetricPerformanceTester::getMetricString(U32 index) const -{ - return mMetricStrings[index] ; -} - -void LLMetricPerformanceTester::prePerformanceAnalysis() -{ - mCount = 0 ; - incLabel() ; -} - -// -//default analyzing the performance -// -/*virtual*/ -void LLMetricPerformanceTester::analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current) -{ - if(mUseDefaultPerformanceAnalysis)//use default performance analysis - { - prePerformanceAnalysis() ; - - BOOL in_base = (*base).has(mCurLabel) ; - BOOL in_current = (*current).has(mCurLabel) ; - - while(in_base || in_current) - { - LLSD::String label = mCurLabel ; - - if(in_base && in_current) - { - *os << llformat("%s\n", label.c_str()) ; - - for(U32 index = 0 ; index < mMetricStrings.size() ; index++) - { - switch((*current)[label][ mMetricStrings[index] ].type()) - { - case LLSD::TypeInteger: - compareTestResults(os, mMetricStrings[index], - (S32)((*base)[label][ mMetricStrings[index] ].asInteger()), (S32)((*current)[label][ mMetricStrings[index] ].asInteger())) ; - break ; - case LLSD::TypeReal: - compareTestResults(os, mMetricStrings[index], - (F32)((*base)[label][ mMetricStrings[index] ].asReal()), (F32)((*current)[label][ mMetricStrings[index] ].asReal())) ; - break; - default: - llerrs << "unsupported metric " << mMetricStrings[index] << " LLSD type: " << (S32)(*current)[label][ mMetricStrings[index] ].type() << llendl ; - } - } - } - - incLabel() ; - in_base = (*base).has(mCurLabel) ; - in_current = (*current).has(mCurLabel) ; - } - }//end of default - else - { - //load the base session - prePerformanceAnalysis() ; - mBaseSessionp = loadTestSession(base) ; - - //load the current session - prePerformanceAnalysis() ; - mCurrentSessionp = loadTestSession(current) ; - - if(!mBaseSessionp || !mCurrentSessionp) - { - llerrs << "memory error during loading test sessions." << llendl ; - } - - //compare - compareTestSessions(os) ; - - //release memory - if(mBaseSessionp) - { - delete mBaseSessionp ; - mBaseSessionp = NULL ; - } - if(mCurrentSessionp) - { - delete mCurrentSessionp ; - mCurrentSessionp = NULL ; - } - } -} - -//virtual -void LLMetricPerformanceTester::compareTestResults(std::ofstream* os, std::string metric_string, S32 v_base, S32 v_current) -{ - *os << llformat(" ,%s, %d, %d, %d, %.4f\n", metric_string.c_str(), v_base, v_current, - v_current - v_base, (v_base != 0) ? 100.f * v_current / v_base : 0) ; -} - -//virtual -void LLMetricPerformanceTester::compareTestResults(std::ofstream* os, std::string metric_string, F32 v_base, F32 v_current) -{ - *os << llformat(" ,%s, %.4f, %.4f, %.4f, %.4f\n", metric_string.c_str(), v_base, v_current, - v_current - v_base, (fabs(v_base) > 0.0001f) ? 100.f * v_current / v_base : 0.f ) ; -} - -//virtual -LLMetricPerformanceTester::LLTestSession::~LLTestSession() -{ -} - diff --git a/indra/newview/llmetricperformancetester.h b/indra/newview/llmetricperformancetester.h deleted file mode 100644 index 6f5dc03564..0000000000 --- a/indra/newview/llmetricperformancetester.h +++ /dev/null @@ -1,153 +0,0 @@ -/** - * @file LLMetricPerformanceTester.h - * @brief LLMetricPerformanceTester class definition - * - * $LicenseInfo:firstyear=2004&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_METRICPERFORMANCETESTER_H -#define LL_METRICPERFORMANCETESTER_H - -class LLMetricPerformanceTester -{ -public: - // - //name passed to the constructor is a unique string for each tester. - //an error is reported if the name is already used by some other tester. - // - LLMetricPerformanceTester(std::string name, BOOL use_default_performance_analysis) ; - virtual ~LLMetricPerformanceTester(); - - // - //return the name of the tester - // - std::string getName() const { return mName ;} - // - //return the number of the test metrics in this tester - // - S32 getNumOfMetricStrings() const { return mMetricStrings.size() ;} - // - //return the metric string at the index - // - const std::string& getMetricString(U32 index) const ; - - // - //this function to compare the test results. - //by default, it compares the test results against the baseline one by one, item by item, - //in the increasing order of the LLSD label counter, starting from the first one. - //you can define your own way to analyze performance by passing FALSE to "use_default_performance_analysis", - //and implement two abstract virtual functions below: loadTestSession(...) and compareTestSessions(...). - // - void analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current) ; - -protected: - // - //insert metric strings used in the tester. - // - void addMetricString(std::string str) ; - - // - //increase LLSD label by 1 - // - void incLabel() ; - - // - //the function to write a set of test results to the log LLSD. - // - void outputTestResults() ; - - // - //compare the test results. - //you can write your own to overwrite the default one. - // - virtual void compareTestResults(std::ofstream* os, std::string metric_string, S32 v_base, S32 v_current) ; - virtual void compareTestResults(std::ofstream* os, std::string metric_string, F32 v_base, F32 v_current) ; - - // - //for performance analysis use - //it defines an interface for the two abstract virtual functions loadTestSession(...) and compareTestSessions(...). - //please make your own test session class derived from it. - // - class LLTestSession - { - public: - virtual ~LLTestSession() ; - }; - - // - //load a test session for log LLSD - //you need to implement it only when you define your own way to analyze performance. - //otherwise leave it empty. - // - virtual LLMetricPerformanceTester::LLTestSession* loadTestSession(LLSD* log) = 0 ; - // - //compare the base session and the target session - //you need to implement it only when you define your own way to analyze performance. - //otherwise leave it empty. - // - virtual void compareTestSessions(std::ofstream* os) = 0 ; - // - //the function to write a set of test results to the log LLSD. - //you have to write you own version of this function. - // - virtual void outputTestRecord(LLSD* sd) = 0 ; - -private: - void preOutputTestResults(LLSD* sd) ; - void postOutputTestResults(LLSD* sd) ; - void prePerformanceAnalysis() ; - -protected: - // - //the unique name string of the tester - // - std::string mName ; - // - //the current label counter for the log LLSD - // - std::string mCurLabel ; - S32 mCount ; - - BOOL mUseDefaultPerformanceAnalysis ; - LLTestSession* mBaseSessionp ; - LLTestSession* mCurrentSessionp ; - - //metrics strings - std::vector< std::string > mMetricStrings ; - -//static members -private: - static void addTester(LLMetricPerformanceTester* tester) ; - -public: - typedef std::map< std::string, LLMetricPerformanceTester* > name_tester_map_t; - static name_tester_map_t sTesterMap ; - - static LLMetricPerformanceTester* getTester(std::string label) ; - static BOOL hasMetricPerformanceTesters() {return !sTesterMap.empty() ;} - - static void initClass() ; - static void cleanClass() ; -}; - -#endif - diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index 6658e1d7e8..142ee40cc8 100644 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -94,6 +94,7 @@ BOOL LLFloaterMove::postBuild() { setIsChrome(TRUE); setTitleVisible(TRUE); // restore title visibility after chrome applying + updateTransparency(TT_ACTIVE); // force using active floater transparency (STORM-730) LLDockableFloater::postBuild(); @@ -448,17 +449,20 @@ void LLFloaterMove::updatePosition() LLBottomTray* tray = LLBottomTray::getInstance(); if (!tray) return; - LLButton* movement_btn = tray->getChild<LLButton>(BOTTOM_TRAY_BUTTON_NAME); + LLButton* movement_btn = tray->findChild<LLButton>(BOTTOM_TRAY_BUTTON_NAME); - //align centers of a button and a floater - S32 x = movement_btn->calcScreenRect().getCenterX() - getRect().getWidth()/2; - - S32 y = 0; - if (!mModeActionsPanel->getVisible()) + if (movement_btn) { - y = mModeActionsPanel->getRect().getHeight(); + //align centers of a button and a floater + S32 x = movement_btn->calcScreenRect().getCenterX() - getRect().getWidth()/2; + + S32 y = 0; + if (!mModeActionsPanel->getVisible()) + { + y = mModeActionsPanel->getRect().getHeight(); + } + setOrigin(x, y); } - setOrigin(x, y); } //static @@ -552,7 +556,7 @@ LLPanelStandStopFlying::LLPanelStandStopFlying() : } // static -inline LLPanelStandStopFlying* LLPanelStandStopFlying::getInstance() +LLPanelStandStopFlying* LLPanelStandStopFlying::getInstance() { static LLPanelStandStopFlying* panel = getStandStopFlyingPanel(); return panel; @@ -735,10 +739,18 @@ void LLPanelStandStopFlying::updatePosition() LLBottomTray* tray = LLBottomTray::getInstance(); if (!tray || mAttached) return; - LLButton* movement_btn = tray->getChild<LLButton>(BOTTOM_TRAY_BUTTON_NAME); + LLButton* movement_btn = tray->findChild<LLButton>(BOTTOM_TRAY_BUTTON_NAME); - // Align centers of the button and the panel. - S32 x = movement_btn->calcScreenRect().getCenterX() - getRect().getWidth()/2; + S32 x = 0; + if (movement_btn) + { + // Align centers of the button and the panel. + x = movement_btn->calcScreenRect().getCenterX() - getRect().getWidth()/2; + } + else + { + x = tray->calcScreenRect().getCenterX() - getRect().getWidth()/2; + } setOrigin(x, 0); } diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp index 58849393b4..e4f83ce6b9 100644 --- a/indra/newview/llnavigationbar.cpp +++ b/indra/newview/llnavigationbar.cpp @@ -276,9 +276,6 @@ LLNavigationBar::LLNavigationBar() // set a listener function for LoginComplete event LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLNavigationBar::handleLoginComplete, this)); - - // Necessary for focus movement among child controls - setFocusRoot(TRUE); } LLNavigationBar::~LLNavigationBar() diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 180695e40b..572eeb8fc7 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -365,3 +365,16 @@ BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) mChatHistory->setFocus(TRUE); return LLDockableFloater::handleMouseDown(x, y, mask); } + +void LLNearbyChat::draw() +{ + // *HACK: Update transparency type depending on whether our children have focus. + // This is needed because this floater is chrome and thus cannot accept focus, so + // the transparency type setting code from LLFloater::setFocus() isn't reached. + if (getTransparencyType() != TT_DEFAULT) + { + setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); + } + + LLDockableFloater::draw(); +} diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 1e62910385..2ea79797f8 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -48,6 +48,7 @@ public: bool onNearbyChatCheckContextMenuItem(const LLSD& userdata); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual void draw(); // focus overrides /*virtual*/ void onFocusLost(); diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index 932ad75f29..836ae9a0cf 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -94,15 +94,19 @@ public: LLGestureComboList::Params::Params() : combo_button("combo_button"), - combo_list("combo_list") + combo_list("combo_list"), + get_more("get_more", true), + view_all("view_all", true) { } LLGestureComboList::LLGestureComboList(const LLGestureComboList::Params& p) -: LLUICtrl(p) - , mLabel(p.label) - , mViewAllItemIndex(0) - , mGetMoreItemIndex(0) +: LLUICtrl(p), + mLabel(p.label), + mViewAllItemIndex(-1), + mGetMoreItemIndex(-1), + mShowViewAll(p.view_all), + mShowGetMore(p.get_more) { LLBottomtrayButton::Params button_params = p.combo_button; button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT); @@ -286,12 +290,16 @@ void LLGestureComboList::refreshGestures() sortByName(); // store indices for Get More and View All items (idx is the index followed by the last added Gesture) - mGetMoreItemIndex = idx; - mViewAllItemIndex = idx + 1; - - // add Get More and View All items at the bottom - mList->addSimpleElement(LLTrans::getString("GetMoreGestures"), ADD_BOTTOM, LLSD(mGetMoreItemIndex)); - mList->addSimpleElement(LLTrans::getString("ViewAllGestures"), ADD_BOTTOM, LLSD(mViewAllItemIndex)); + if (mShowGetMore) + { + mGetMoreItemIndex = idx; + mList->addSimpleElement(LLTrans::getString("GetMoreGestures"), ADD_BOTTOM, LLSD(mGetMoreItemIndex)); + } + if (mShowViewAll) + { + mViewAllItemIndex = idx + 1; + mList->addSimpleElement(LLTrans::getString("ViewAllGestures"), ADD_BOTTOM, LLSD(mViewAllItemIndex)); + } // Insert label after sorting, at top, with separator below it mList->addSeparator(ADD_TOP); diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index cc905736fd..033d1dd5a2 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -46,6 +46,8 @@ public: { Optional<LLBottomtrayButton::Params> combo_button; Optional<LLScrollListCtrl::Params> combo_list; + Optional<bool> get_more, + view_all; Params(); }; @@ -56,6 +58,8 @@ protected: LLGestureComboList(const Params&); std::vector<LLMultiGesture*> mGestures; std::string mLabel; + bool mShowViewAll; + bool mShowGetMore; LLSD::Integer mViewAllItemIndex; LLSD::Integer mGetMoreItemIndex; diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index 47d32e57fb..de5439e4e0 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -64,6 +64,18 @@ public: LLNearbyChatScreenChannel(const LLUUID& id):LLScreenChannelBase(id) { mStopProcessing = false; + + LLControlVariable* ctrl = gSavedSettings.getControl("NearbyToastLifeTime").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLNearbyChatScreenChannel::updateToastsLifetime, this)); + } + + ctrl = gSavedSettings.getControl("NearbyToastFadingTime").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLNearbyChatScreenChannel::updateToastFadingTime, this)); + } } void addNotification (LLSD& notification); @@ -111,11 +123,24 @@ protected: toast->setVisible(FALSE); toast->stopTimer(); toast->setIsHidden(true); + + // Nearby chat toasts that are hidden, not destroyed. They are collected to the toast pool, so that + // they can be used next time, this is done for performance. But if the toast lifetime was changed + // (from preferences floater (STORY-36)) while it was shown (at this moment toast isn't in the pool yet) + // changes don't take affect. + // So toast's lifetime should be updated each time it's added to the pool. Otherwise viewer would have + // to be restarted so that changes take effect. + toast->setLifetime(gSavedSettings.getS32("NearbyToastLifeTime")); + toast->setFadingTime(gSavedSettings.getS32("NearbyToastFadingTime")); m_toast_pool.push_back(toast->getHandle()); } void createOverflowToast(S32 bottom, F32 timer); + void updateToastsLifetime(); + + void updateToastFadingTime(); + create_toast_panel_callback_t m_create_toast_panel_callback_t; bool createPoolToast(); @@ -205,6 +230,27 @@ void LLNearbyChatScreenChannel::onToastFade(LLToast* toast) arrangeToasts(); } +void LLNearbyChatScreenChannel::updateToastsLifetime() +{ + S32 seconds = gSavedSettings.getS32("NearbyToastLifeTime"); + toast_list_t::iterator it; + + for(it = m_toast_pool.begin(); it != m_toast_pool.end(); ++it) + { + (*it).get()->setLifetime(seconds); + } +} + +void LLNearbyChatScreenChannel::updateToastFadingTime() +{ + S32 seconds = gSavedSettings.getS32("NearbyToastFadingTime"); + toast_list_t::iterator it; + + for(it = m_toast_pool.begin(); it != m_toast_pool.end(); ++it) + { + (*it).get()->setFadingTime(seconds); + } +} bool LLNearbyChatScreenChannel::createPoolToast() { @@ -250,7 +296,7 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) { panel->addMessage(notification); toast->reshapeToPanel(); - toast->resetTimer(); + toast->startTimer(); arrangeToasts(); return; @@ -295,7 +341,7 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) panel->init(notification); toast->reshapeToPanel(); - toast->resetTimer(); + toast->startTimer(); m_active_toasts.push_back(toast->getHandle()); @@ -325,9 +371,9 @@ void LLNearbyChatScreenChannel::arrangeToasts() int sort_toasts_predicate(LLHandle<LLToast> first, LLHandle<LLToast> second) { - F32 v1 = first.get()->getTimer()->getEventTimer().getElapsedTimeF32(); - F32 v2 = second.get()->getTimer()->getEventTimer().getElapsedTimeF32(); - return v1 < v2; + F32 v1 = first.get()->getTimeLeftToLive(); + F32 v2 = second.get()->getTimeLeftToLive(); + return v1 > v2; } void LLNearbyChatScreenChannel::showToastsBottom() @@ -336,7 +382,10 @@ void LLNearbyChatScreenChannel::showToastsBottom() return; LLRect toast_rect; - S32 bottom = getRect().mBottom; + updateBottom(); + S32 channel_bottom = getRect().mBottom; + + S32 bottom = channel_bottom; S32 margin = gSavedSettings.getS32("ToastGap"); //sort active toasts @@ -468,9 +517,18 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, const LLSD &args) } nearby_chat->addMessage(chat_msg, true, args); + + if(chat_msg.mSourceType == CHAT_SOURCE_AGENT + && chat_msg.mFromID.notNull() + && chat_msg.mFromID != gAgentID) + { + LLFirstUse::otherAvatarChatFirst(); + } + if( nearby_chat->getVisible() || ( chat_msg.mSourceType == CHAT_SOURCE_AGENT - && gSavedSettings.getBOOL("UseChatBubbles") ) ) + && gSavedSettings.getBOOL("UseChatBubbles") ) + || !mChannel->getShowToasts() ) // to prevent toasts in Busy mode return;//no need in toast if chat is visible or if bubble chat is enabled // Handle irc styled messages for toast panel @@ -527,13 +585,7 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, const LLSD &args) notification["font_size"] = (S32)LLViewerChat::getChatFontSize() ; channel->addNotification(notification); } - - if(chat_msg.mSourceType == CHAT_SOURCE_AGENT - && chat_msg.mFromID.notNull() - && chat_msg.mFromID != gAgentID) - { - LLFirstUse::otherAvatarChatFirst(); - } + } void LLNearbyChatHandler::onDeleteToast(LLToast* toast) diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index f084002385..1a8ec4991d 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -94,10 +94,12 @@ LLNetMap::LLNetMap (const Params & p) mToolTipMsg() { mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS); + setScale(gSavedSettings.getF32("MiniMapScale")); } LLNetMap::~LLNetMap() { + gSavedSettings.setF32("MiniMapScale", mScale); } void LLNetMap::setScale( F32 scale ) diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h index 650bce0da4..e053b1c177 100644 --- a/indra/newview/llnetmap.h +++ b/indra/newview/llnetmap.h @@ -38,6 +38,7 @@ class LLColor4U; class LLCoordGL; class LLImageRaw; class LLViewerTexture; +class LLFloaterMap; class LLNetMap : public LLUICtrl { @@ -55,6 +56,7 @@ public: protected: LLNetMap (const Params & p); friend class LLUICtrlFactory; + friend class LLFloaterMap; public: virtual ~LLNetMap(); diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index db9d386b6b..6435126fc0 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -38,9 +38,11 @@ #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "lllistcontextmenu.h" +#include "llmenubutton.h" #include "llnotificationsutil.h" #include "lloutfitobserver.h" #include "llsidetray.h" +#include "lltoggleablemenu.h" #include "lltransutil.h" #include "llviewermenu.h" #include "llvoavatar.h" @@ -113,7 +115,7 @@ public: registrar.add("Gear.Wear", boost::bind(&LLOutfitListGearMenu::onWear, this)); registrar.add("Gear.TakeOff", boost::bind(&LLOutfitListGearMenu::onTakeOff, this)); registrar.add("Gear.Rename", boost::bind(&LLOutfitListGearMenu::onRename, this)); - registrar.add("Gear.Delete", boost::bind(&LLOutfitListGearMenu::onDelete, this)); + registrar.add("Gear.Delete", boost::bind(&LLOutfitsList::removeSelected, mOutfitList)); registrar.add("Gear.Create", boost::bind(&LLOutfitListGearMenu::onCreate, this, _2)); registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenu::onAdd, this)); @@ -121,23 +123,11 @@ public: enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenu::onEnable, this, _2)); enable_registrar.add("Gear.OnVisible", boost::bind(&LLOutfitListGearMenu::onVisible, this, _2)); - mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( + mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( "menu_outfit_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); llassert(mMenu); } - void show(LLView* spawning_view) - { - if (!mMenu) return; - - updateItemsVisibility(); - mMenu->buildDrawLabels(); - mMenu->updateParent(LLMenuGL::sMenuContainer); - S32 menu_x = 0; - S32 menu_y = spawning_view->getRect().getHeight() + mMenu->getRect().getHeight(); - LLMenuGL::showPopup(spawning_view, mMenu, menu_x, menu_y); - } - void updateItemsVisibility() { if (!mMenu) return; @@ -148,6 +138,8 @@ public: mMenu->arrangeAndClear(); // update menu height } + LLToggleableMenu* getMenu() { return mMenu; } + private: const LLUUID& getSelectedOutfitID() { @@ -205,15 +197,6 @@ private: } } - void onDelete() - { - const LLUUID& selected_outfit_id = getSelectedOutfitID(); - if (selected_outfit_id.notNull()) - { - remove_category(&gInventory, selected_outfit_id); - } - } - void onCreate(const LLSD& data) { LLWearableType::EType type = LLWearableType::typeNameToType(data.asString()); @@ -260,14 +243,20 @@ private: return true; } - LLOutfitsList* mOutfitList; - LLMenuGL* mMenu; + LLOutfitsList* mOutfitList; + LLToggleableMenu* mMenu; }; ////////////////////////////////////////////////////////////////////////// class LLOutfitContextMenu : public LLListContextMenu { +public: + + LLOutfitContextMenu(LLOutfitsList* outfit_list) + : LLListContextMenu(), + mOutfitList(outfit_list) + {} protected: /* virtual */ LLContextMenu* createMenu() { @@ -283,7 +272,7 @@ protected: boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); registrar.add("Outfit.Edit", boost::bind(editOutfit)); registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); - registrar.add("Outfit.Delete", boost::bind(deleteOutfit, selected_id)); + registrar.add("Outfit.Delete", boost::bind(&LLOutfitsList::removeSelected, mOutfitList)); enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitContextMenu::onEnable, this, _2)); enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitContextMenu::onVisible, this, _2)); @@ -346,10 +335,8 @@ protected: LLAppearanceMgr::instance().renameOutfit(outfit_cat_id); } - static void deleteOutfit(const LLUUID& outfit_cat_id) - { - remove_category(&gInventory, outfit_cat_id); - } +private: + LLOutfitsList* mOutfitList; }; ////////////////////////////////////////////////////////////////////////// @@ -366,7 +353,7 @@ LLOutfitsList::LLOutfitsList() mCategoriesObserver = new LLInventoryCategoriesObserver(); mGearMenu = new LLOutfitListGearMenu(this); - mOutfitMenu = new LLOutfitContextMenu(); + mOutfitMenu = new LLOutfitContextMenu(this); } LLOutfitsList::~LLOutfitsList() @@ -386,6 +373,11 @@ BOOL LLOutfitsList::postBuild() mAccordion = getChild<LLAccordionCtrl>("outfits_accordion"); mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR); + LLMenuButton* menu_gear_btn = getChild<LLMenuButton>("options_gear_btn"); + + menu_gear_btn->setMouseDownCallback(boost::bind(&LLOutfitListGearMenu::updateItemsVisibility, mGearMenu)); + menu_gear_btn->setMenu(mGearMenu->getMenu()); + return TRUE; } @@ -638,6 +630,14 @@ void LLOutfitsList::performAction(std::string action) void LLOutfitsList::removeSelected() { + LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitsList::onOutfitsRemovalConfirmation, this, _1, _2)); +} + +void LLOutfitsList::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; // canceled + if (mSelectedOutfitUUID.notNull()) { remove_category(&gInventory, mSelectedOutfitUUID); @@ -727,13 +727,6 @@ bool LLOutfitsList::isActionEnabled(const LLSD& userdata) return false; } -// virtual -void LLOutfitsList::showGearMenu(LLView* spawning_view) -{ - if (!mGearMenu) return; - mGearMenu->show(spawning_view); -} - void LLOutfitsList::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const { // Collect selected items from all selected lists. diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h index f73ae5bef2..a0598737f1 100644 --- a/indra/newview/lloutfitslist.h +++ b/indra/newview/lloutfitslist.h @@ -94,8 +94,6 @@ public: /*virtual*/ bool isActionEnabled(const LLSD& userdata); - /*virtual*/ void showGearMenu(LLView* spawning_view); - const LLUUID& getSelectedOutfitUUID() const { return mSelectedOutfitUUID; } /*virtual*/ void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const; @@ -112,6 +110,8 @@ public: private: + void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response); + /** * Wrapper for LLCommonUtils::computeDifference. @see LLCommonUtils::computeDifference */ diff --git a/indra/newview/llpanelappearancetab.h b/indra/newview/llpanelappearancetab.h index 81366c5db4..2ed6b00497 100644 --- a/indra/newview/llpanelappearancetab.h +++ b/indra/newview/llpanelappearancetab.h @@ -39,8 +39,6 @@ public: virtual bool isActionEnabled(const LLSD& userdata) = 0; - virtual void showGearMenu(LLView* spawning_view) = 0; - virtual void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const {} static const std::string& getFilterSubString() { return sFilterSubString; } diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 57180f63b5..94b2340c93 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -34,6 +34,7 @@ #include "llcombobox.h" #include "lldateutil.h" // ageFromDate() #include "llimview.h" +#include "llmenubutton.h" #include "llnotificationsutil.h" #include "lltexteditor.h" #include "lltexturectrl.h" @@ -340,10 +341,11 @@ LLPanelAvatarNotes::~LLPanelAvatarNotes() if(getAvatarId().notNull()) { LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this); - if(LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this); - } + } + + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this); } } @@ -475,11 +477,11 @@ LLPanelAvatarProfile::LLPanelAvatarProfile() BOOL LLPanelAvatarProfile::postBuild() { + childSetCommitCallback("see_profile_btn",(boost::bind(&LLPanelAvatarProfile::onSeeProfileBtnClick,this)),NULL); childSetCommitCallback("add_friend",(boost::bind(&LLPanelAvatarProfile::onAddFriendButtonClick,this)),NULL); childSetCommitCallback("im",(boost::bind(&LLPanelAvatarProfile::onIMButtonClick,this)),NULL); childSetCommitCallback("call",(boost::bind(&LLPanelAvatarProfile::onCallButtonClick,this)),NULL); childSetCommitCallback("teleport",(boost::bind(&LLPanelAvatarProfile::onTeleportButtonClick,this)),NULL); - childSetCommitCallback("overflow_btn", boost::bind(&LLPanelAvatarProfile::onOverflowButtonClicked, this), NULL); childSetCommitCallback("share",(boost::bind(&LLPanelAvatarProfile::onShareButtonClick,this)),NULL); childSetCommitCallback("show_on_map_btn", (boost::bind( &LLPanelAvatarProfile::onMapButtonClick, this)), NULL); @@ -500,7 +502,8 @@ BOOL LLPanelAvatarProfile::postBuild() enable.add("Profile.EnableBlock", boost::bind(&LLPanelAvatarProfile::enableBlock, this)); enable.add("Profile.EnableUnblock", boost::bind(&LLPanelAvatarProfile::enableUnblock, this)); - mProfileMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_profile_overflow.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + LLToggleableMenu* profile_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_profile_overflow.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + getChild<LLMenuButton>("overflow_btn")->setMenu(profile_menu, LLMenuButton::MP_TOP_RIGHT); LLVoiceClient::getInstance()->addObserver((LLVoiceClientStatusObserver*)this); @@ -622,6 +625,35 @@ void LLPanelAvatarProfile::processGroupProperties(const LLAvatarGroups* avatar_g getChild<LLUICtrl>("sl_groups")->setValue(groups); } +void LLPanelAvatarProfile::got_full_name_callback( const LLUUID& id, const std::string& full_name, bool is_group )
+{
+ LLStringUtil::format_map_t args;
+
+ std::string name; + if (LLAvatarNameCache::useDisplayNames()) + { + name = LLCacheName::buildUsername(full_name); + } + else + { + name = full_name; + }
+
+ args["[NAME]"] = name;
+
+ std::string linden_name = getString("name_text_args", args);
+ getChild<LLUICtrl>("name_descr_text")->setValue(linden_name);
+}
+ +void LLPanelAvatarProfile::onNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + LLStringUtil::format_map_t args;
+ args["[DISPLAY_NAME]"] = av_name.mDisplayName;
+
+ std::string display_name = getString("display_name_text_args", args);
+ getChild<LLUICtrl>("display_name_descr_text")->setValue(display_name);
+} + void LLPanelAvatarProfile::fillCommonData(const LLAvatarData* avatar_data) { //remove avatar id from cache to get fresh info @@ -633,6 +665,24 @@ void LLPanelAvatarProfile::fillCommonData(const LLAvatarData* avatar_data) LLStringUtil::format(birth_date, LLSD().with("datetime", (S32) avatar_data->born_on.secondsSinceEpoch())); args["[REG_DATE]"] = birth_date; } + + // ask (asynchronously) for the avatar name + std::string full_name;
+ if (gCacheName->getFullName(avatar_data->agent_id, full_name))
+ {
+ // name in cache, call callback directly
+ got_full_name_callback( avatar_data->agent_id, full_name, false );
+ }
+ else
+ {
+ // not in cache, lookup name
+ gCacheName->get(avatar_data->agent_id, false, boost::bind( &LLPanelAvatarProfile::got_full_name_callback, this, _1, _2, _3 ));
+ }
+
+ // get display name
+ LLAvatarNameCache::get(avatar_data->avatar_id, + boost::bind(&LLPanelAvatarProfile::onNameCache, this, _1, _2));
+
args["[AGE]"] = LLDateUtil::ageFromDate( avatar_data->born_on, LLDate::now()); std::string register_date = getString("RegisterDateFormat", args); getChild<LLUICtrl>("register_date")->setValue(register_date ); @@ -732,6 +782,11 @@ void LLPanelAvatarProfile::onAddFriendButtonClick() LLAvatarActions::requestFriendshipDialog(getAvatarId()); } +void LLPanelAvatarProfile::onSeeProfileBtnClick() +{ + LLAvatarActions::showProfile(getAvatarId()); +} + void LLPanelAvatarProfile::onIMButtonClick() { LLAvatarActions::startIM(getAvatarId()); @@ -752,32 +807,16 @@ void LLPanelAvatarProfile::onShareButtonClick() //*TODO not implemented } -void LLPanelAvatarProfile::onOverflowButtonClicked() -{ - if (!mProfileMenu->toggleVisibility()) - return; - - LLView* btn = getChild<LLView>("overflow_btn"); - - if (mProfileMenu->getButtonRect().isEmpty()) - { - mProfileMenu->setButtonRect(btn); - } - mProfileMenu->updateParent(LLMenuGL::sMenuContainer); - - LLRect rect = btn->getRect(); - LLMenuGL::showPopup(this, mProfileMenu, rect.mRight, rect.mTop); -} - LLPanelAvatarProfile::~LLPanelAvatarProfile() { if(getAvatarId().notNull()) { LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this); - if(LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this); - } + } + + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this); } } diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h index 11c7716322..070fe4579a 100644 --- a/indra/newview/llpanelavatar.h +++ b/indra/newview/llpanelavatar.h @@ -1,299 +1,298 @@ -/** - * @file llpanelavatar.h - * @brief LLPanelAvatar and related class definitions - * - * $LicenseInfo:firstyear=2004&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_LLPANELAVATAR_H -#define LL_LLPANELAVATAR_H - -#include "llpanel.h" -#include "llavatarpropertiesprocessor.h" -#include "llcallingcard.h" -#include "llvoiceclient.h" - -class LLComboBox; -class LLLineEditor; -class LLToggleableMenu; - -enum EOnlineStatus -{ - ONLINE_STATUS_NO = 0, - ONLINE_STATUS_YES = 1 -}; - -/** -* Base class for any Profile View or My Profile Panel. -*/ -class LLPanelProfileTab - : public LLPanel - , public LLAvatarPropertiesObserver -{ -public: - - /** - * Sets avatar ID, sets panel as observer of avatar related info replies from server. - */ - virtual void setAvatarId(const LLUUID& id); - - /** - * Returns avatar ID. - */ - virtual const LLUUID& getAvatarId() { return mAvatarId; } - - /** - * Sends update data request to server. - */ - virtual void updateData() = 0; - - /** - * Clears panel data if viewing avatar info for first time and sends update data request. - */ - virtual void onOpen(const LLSD& key); - - /** - * Profile tabs should close any opened panels here. - * - * Called from LLPanelProfile::onOpen() before opening new profile. - * See LLPanelPicks::onClosePanel for example. LLPanelPicks closes picture info panel - * before new profile is displayed, otherwise new profile will - * be hidden behind picture info panel. - */ - virtual void onClosePanel() {} - - /** - * Resets controls visibility, state, etc. - */ - virtual void resetControls(){}; - - /** - * Clears all data received from server. - */ - virtual void resetData(){}; - - /*virtual*/ ~LLPanelProfileTab(); - -protected: - - LLPanelProfileTab(); - - /** - * Scrolls panel to top when viewing avatar info for first time. - */ - void scrollToTop(); - - virtual void onMapButtonClick(); - - virtual void updateButtons(); - -private: - - LLUUID mAvatarId; -}; - -/** -* Panel for displaying Avatar's first and second life related info. -*/ -class LLPanelAvatarProfile - : public LLPanelProfileTab - , public LLFriendObserver - , public LLVoiceClientStatusObserver -{ -public: - LLPanelAvatarProfile(); - /*virtual*/ ~LLPanelAvatarProfile(); - - /*virtual*/ void onOpen(const LLSD& key); - - /** - * LLFriendObserver trigger - */ - virtual void changed(U32 mask); - - // Implements LLVoiceClientStatusObserver::onChange() to enable the call - // button when voice is available - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - - /*virtual*/ void setAvatarId(const LLUUID& id); - - /** - * Processes data received from server. - */ - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - - /*virtual*/ BOOL postBuild(); - - /*virtual*/ void updateData(); - - /*virtual*/ void resetControls(); - - /*virtual*/ void resetData(); - -protected: - - /** - * Process profile related data received from server. - */ - virtual void processProfileProperties(const LLAvatarData* avatar_data); - - /** - * Processes group related data received from server. - */ - virtual void processGroupProperties(const LLAvatarGroups* avatar_groups); - - /** - * Fills common for Avatar profile and My Profile fields. - */ - virtual void fillCommonData(const LLAvatarData* avatar_data); - - /** - * Fills partner data. - */ - virtual void fillPartnerData(const LLAvatarData* avatar_data); - - /** - * Fills account status. - */ - virtual void fillAccountStatus(const LLAvatarData* avatar_data); - - /** - * Opens "Pay Resident" dialog. - */ - void pay(); - - /** - * opens inventory and IM for sharing items - */ - void share(); - - /** - * Add/remove resident to/from your block list. - */ - void toggleBlock(); - - void kick(); - void freeze(); - void unfreeze(); - void csr(); - - bool enableShowOnMap(); - bool enableBlock(); - bool enableUnblock(); - bool enableGod(); - - - void onAddFriendButtonClick(); - void onIMButtonClick(); - void onCallButtonClick(); - void onTeleportButtonClick(); - void onShareButtonClick(); - void onOverflowButtonClicked(); - -private: - - typedef std::map< std::string,LLUUID> group_map_t; - group_map_t mGroups; - - LLToggleableMenu* mProfileMenu; -}; - -/** - * Panel for displaying own first and second life related info. - */ -class LLPanelMyProfile - : public LLPanelAvatarProfile -{ -public: - LLPanelMyProfile(); - - /*virtual*/ BOOL postBuild(); - -protected: - - /*virtual*/ void onOpen(const LLSD& key); - - /*virtual*/ void processProfileProperties(const LLAvatarData* avatar_data); - - /*virtual*/ void resetControls(); - -protected: - void onStatusMessageChanged(); -}; - -/** - * Panel for displaying Avatar's notes and modifying friend's rights. - */ -class LLPanelAvatarNotes - : public LLPanelProfileTab - , public LLFriendObserver - , public LLVoiceClientStatusObserver -{ -public: - LLPanelAvatarNotes(); - /*virtual*/ ~LLPanelAvatarNotes(); - - virtual void setAvatarId(const LLUUID& id); - - /** - * LLFriendObserver trigger - */ - virtual void changed(U32 mask); - - // Implements LLVoiceClientStatusObserver::onChange() to enable the call - // button when voice is available - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - - /*virtual*/ void onOpen(const LLSD& key); - - /*virtual*/ BOOL postBuild(); - - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - - /*virtual*/ void updateData(); - -protected: - - /*virtual*/ void resetControls(); - - /*virtual*/ void resetData(); - - /** - * Fills rights data for friends. - */ - void fillRightsData(); - - void rightsConfirmationCallback(const LLSD& notification, - const LLSD& response, S32 rights); - void confirmModifyRights(bool grant, S32 rights); - void onCommitRights(); - void onCommitNotes(); - - void onAddFriendButtonClick(); - void onIMButtonClick(); - void onCallButtonClick(); - void onTeleportButtonClick(); - void onShareButtonClick(); - void enableCheckboxes(bool enable); -}; - -#endif // LL_LLPANELAVATAR_H +/**
+ * @file llpanelavatar.h
+ * @brief LLPanelAvatar and related class definitions
+ *
+ * $LicenseInfo:firstyear=2004&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_LLPANELAVATAR_H
+#define LL_LLPANELAVATAR_H
+
+#include "llpanel.h"
+#include "llavatarpropertiesprocessor.h"
+#include "llcallingcard.h"
+#include "llvoiceclient.h"
+#include "llavatarnamecache.h"
+
+class LLComboBox;
+class LLLineEditor;
+
+enum EOnlineStatus
+{
+ ONLINE_STATUS_NO = 0,
+ ONLINE_STATUS_YES = 1
+};
+
+/**
+* Base class for any Profile View or My Profile Panel.
+*/
+class LLPanelProfileTab
+ : public LLPanel
+ , public LLAvatarPropertiesObserver
+{
+public:
+
+ /**
+ * Sets avatar ID, sets panel as observer of avatar related info replies from server.
+ */
+ virtual void setAvatarId(const LLUUID& id);
+
+ /**
+ * Returns avatar ID.
+ */
+ virtual const LLUUID& getAvatarId() { return mAvatarId; }
+
+ /**
+ * Sends update data request to server.
+ */
+ virtual void updateData() = 0;
+
+ /**
+ * Clears panel data if viewing avatar info for first time and sends update data request.
+ */
+ virtual void onOpen(const LLSD& key);
+
+ /**
+ * Profile tabs should close any opened panels here.
+ *
+ * Called from LLPanelProfile::onOpen() before opening new profile.
+ * See LLPanelPicks::onClosePanel for example. LLPanelPicks closes picture info panel
+ * before new profile is displayed, otherwise new profile will
+ * be hidden behind picture info panel.
+ */
+ virtual void onClosePanel() {}
+
+ /**
+ * Resets controls visibility, state, etc.
+ */
+ virtual void resetControls(){};
+
+ /**
+ * Clears all data received from server.
+ */
+ virtual void resetData(){};
+
+ /*virtual*/ ~LLPanelProfileTab();
+
+protected:
+
+ LLPanelProfileTab();
+
+ /**
+ * Scrolls panel to top when viewing avatar info for first time.
+ */
+ void scrollToTop();
+
+ virtual void onMapButtonClick();
+
+ virtual void updateButtons();
+
+private:
+
+ LLUUID mAvatarId;
+};
+
+/**
+* Panel for displaying Avatar's first and second life related info.
+*/
+class LLPanelAvatarProfile
+ : public LLPanelProfileTab
+ , public LLFriendObserver
+ , public LLVoiceClientStatusObserver
+{
+public:
+ LLPanelAvatarProfile();
+ /*virtual*/ ~LLPanelAvatarProfile();
+
+ /*virtual*/ void onOpen(const LLSD& key);
+
+ /**
+ * LLFriendObserver trigger
+ */
+ virtual void changed(U32 mask);
+
+ // Implements LLVoiceClientStatusObserver::onChange() to enable the call
+ // button when voice is available
+ /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+
+ /*virtual*/ void setAvatarId(const LLUUID& id);
+
+ /**
+ * Processes data received from server.
+ */
+ /*virtual*/ void processProperties(void* data, EAvatarProcessorType type);
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ void updateData();
+
+ /*virtual*/ void resetControls();
+
+ /*virtual*/ void resetData();
+
+protected:
+
+ /**
+ * Process profile related data received from server.
+ */
+ virtual void processProfileProperties(const LLAvatarData* avatar_data);
+
+ /**
+ * Processes group related data received from server.
+ */
+ virtual void processGroupProperties(const LLAvatarGroups* avatar_groups);
+
+ /**
+ * Fills common for Avatar profile and My Profile fields.
+ */
+ virtual void fillCommonData(const LLAvatarData* avatar_data);
+
+ /**
+ * Fills partner data.
+ */
+ virtual void fillPartnerData(const LLAvatarData* avatar_data);
+
+ /**
+ * Fills account status.
+ */
+ virtual void fillAccountStatus(const LLAvatarData* avatar_data);
+
+ /**
+ * Opens "Pay Resident" dialog.
+ */
+ void pay();
+
+ /**
+ * opens inventory and IM for sharing items
+ */
+ void share();
+
+ /**
+ * Add/remove resident to/from your block list.
+ */
+ void toggleBlock();
+
+ void kick();
+ void freeze();
+ void unfreeze();
+ void csr();
+
+ bool enableShowOnMap();
+ bool enableBlock();
+ bool enableUnblock();
+ bool enableGod();
+
+ void onSeeProfileBtnClick();
+ void onAddFriendButtonClick();
+ void onIMButtonClick();
+ void onCallButtonClick();
+ void onTeleportButtonClick();
+ void onShareButtonClick();
+
+private:
+ void got_full_name_callback( const LLUUID& id, const std::string& full_name, bool is_group );
+ void onNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
+
+ typedef std::map< std::string,LLUUID> group_map_t;
+ group_map_t mGroups;
+};
+
+/**
+ * Panel for displaying own first and second life related info.
+ */
+class LLPanelMyProfile
+ : public LLPanelAvatarProfile
+{
+public:
+ LLPanelMyProfile();
+
+ /*virtual*/ BOOL postBuild();
+
+protected:
+
+ /*virtual*/ void onOpen(const LLSD& key);
+
+ /*virtual*/ void processProfileProperties(const LLAvatarData* avatar_data);
+
+ /*virtual*/ void resetControls();
+
+protected:
+ void onStatusMessageChanged();
+};
+
+/**
+ * Panel for displaying Avatar's notes and modifying friend's rights.
+ */
+class LLPanelAvatarNotes
+ : public LLPanelProfileTab
+ , public LLFriendObserver
+ , public LLVoiceClientStatusObserver
+{
+public:
+ LLPanelAvatarNotes();
+ /*virtual*/ ~LLPanelAvatarNotes();
+
+ virtual void setAvatarId(const LLUUID& id);
+
+ /**
+ * LLFriendObserver trigger
+ */
+ virtual void changed(U32 mask);
+
+ // Implements LLVoiceClientStatusObserver::onChange() to enable the call
+ // button when voice is available
+ /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+
+ /*virtual*/ void onOpen(const LLSD& key);
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ void processProperties(void* data, EAvatarProcessorType type);
+
+ /*virtual*/ void updateData();
+
+protected:
+
+ /*virtual*/ void resetControls();
+
+ /*virtual*/ void resetData();
+
+ /**
+ * Fills rights data for friends.
+ */
+ void fillRightsData();
+
+ void rightsConfirmationCallback(const LLSD& notification,
+ const LLSD& response, S32 rights);
+ void confirmModifyRights(bool grant, S32 rights);
+ void onCommitRights();
+ void onCommitNotes();
+
+ void onAddFriendButtonClick();
+ void onIMButtonClick();
+ void onCallButtonClick();
+ void onTeleportButtonClick();
+ void onShareButtonClick();
+ void enableCheckboxes(bool enable);
+};
+
+#endif // LL_LLPANELAVATAR_H
diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 80df420a4e..ec340dc258 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -692,7 +692,8 @@ void LLPanelGroupGeneral::updateMembers() LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); if (!mListVisibleMembers || !gdatap - || !gdatap->isMemberDataComplete()) + || !gdatap->isMemberDataComplete() + || gdatap->mMembers.empty()) { return; } diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 35f898bfa6..d1362d7922 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -49,6 +49,7 @@ #include "llviewertexturelist.h" #include "llviewerwindow.h" #include "llfocusmgr.h" +#include "llviewercontrol.h" #include "roles_constants.h" @@ -742,10 +743,12 @@ LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab() mHasMatch(FALSE), mNumOwnerAdditions(0) { + mUdpateSessionID = LLUUID::null; } LLPanelGroupMembersSubTab::~LLPanelGroupMembersSubTab() { + gSavedSettings.setString("GroupMembersSortOrder", mMembersList->getSortColumnName()); } BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root) @@ -772,6 +775,17 @@ BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root) // Show the member's profile on double click. mMembersList->setDoubleClickCallback(onMemberDoubleClick, this); mMembersList->setContextMenu(LLScrollListCtrl::MENU_AVATAR); + + LLSD row; + row["columns"][0]["column"] = "name"; + row["columns"][1]["column"] = "donated"; + row["columns"][2]["column"] = "online"; + mMembersList->addElement(row); + std::string order_by = gSavedSettings.getString("GroupMembersSortOrder"); + if(!order_by.empty()) + { + mMembersList->sortByColumn(order_by, TRUE); + } LLButton* button = parent->getChild<LLButton>("member_invite", recurse); if ( button ) @@ -1529,6 +1543,10 @@ void LLPanelGroupMembersSubTab::update(LLGroupChange gc) mMemberProgress = gdatap->mMembers.begin(); mPendingMemberUpdate = TRUE; mHasMatch = FALSE; + // Generate unique ID for current updateMembers()- see onNameCache for details. + // Using unique UUID is perhaps an overkill but this way we are perfectly safe + // from coincidences. + mUdpateSessionID.generate(); } else { @@ -1556,6 +1574,63 @@ void LLPanelGroupMembersSubTab::update(LLGroupChange gc) } } +void LLPanelGroupMembersSubTab::addMemberToList(LLUUID id, LLGroupMemberData* data) +{ + if (!data) return; + LLUIString donated = getString("donation_area"); + donated.setArg("[AREA]", llformat("%d", data->getContribution())); + + LLSD row; + row["id"] = id; + + row["columns"][0]["column"] = "name"; + // value is filled in by name list control + + row["columns"][1]["column"] = "donated"; + row["columns"][1]["value"] = donated.getString(); + + row["columns"][2]["column"] = "online"; + row["columns"][2]["value"] = data->getOnlineStatus(); + row["columns"][2]["font"] = "SANSSERIF_SMALL"; + + mMembersList->addElement(row); + + mHasMatch = TRUE; +} + +void LLPanelGroupMembersSubTab::onNameCache(const LLUUID& update_id, const LLUUID& id) +{ + // Update ID is used to determine whether member whose id is passed + // into onNameCache() was passed after current or previous user-initiated update. + // This is needed to avoid probable duplication of members in list after changing filter + // or adding of members of another group if gets for their names were called on + // previous update. If this id is from get() called from older update, + // we do nothing. + if (mUdpateSessionID != update_id) return; + + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); + if (!gdatap) + { + llwarns << "LLPanelGroupMembersSubTab::updateMembers() -- No group data!" << llendl; + return; + } + + std::string fullname; + gCacheName->getFullName(id, fullname); + + LLGroupMemberData* data; + // trying to avoid unnecessary hash lookups + if (matchesSearchFilter(fullname) && ((data = gdatap->mMembers[id]) != NULL)) + { + addMemberToList(id, data); + if(!mMembersList->getEnabled()) + { + mMembersList->setEnabled(TRUE); + } + } + +} + void LLPanelGroupMembersSubTab::updateMembers() { mPendingMemberUpdate = FALSE; @@ -1580,12 +1655,13 @@ void LLPanelGroupMembersSubTab::updateMembers() //cleanup list only for first iretation if(mMemberProgress == gdatap->mMembers.begin()) + { mMembersList->deleteAllItems(); + } LLGroupMgrGroupData::member_list_t::iterator end = gdatap->mMembers.end(); - LLUIString donated = getString("donation_area"); - + S32 i = 0; for( ; mMemberProgress != end && i<UPDATE_MEMBERS_PER_FRAME; ++mMemberProgress, ++i) @@ -1593,38 +1669,19 @@ void LLPanelGroupMembersSubTab::updateMembers() if (!mMemberProgress->second) continue; // Do filtering on name if it is already in the cache. - bool add_member = true; - std::string fullname; if (gCacheName->getFullName(mMemberProgress->first, fullname)) { - if ( !matchesSearchFilter(fullname) ) + if (matchesSearchFilter(fullname)) { - add_member = false; + addMemberToList(mMemberProgress->first, mMemberProgress->second); } } - - if (add_member) + else { - donated.setArg("[AREA]", llformat("%d", mMemberProgress->second->getContribution())); - - LLSD row; - row["id"] = (*mMemberProgress).first; - - row["columns"][0]["column"] = "name"; - // value is filled in by name list control - - row["columns"][1]["column"] = "donated"; - row["columns"][1]["value"] = donated.getString(); - - row["columns"][2]["column"] = "online"; - row["columns"][2]["value"] = mMemberProgress->second->getOnlineStatus(); - row["columns"][2]["font"] = "SANSSERIF_SMALL"; - - LLScrollListItem* member = mMembersList->addElement(row); - - LLUUID id = member->getUUID(); - mHasMatch = TRUE; + // If name is not cached, onNameCache() should be called when it is cached and add this member to list. + gCacheName->get(mMemberProgress->first, FALSE, boost::bind(&LLPanelGroupMembersSubTab::onNameCache, + this, mUdpateSessionID, _1)); } } diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index 6a773f1ebb..270259c16f 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -187,6 +187,9 @@ public: virtual void setGroupID(const LLUUID& id); + void addMemberToList(LLUUID id, LLGroupMemberData* data); + void onNameCache(const LLUUID& update_id, const LLUUID& id); + protected: typedef std::map<LLUUID, LLRoleMemberChangeType> role_change_data_map_t; typedef std::map<LLUUID, role_change_data_map_t*> member_role_changes_map_t; @@ -207,6 +210,9 @@ protected: BOOL mPendingMemberUpdate; BOOL mHasMatch; + // This id is generated after each user initiated member list update(opening Roles or changing filter) + LLUUID mUdpateSessionID; + member_role_changes_map_t mMemberRoleChangeData; U32 mNumOwnerAdditions; diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 87acd83b23..c57746ec00 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -180,6 +180,9 @@ void LLPanelLandmarkInfo::setInfoType(EInfoType type) populateFoldersList(); + // Prevent the floater from losing focus (if the sidepanel is undocked). + setFocus(TRUE); + LLPanelPlaceInfo::setInfoType(type); } @@ -330,6 +333,9 @@ void LLPanelLandmarkInfo::toggleLandmarkEditMode(BOOL enabled) // when it was enabled/disabled we set the text once again. mNotesEditor->setText(mNotesEditor->getText()); } + + // Prevent the floater from losing focus (if the sidepanel is undocked). + setFocus(TRUE); } const std::string& LLPanelLandmarkInfo::getLandmarkTitle() const diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index c4a484d368..e8c8273a9d 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -47,9 +47,11 @@ #include "llinventorymodelbackgroundfetch.h" #include "llinventorypanel.h" #include "lllandmarkactions.h" +#include "llmenubutton.h" #include "llplacesinventorybridge.h" #include "llplacesinventorypanel.h" #include "llsidetray.h" +#include "lltoggleablemenu.h" #include "llviewermenu.h" #include "llviewerregion.h" @@ -191,6 +193,7 @@ LLLandmarksPanel::LLLandmarksPanel() , mLibraryInventoryPanel(NULL) , mCurrentSelectedList(NULL) , mListCommands(NULL) + , mGearButton(NULL) , mGearFolderMenu(NULL) , mGearLandmarkMenu(NULL) { @@ -517,9 +520,6 @@ void LLLandmarksPanel::setParcelID(const LLUUID& parcel_id) { if (!parcel_id.isNull()) { - //ext-4655, defensive. remove now incase this gets called twice without a remove - LLRemoteParcelInfoProcessor::getInstance()->removeObserver(parcel_id, this); - LLRemoteParcelInfoProcessor::getInstance()->addObserver(parcel_id, this); LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(parcel_id); } @@ -685,7 +685,9 @@ void LLLandmarksPanel::initListCommandsHandlers() { mListCommands = getChild<LLPanel>("bottom_panel"); - mListCommands->childSetAction(OPTIONS_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onActionsButtonClick, this)); + mGearButton = getChild<LLMenuButton>(OPTIONS_BUTTON_NAME); + mGearButton->setMouseDownCallback(boost::bind(&LLLandmarksPanel::onActionsButtonClick, this)); + mListCommands->childSetAction(TRASH_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onTrashButtonClick, this)); LLDragAndDropButton* trash_btn = mListCommands->getChild<LLDragAndDropButton>(TRASH_BUTTON_NAME); @@ -702,8 +704,8 @@ void LLLandmarksPanel::initListCommandsHandlers() mCommitCallbackRegistrar.add("Places.LandmarksGear.Folding.Action", boost::bind(&LLLandmarksPanel::onFoldingAction, this, _2)); mEnableCallbackRegistrar.add("Places.LandmarksGear.Check", boost::bind(&LLLandmarksPanel::isActionChecked, this, _2)); mEnableCallbackRegistrar.add("Places.LandmarksGear.Enable", boost::bind(&LLLandmarksPanel::isActionEnabled, this, _2)); - mGearLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_places_gear_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mGearFolderMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_places_gear_folder.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mGearLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_places_gear_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mGearFolderMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_places_gear_folder.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); mMenuAdd = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_place_add_button.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); mListCommands->childSetAction(ADD_BUTTON_NAME, boost::bind(&LLLandmarksPanel::showActionMenu, this, mMenuAdd, ADD_BUTTON_NAME)); @@ -722,7 +724,7 @@ void LLLandmarksPanel::updateListCommands() void LLLandmarksPanel::onActionsButtonClick() { - LLMenuGL* menu = mGearFolderMenu; + LLToggleableMenu* menu = mGearFolderMenu; LLFolderViewItem* cur_item = NULL; if(mCurrentSelectedList) @@ -741,7 +743,7 @@ void LLLandmarksPanel::onActionsButtonClick() } } - showActionMenu(menu,OPTIONS_BUTTON_NAME); + mGearButton->setMenu(menu); } void LLLandmarksPanel::showActionMenu(LLMenuGL* menu, std::string spawning_view_name) @@ -750,7 +752,10 @@ void LLLandmarksPanel::showActionMenu(LLMenuGL* menu, std::string spawning_view_ { menu->buildDrawLabels(); menu->updateParent(LLMenuGL::sMenuContainer); - LLView* spawning_view = getChild<LLView> (spawning_view_name); + menu->arrangeAndClear(); + + LLView* spawning_view = getChild<LLView>(spawning_view_name); + S32 menu_x, menu_y; //show menu in co-ordinates of panel spawning_view->localPointToOtherView(0, spawning_view->getRect().getHeight(), &menu_x, &menu_y, this); diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index 0d4402d8cb..8dcbca0440 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -39,7 +39,9 @@ class LLAccordionCtrlTab; class LLFolderViewItem; +class LLMenuButton; class LLMenuGL; +class LLToggleableMenu; class LLInventoryPanel; class LLPlacesInventoryPanel; @@ -155,8 +157,9 @@ private: LLPlacesInventoryPanel* mLandmarksInventoryPanel; LLPlacesInventoryPanel* mMyInventoryPanel; LLPlacesInventoryPanel* mLibraryInventoryPanel; - LLMenuGL* mGearLandmarkMenu; - LLMenuGL* mGearFolderMenu; + LLMenuButton* mGearButton; + LLToggleableMenu* mGearLandmarkMenu; + LLToggleableMenu* mGearFolderMenu; LLMenuGL* mMenuAdd; LLPlacesInventoryPanel* mCurrentSelectedList; LLInventoryObserver* mInventoryObserver; diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 467aefc60f..8d3b1fd7a0 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -35,6 +35,7 @@ #include "llsecondlifeurls.h" #include "v4color.h" +#include "llappviewer.h" #include "llbutton.h" #include "llcheckboxctrl.h" #include "llcommandhandler.h" // for secondlife:///app/login/ @@ -73,7 +74,6 @@ #endif // LL_WINDOWS #include "llsdserialize.h" -#define USE_VIEWER_AUTH 0 const S32 BLACK_BORDER_HEIGHT = 160; const S32 MAX_PASSWORD = 16; @@ -163,8 +163,6 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, mHtmlAvailable( TRUE ), mListener(new LLPanelLoginListener(this)) { - setFocusRoot(TRUE); - setBackgroundVisible(FALSE); setBackgroundOpaque(TRUE); @@ -181,18 +179,17 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, mPasswordModified = FALSE; LLPanelLogin::sInstance = this; - // add to front so we are the bottom-most child - gViewerWindow->getRootView()->addChildInBack(this); + LLView* login_holder = gViewerWindow->getLoginPanelHolder(); + if (login_holder) + { + login_holder->addChild(this); + } // Logo mLogoImage = LLUI::getUIImage("startup_logo"); buildFromFile( "panel_login.xml"); -#if USE_VIEWER_AUTH - //leave room for the login menu bar - setRect(LLRect(0, rect.getHeight()-18, rect.getWidth(), 0)); -#endif // Legacy login web page is hidden under the menu bar. // Adjust reg-in-client web browser widget to not be hidden. if (gSavedSettings.getBOOL("RegInClient")) @@ -204,16 +201,12 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, reshape(rect.getWidth(), rect.getHeight()); } -#if !USE_VIEWER_AUTH getChild<LLLineEditor>("password_edit")->setKeystrokeCallback(onPassKey, this); // change z sort of clickable text to be behind buttons //sendChildToBack(getChildView("channel_text")); sendChildToBack(getChildView("forgot_password_text")); - LLLineEditor* edit = getChild<LLLineEditor>("password_edit"); - if (edit) edit->setDrawAsterixes(TRUE); - if(LLStartUp::getStartSLURL().getType() != LLSLURL::LOCATION) { LLSLURL slurl(gSavedSettings.getString("LoginLocation")); @@ -230,7 +223,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, getChild<LLPanel>("login")->setDefaultBtn("connect_btn"); - std::string channel = gSavedSettings.getString("VersionChannelName"); + std::string channel = LLVersionInfo::getChannel(); std::string version = llformat("%s (%d)", LLVersionInfo::getShortVersion().c_str(), LLVersionInfo::getBuild()); @@ -247,7 +240,6 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLTextBox* need_help_text = getChild<LLTextBox>("login_help"); need_help_text->setClickedCallback(onClickHelp, NULL); -#endif // get the web browser control LLMediaCtrl* web_browser = getChild<LLMediaCtrl>("login_html"); @@ -262,11 +254,67 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, gResponsePtr = LLIamHereLogin::build( this ); LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr ); - + + // Show last logged in user favorites in "Start at" combo. + addUsersWithFavoritesToUsername(); + getChild<LLComboBox>("username_combo")->setTextChangedCallback(boost::bind(&LLPanelLogin::addFavoritesToStartLocation, this)); + updateLocationCombo(false); } +void LLPanelLogin::addUsersWithFavoritesToUsername() +{ + LLComboBox* combo = getChild<LLComboBox>("username_combo"); + if (!combo) return; + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(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) + { + combo->add(iter->first); + } +} + +void LLPanelLogin::addFavoritesToStartLocation() +{ + LLComboBox* combo = getChild<LLComboBox>("start_location_combo"); + if (!combo) return; + int num_items = combo->getItemCount(); + for (int i = num_items - 1; i > 2; i--) + { + combo->remove(i); + } + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(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) + { + if(iter->first != getChild<LLComboBox>("username_combo")->getSimple()) continue; + combo->addSeparator(); + LLSD 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 != "" && value != "") + { + combo->add(label, value); + } + } + break; + } +} + // force the size to be correct (XML doesn't seem to be sufficient to do this) // (with some padding so the other login screen doesn't show through) void LLPanelLogin::reshapeBrowser() @@ -274,15 +322,9 @@ void LLPanelLogin::reshapeBrowser() LLMediaCtrl* web_browser = getChild<LLMediaCtrl>("login_html"); LLRect rect = gViewerWindow->getWindowRectScaled(); LLRect html_rect; -#if USE_VIEWER_AUTH - html_rect.setCenterAndSize( - rect.getCenterX() - 2, rect.getCenterY(), - rect.getWidth() + 6, rect.getHeight()); -#else html_rect.setCenterAndSize( rect.getCenterX() - 2, rect.getCenterY() + 40, rect.getWidth() + 6, rect.getHeight() - 78 ); -#endif web_browser->setRect( html_rect ); web_browser->reshape( html_rect.getWidth(), html_rect.getHeight(), TRUE ); reshape( rect.getWidth(), rect.getHeight(), 1 ); @@ -305,7 +347,6 @@ void LLPanelLogin::setSiteIsAlive( bool alive ) else // the site is not available (missing page, server down, other badness) { -#if !USE_VIEWER_AUTH if ( web_browser ) { // hide browser control (revealing default one) @@ -314,16 +355,6 @@ void LLPanelLogin::setSiteIsAlive( bool alive ) // mark as unavailable mHtmlAvailable = FALSE; } -#else - - if ( web_browser ) - { - web_browser->navigateToLocalPage( "loading-error" , "index.html" ); - - // mark as available - mHtmlAvailable = TRUE; - } -#endif } } @@ -363,7 +394,6 @@ void LLPanelLogin::draw() if ( mHtmlAvailable ) { -#if !USE_VIEWER_AUTH if (getChild<LLView>("login_widgets")->getVisible()) { // draw a background box in black @@ -372,7 +402,6 @@ void LLPanelLogin::draw() // just the blue background to the native client UI mLogoImage->draw(0, -264, width + 8, mLogoImage->getHeight()); } -#endif } else { @@ -418,22 +447,17 @@ void LLPanelLogin::setFocus(BOOL b) // static void LLPanelLogin::giveFocus() { -#if USE_VIEWER_AUTH - if (sInstance) - { - sInstance->setFocus(TRUE); - } -#else if( sInstance ) { // Grab focus and move cursor to first blank input field - std::string username = sInstance->getChild<LLUICtrl>("username_edit")->getValue().asString(); + std::string username = sInstance->getChild<LLUICtrl>("username_combo")->getValue().asString(); std::string pass = sInstance->getChild<LLUICtrl>("password_edit")->getValue().asString(); BOOL have_username = !username.empty(); BOOL have_pass = !pass.empty(); LLLineEditor* edit = NULL; + LLComboBox* combo = NULL; if (have_username && !have_pass) { // User saved his name but not his password. Move @@ -443,7 +467,7 @@ void LLPanelLogin::giveFocus() else { // User doesn't have a name, so start there. - edit = sInstance->getChild<LLLineEditor>("username_edit"); + combo = sInstance->getChild<LLComboBox>("username_combo"); } if (edit) @@ -451,21 +475,26 @@ void LLPanelLogin::giveFocus() edit->setFocus(TRUE); edit->selectAll(); } + else if (combo) + { + combo->setFocus(TRUE); + } } -#endif } // static void LLPanelLogin::showLoginWidgets() { + // *NOTE: Mani - This may or may not be obselete code. + // It seems to be part of the defunct? reg-in-client project. sInstance->getChildView("login_widgets")->setVisible( true); LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html"); sInstance->reshapeBrowser(); // *TODO: Append all the usual login parameters, like first_login=Y etc. - std::string splash_screen_url = sInstance->getString("real_url"); + std::string splash_screen_url = LLGridManager::getInstance()->getLoginPage(); web_browser->navigateTo( splash_screen_url, "text/html" ); - LLUICtrl* username_edit = sInstance->getChild<LLUICtrl>("username_edit"); - username_edit->setFocus(TRUE); + LLUICtrl* username_combo = sInstance->getChild<LLUICtrl>("username_combo"); + username_combo->setFocus(TRUE); } // static @@ -509,16 +538,17 @@ void LLPanelLogin::setFields(LLPointer<LLCredential> credential, login_id += " "; login_id += lastname; } - sInstance->getChild<LLUICtrl>("username_edit")->setValue(login_id); + sInstance->getChild<LLComboBox>("username_combo")->setLabel(login_id); } else if((std::string)identifier["type"] == "account") { - sInstance->getChild<LLUICtrl>("username_edit")->setValue((std::string)identifier["account_name"]); + sInstance->getChild<LLComboBox>("username_combo")->setLabel((std::string)identifier["account_name"]); } else { - sInstance->getChild<LLUICtrl>("username_edit")->setValue(std::string()); + sInstance->getChild<LLComboBox>("username_combo")->setLabel(std::string()); } + sInstance->addFavoritesToStartLocation(); // if the password exists in the credential, set the password field with // a filler to get some stars LLSD authenticator = credential->getAuthenticator(); @@ -566,7 +596,7 @@ void LLPanelLogin::getFields(LLPointer<LLCredential>& credential, authenticator = credential->getAuthenticator(); } - std::string username = sInstance->getChild<LLUICtrl>("username_edit")->getValue().asString(); + std::string username = sInstance->getChild<LLUICtrl>("username_combo")->getValue().asString(); LLStringUtil::trim(username); std::string password = sInstance->getChild<LLUICtrl>("password_edit")->getValue().asString(); @@ -658,15 +688,15 @@ BOOL LLPanelLogin::areCredentialFieldsDirty() } else { - std::string username = sInstance->getChild<LLUICtrl>("username_edit")->getValue().asString(); + std::string username = sInstance->getChild<LLUICtrl>("username_combo")->getValue().asString(); LLStringUtil::trim(username); std::string password = sInstance->getChild<LLUICtrl>("password_edit")->getValue().asString(); - LLLineEditor* ctrl = sInstance->getChild<LLLineEditor>("username_edit"); - if(ctrl && ctrl->isDirty()) + LLComboBox* combo = sInstance->getChild<LLComboBox>("username_combo"); + if(combo && combo->isDirty()) { return true; } - ctrl = sInstance->getChild<LLLineEditor>("password_edit"); + LLLineEditor* ctrl = sInstance->getChild<LLLineEditor>("password_edit"); if(ctrl && ctrl->isDirty()) { return true; @@ -761,7 +791,7 @@ void LLPanelLogin::closePanel() { if (sInstance) { - gViewerWindow->getRootView()->removeChild( LLPanelLogin::sInstance ); + LLPanelLogin::sInstance->getParent()->removeChild( LLPanelLogin::sInstance ); delete sInstance; sInstance = NULL; @@ -817,7 +847,7 @@ void LLPanelLogin::loadLoginPage() LLVersionInfo::getShortVersion().c_str(), LLVersionInfo::getBuild()); - char* curl_channel = curl_escape(gSavedSettings.getString("VersionChannelName").c_str(), 0); + char* curl_channel = curl_escape(LLVersionInfo::getChannel().c_str(), 0); char* curl_version = curl_escape(version.c_str(), 0); oStr << "&channel=" << curl_channel; @@ -830,75 +860,15 @@ void LLPanelLogin::loadLoginPage() char* curl_grid = curl_escape(LLGridManager::getInstance()->getGridLabel().c_str(), 0); oStr << "&grid=" << curl_grid; curl_free(curl_grid); - gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid()); - gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor()); - - -#if USE_VIEWER_AUTH - LLURLSimString::sInstance.parse(); - - std::string location; - std::string region; - std::string password; - - if (LLURLSimString::parse()) - { - std::ostringstream oRegionStr; - location = "specify"; - oRegionStr << LLURLSimString::sInstance.mSimName << "/" << LLURLSimString::sInstance.mX << "/" - << LLURLSimString::sInstance.mY << "/" - << LLURLSimString::sInstance.mZ; - region = oRegionStr.str(); - } - else - { - location = gSavedSettings.getString("LoginLocation"); - } - std::string username; - - if(gSavedSettings.getLLSD("UserLoginInfo").size() == 3) - { - LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); - username = cmd_line_login[0].asString() + " " + cmd_line_login[1]; - password = cmd_line_login[2].asString(); - } - + // add OS info + char * os_info = curl_escape(LLAppViewer::instance()->getOSInfo().getOSStringSimple().c_str(), 0); + oStr << "&os=" << os_info; + curl_free(os_info); - char* curl_region = curl_escape(region.c_str(), 0); - - oStr <<"username=" << username << - "&location=" << location << "®ion=" << curl_region; - curl_free(curl_region); - - if (!password.empty()) - { - oStr << "&password=" << password; - } - else if (!(password = load_password_from_disk()).empty()) - { - oStr << "&password=$1$" << password; - } - if (gAutoLogin) - { - oStr << "&auto_login=TRUE"; - } - if (gSavedSettings.getBOOL("ShowStartLocation")) - { - oStr << "&show_start_location=TRUE"; - } - if (gSavedSettings.getBOOL("RememberPassword")) - { - oStr << "&remember_password=TRUE"; - } -#ifndef LL_RELEASE_FOR_DOWNLOAD - oStr << "&show_grid=TRUE"; -#else - if (gSavedSettings.getBOOL("ForceShowGrid")) - oStr << "&show_grid=TRUE"; -#endif -#endif + gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid()); + gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor()); LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html"); @@ -973,7 +943,7 @@ void LLPanelLogin::onClickConnect(void *) return; } updateStartSLURL(); - std::string username = sInstance->getChild<LLUICtrl>("username_edit")->getValue().asString(); + std::string username = sInstance->getChild<LLUICtrl>("username_combo")->getValue().asString(); if(username.empty()) diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 83e76a308b..1ef6539ecc 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -85,6 +85,8 @@ public: private: friend class LLPanelLoginListener; void reshapeBrowser(); + void addFavoritesToStartLocation(); + void addUsersWithFavoritesToUsername(); static void onClickConnect(void*); static void onClickNewAccount(void*); // static bool newAccountAlertCallback(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 5b07e4863b..c83176d980 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -39,12 +39,14 @@ #include "llinventorypanel.h" #include "llfiltereditor.h" #include "llfloaterreg.h" +#include "llmenubutton.h" #include "lloutfitobserver.h" #include "llpreviewtexture.h" #include "llresmgr.h" #include "llscrollcontainer.h" #include "llsdserialize.h" #include "llspinctrl.h" +#include "lltoggleablemenu.h" #include "lltooldraganddrop.h" #include "llviewermenu.h" #include "llviewertexturelist.h" @@ -192,6 +194,8 @@ BOOL LLPanelMainInventory::postBuild() mFilterEditor->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterEdit, this, _2)); } + mGearMenuButton = getChild<LLMenuButton>("options_gear_btn"); + initListCommandsHandlers(); // *TODO:Get the cost info from the server @@ -325,15 +329,23 @@ void LLPanelMainInventory::setSortBy(const LLSD& userdata) if (sort_field == "name") { U32 order = getActivePanel()->getSortOrder(); - getActivePanel()->setSortOrder( order & ~LLInventoryFilter::SO_DATE ); - + order &= ~LLInventoryFilter::SO_DATE; + + getActivePanel()->setSortOrder( order ); + + gSavedSettings.setU32("InventorySortOrder", order); + gSavedSettings.setBOOL("Inventory.SortByName", TRUE ); gSavedSettings.setBOOL("Inventory.SortByDate", FALSE ); } else if (sort_field == "date") { U32 order = getActivePanel()->getSortOrder(); - getActivePanel()->setSortOrder( order | LLInventoryFilter::SO_DATE ); + order |= LLInventoryFilter::SO_DATE; + + getActivePanel()->setSortOrder( order ); + + gSavedSettings.setU32("InventorySortOrder", order); gSavedSettings.setBOOL("Inventory.SortByName", FALSE ); gSavedSettings.setBOOL("Inventory.SortByDate", TRUE ); @@ -371,6 +383,8 @@ void LLPanelMainInventory::setSortBy(const LLSD& userdata) gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", TRUE ); } getActivePanel()->setSortOrder( order ); + + gSavedSettings.setU32("InventorySortOrder", order); } } @@ -492,9 +506,6 @@ void LLPanelMainInventory::onFilterSelected() return; } - BOOL recent_active = ("Recent Items" == mActivePanel->getName()); - getChildView("add_btn_panel")->setVisible( !recent_active); - setFilterSubString(mFilterSubString); LLInventoryFilter* filter = mActivePanel->getFilter(); LLFloaterInventoryFinder *finder = getFinder(); @@ -900,7 +911,6 @@ void LLFloaterInventoryFinder::selectNoTypes(void* user_data) void LLPanelMainInventory::initListCommandsHandlers() { - childSetAction("options_gear_btn", boost::bind(&LLPanelMainInventory::onGearButtonClick, this)); childSetAction("trash_btn", boost::bind(&LLPanelMainInventory::onTrashButtonClick, this)); childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddButtonClick, this)); @@ -912,8 +922,10 @@ void LLPanelMainInventory::initListCommandsHandlers() )); mCommitCallbackRegistrar.add("Inventory.GearDefault.Custom.Action", boost::bind(&LLPanelMainInventory::onCustomAction, this, _2)); + mEnableCallbackRegistrar.add("Inventory.GearDefault.Check", boost::bind(&LLPanelMainInventory::isActionChecked, this, _2)); mEnableCallbackRegistrar.add("Inventory.GearDefault.Enable", boost::bind(&LLPanelMainInventory::isActionEnabled, this, _2)); - mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_inventory_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mGearMenuButton->setMenu(mMenuGearDefault); mMenuAdd = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory_add.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); // Update the trash button when selected item(s) get worn or taken off. @@ -927,13 +939,13 @@ void LLPanelMainInventory::updateListCommands() mTrashButton->setEnabled(trash_enabled); } -void LLPanelMainInventory::onGearButtonClick() -{ - showActionMenu(mMenuGearDefault,"options_gear_btn"); -} - void LLPanelMainInventory::onAddButtonClick() { +// Gray out the "New Folder" option when the Recent tab is active as new folders will not be displayed +// unless "Always show folders" is checked in the filter options. + bool recent_active = ("Recent Items" == mActivePanel->getName()); + mMenuAdd->getChild<LLMenuItemGL>("New Folder")->setEnabled(!recent_active); + setUploadCostIfNeeded(); showActionMenu(mMenuAdd,"add_btn"); @@ -1001,6 +1013,11 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) const LLSD arg = "date"; setSortBy(arg); } + if (command_name == "sort_system_folders_to_top") + { + const LLSD arg = "systemfolderstotop"; + setSortBy(arg); + } if (command_name == "show_filters") { toggleFindOptions(); @@ -1174,6 +1191,31 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) return TRUE; } +BOOL LLPanelMainInventory::isActionChecked(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + if (command_name == "sort_by_name") + { + U32 order = getActivePanel()->getSortOrder(); + return ~order & LLInventoryFilter::SO_DATE; + } + + if (command_name == "sort_by_recent") + { + U32 order = getActivePanel()->getSortOrder(); + return order & LLInventoryFilter::SO_DATE; + } + + if (command_name == "sort_system_folders_to_top") + { + U32 order = getActivePanel()->getSortOrder(); + return order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP; + } + + return FALSE; +} + bool LLPanelMainInventory::handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept) { *accept = ACCEPT_NO; diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index cf2cc14531..c2b78ff9ea 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -40,7 +40,9 @@ class LLSaveFolderState; class LLFilterEditor; class LLTabContainer; class LLFloaterInventoryFinder; +class LLMenuButton; class LLMenuGL; +class LLToggleableMenu; class LLFloater; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -129,12 +131,12 @@ private: protected: void initListCommandsHandlers(); void updateListCommands(); - void onGearButtonClick(); void onAddButtonClick(); void showActionMenu(LLMenuGL* menu, std::string spawning_view_name); void onTrashButtonClick(); void onClipboardAction(const LLSD& userdata); BOOL isActionEnabled(const LLSD& command_name); + BOOL isActionChecked(const LLSD& userdata); void onCustomAction(const LLSD& command_name); bool handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept); /** @@ -143,8 +145,9 @@ protected: void setUploadCostIfNeeded(); private: LLDragAndDropButton* mTrashButton; - LLMenuGL* mMenuGearDefault; + LLToggleableMenu* mMenuGearDefault; LLMenuGL* mMenuAdd; + LLMenuButton* mGearMenuButton; bool mNeedUploadCost; // List Commands // diff --git a/indra/newview/llpanelme.cpp b/indra/newview/llpanelme.cpp index 5ea94e0611..d3c9c3e131 100644 --- a/indra/newview/llpanelme.cpp +++ b/indra/newview/llpanelme.cpp @@ -76,17 +76,19 @@ void LLPanelMe::onOpen(const LLSD& key) { LLPanelProfile::onOpen(key); - // Force Edit My Profile if this is the first time when user is opening Me Panel (EXT-5068) - bool opened = gSavedSettings.getBOOL("MePanelOpened"); - // In some cases Side Tray my call onOpen() twice, check getCollapsed() to be sure this - // is the last time onOpen() is called - if( !opened && !LLSideTray::getInstance()->getCollapsed() ) - { - buildEditPanel(); - openPanel(mEditPanel, getAvatarId()); - - gSavedSettings.setBOOL("MePanelOpened", true); - } + // Removed this action as per SOCIAL-431 The first time a new resident opens the profile tab + // in the sidebar, they see the old profile editing panel + // + //// Force Edit My Profile if this is the first time when user is opening Me Panel (EXT-5068) + //bool opened = gSavedSettings.getBOOL("MePanelOpened"); + //// In some cases Side Tray my call onOpen() twice, check getCollapsed() to be sure this + //// is the last time onOpen() is called + //if( !opened && !LLSideTray::getInstance()->getCollapsed() ) + //{ + // buildEditPanel(); + // openPanel(mEditPanel, getAvatarId()); + // gSavedSettings.setBOOL("MePanelOpened", true); + //} } bool LLPanelMe::notifyChildren(const LLSD& info) diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index d756a1b931..a0c320ba19 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -1695,10 +1695,10 @@ void LLPanelObject::sendPosition(BOOL btn_down) LLVector3 newpos(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get()); LLViewerRegion* regionp = mObject->getRegion(); - + // Clamp the Z height const F32 height = newpos.mV[VZ]; - const F32 min_height = LLWorld::getInstance()->getMinAllowedZ(mObject); + const F32 min_height = LLWorld::getInstance()->getMinAllowedZ(mObject, mObject->getPositionGlobal()); const F32 max_height = LLWorld::getInstance()->getRegionMaxHeight(); if (!mObject->isAttachment()) diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 408d4f29b7..c10c21683b 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -56,11 +56,13 @@ #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" #include "llloadingindicator.h" +#include "llmenubutton.h" #include "llpaneloutfitsinventory.h" #include "lluiconstants.h" #include "llsaveoutfitcombobtn.h" #include "llscrolllistctrl.h" #include "lltextbox.h" +#include "lltoggleablemenu.h" #include "lltrans.h" #include "lluictrlfactory.h" #include "llsdutil.h" @@ -151,13 +153,13 @@ std::string LLShopURLDispatcher::resolveURL(LLAssetType::EType asset_type, ESex class LLPanelOutfitEditGearMenu { public: - static LLMenuGL* create() + static LLToggleableMenu* create() { LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; registrar.add("Wearable.Create", boost::bind(onCreate, _2)); - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( + LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( "menu_cof_gear.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); llassert(menu); if (menu) @@ -184,14 +186,8 @@ private: // Populate the menu with items like "New Skin", "New Pants", etc. static void populateCreateWearableSubmenus(LLMenuGL* menu) { - LLView* menu_clothes = gMenuHolder->findChildView("COF.Gear.New_Clothes", FALSE); - LLView* menu_bp = gMenuHolder->findChildView("COF.Geear.New_Body_Parts", FALSE); - - if (!menu_clothes || !menu_bp) - { - llassert(menu_clothes && menu_bp); - return; - } + LLView* menu_clothes = gMenuHolder->getChildView("COF.Gear.New_Clothes", FALSE); + LLView* menu_bp = gMenuHolder->getChildView("COF.Geear.New_Body_Parts", FALSE); for (U8 i = LLWearableType::WT_SHAPE; i != (U8) LLWearableType::WT_COUNT; ++i) { @@ -218,7 +214,7 @@ private: class LLAddWearablesGearMenu : public LLInitClass<LLAddWearablesGearMenu> { public: - static LLMenuGL* create(LLWearableItemsList* flat_list, LLInventoryPanel* inventory_panel) + static LLToggleableMenu* create(LLWearableItemsList* flat_list, LLInventoryPanel* inventory_panel) { LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; @@ -233,7 +229,7 @@ public: enable_registrar.add("AddWearable.Gear.Check", boost::bind(onCheck, flat_list_handle, inventory_panel_handle, _2)); enable_registrar.add("AddWearable.Gear.Visible", boost::bind(onVisible, inventory_panel_handle, _2)); - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( + LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( "menu_add_wearable_gear.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); @@ -404,7 +400,9 @@ LLPanelOutfitEdit::LLPanelOutfitEdit() mFolderViewFilterCmbBox(NULL), mListViewFilterCmbBox(NULL), mWearableListManager(NULL), - mPlusBtn(NULL) + mPlusBtn(NULL), + mWearablesGearMenuBtn(NULL), + mGearMenuBtn(NULL) { mSavedFolderState = new LLSaveFolderState(); mSavedFolderState->setApply(FALSE); @@ -480,13 +478,14 @@ BOOL LLPanelOutfitEdit::postBuild() childSetCommitCallback("folder_view_btn", boost::bind(&LLPanelOutfitEdit::saveListSelection, this), NULL); childSetCommitCallback("list_view_btn", boost::bind(&LLPanelOutfitEdit::showWearablesListView, this), NULL); childSetCommitCallback("list_view_btn", boost::bind(&LLPanelOutfitEdit::saveListSelection, this), NULL); - childSetCommitCallback("wearables_gear_menu_btn", boost::bind(&LLPanelOutfitEdit::onGearButtonClick, this, _1), NULL); - childSetCommitCallback("gear_menu_btn", boost::bind(&LLPanelOutfitEdit::onGearButtonClick, this, _1), NULL); childSetCommitCallback("shop_btn_1", boost::bind(&LLPanelOutfitEdit::onShopButtonClicked, this), NULL); childSetCommitCallback("shop_btn_2", boost::bind(&LLPanelOutfitEdit::onShopButtonClicked, this), NULL); setVisibleCallback(boost::bind(&LLPanelOutfitEdit::onVisibilityChange, this, _2)); + mWearablesGearMenuBtn = getChild<LLMenuButton>("wearables_gear_menu_btn"); + mGearMenuBtn = getChild<LLMenuButton>("gear_menu_btn"); + mCOFWearables = findChild<LLCOFWearables>("cof_wearables_list"); mCOFWearables->setCommitCallback(boost::bind(&LLPanelOutfitEdit::filterWearablesBySelectedItem, this)); @@ -559,6 +558,13 @@ BOOL LLPanelOutfitEdit::postBuild() mWearableItemsList->setComparator(mWearableListViewItemsComparator); + // Creating "Add Wearables" panel gear menu after initialization of mWearableItemsList and mInventoryItemsPanel. + mAddWearablesGearMenu = LLAddWearablesGearMenu::create(mWearableItemsList, mInventoryItemsPanel); + mWearablesGearMenuBtn->setMenu(mAddWearablesGearMenu); + + mGearMenu = LLPanelOutfitEditGearMenu::create(); + mGearMenuBtn->setMenu(mGearMenu); + mSaveComboBtn.reset(new LLSaveOutfitComboBtn(this)); return TRUE; } @@ -1258,37 +1264,6 @@ void LLPanelOutfitEdit::resetAccordionState() } } -void LLPanelOutfitEdit::onGearButtonClick(LLUICtrl* clicked_button) -{ - LLMenuGL* menu = NULL; - - if (mAddWearablesPanel->getVisible()) - { - if (!mAddWearablesGearMenu) - { - mAddWearablesGearMenu = LLAddWearablesGearMenu::create(mWearableItemsList, mInventoryItemsPanel); - } - - menu = mAddWearablesGearMenu; - } - else - { - if (!mGearMenu) - { - mGearMenu = LLPanelOutfitEditGearMenu::create(); - } - - menu = mGearMenu; - } - - if (!menu) return; - - menu->arrangeAndClear(); // update menu height - S32 menu_y = menu->getRect().getHeight() + clicked_button->getRect().getHeight(); - menu->buildDrawLabels(); - LLMenuGL::showPopup(clicked_button, menu, 0, menu_y); -} - void LLPanelOutfitEdit::onAddMoreButtonClicked() { toggleAddWearablesPanel(); diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h index 2dca986e33..fd366e9cbc 100644 --- a/indra/newview/llpaneloutfitedit.h +++ b/indra/newview/llpaneloutfitedit.h @@ -54,6 +54,7 @@ class LLScrollListCtrl; class LLToggleableMenu; class LLFilterEditor; class LLFilteredWearableListManager; +class LLMenuButton; class LLMenuGL; class LLFindNonLinksByMask; class LLFindWearablesOfType; @@ -186,8 +187,6 @@ public: std::string& tooltip_msg); private: - - void onGearButtonClick(LLUICtrl* clicked_button); void onAddMoreButtonClicked(); void showFilteredWearablesListView(LLWearableType::EType type); void onOutfitChanging(bool started); @@ -234,12 +233,12 @@ private: std::vector<LLFilterItem*> mListViewItemTypes; LLCOFWearables* mCOFWearables; - LLMenuGL* mGearMenu; - LLMenuGL* mAddWearablesGearMenu; + LLToggleableMenu* mGearMenu; + LLToggleableMenu* mAddWearablesGearMenu; bool mInitialized; std::auto_ptr<LLSaveOutfitComboBtn> mSaveComboBtn; - - + LLMenuButton* mWearablesGearMenuBtn; + LLMenuButton* mGearMenuBtn; }; diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index d6d8a38ebe..a90f864ae2 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -232,9 +232,7 @@ void LLPanelOutfitsInventory::initListCommandsHandlers() { mListCommands = getChild<LLPanel>("bottom_panel"); mListCommands->childSetAction("wear_btn", boost::bind(&LLPanelOutfitsInventory::onWearButtonClick, this)); - mMyOutfitsPanel->childSetAction("options_gear_btn", boost::bind(&LLPanelOutfitsInventory::showGearMenu, this)); mMyOutfitsPanel->childSetAction("trash_btn", boost::bind(&LLPanelOutfitsInventory::onTrashButtonClick, this)); - mCurrentOutfitPanel->childSetAction("options_gear_btn", boost::bind(&LLPanelOutfitsInventory::showGearMenu, this)); } void LLPanelOutfitsInventory::updateListCommands() @@ -258,27 +256,9 @@ void LLPanelOutfitsInventory::updateListCommands() } } -void LLPanelOutfitsInventory::showGearMenu() -{ - if (!mActivePanel) return; - - LLView* spawning_view = getChild<LLView>("options_gear_btn"); - mActivePanel->showGearMenu(spawning_view); -} - void LLPanelOutfitsInventory::onTrashButtonClick() { - LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLPanelOutfitsInventory::onOutfitsRemovalConfirmation, this, _1, _2)); -} - -void LLPanelOutfitsInventory::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) return; // canceled - mMyOutfitsPanel->removeSelected(); - updateListCommands(); - updateVerbs(); } bool LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h index f1ca1dbfeb..a7917b457c 100644 --- a/indra/newview/llpaneloutfitsinventory.h +++ b/indra/newview/llpaneloutfitsinventory.h @@ -89,7 +89,6 @@ protected: void onWearButtonClick(); void showGearMenu(); void onTrashButtonClick(); - void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response); bool isActionEnabled(const LLSD& userdata); void setWearablesLoading(bool val); void onWearablesLoaded(); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 040b5319b9..54198d6aa4 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -29,11 +29,13 @@ // libs #include "llavatarname.h" #include "llfloaterreg.h" +#include "llmenubutton.h" #include "llmenugl.h" #include "llnotificationsutil.h" #include "lleventtimer.h" #include "llfiltereditor.h" #include "lltabcontainer.h" +#include "lltoggleablemenu.h" #include "lluictrlfactory.h" #include "llpanelpeople.h" @@ -229,7 +231,7 @@ public: virtual void setActive(bool) {} protected: - void updateList() + void update() { mCallback(); } @@ -237,6 +239,30 @@ protected: callback_t mCallback; }; +/** + * Update buttons on changes in our friend relations (STORM-557). + */ +class LLButtonsUpdater : public LLPanelPeople::Updater, public LLFriendObserver +{ +public: + LLButtonsUpdater(callback_t cb) + : LLPanelPeople::Updater(cb) + { + LLAvatarTracker::instance().addObserver(this); + } + + ~LLButtonsUpdater() + { + LLAvatarTracker::instance().removeObserver(this); + } + + /*virtual*/ void changed(U32 mask) + { + (void) mask; + update(); + } +}; + class LLAvatarListUpdater : public LLPanelPeople::Updater, public LLEventTimer { public: @@ -304,7 +330,7 @@ public: if (mMask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE)) { - updateList(); + update(); } // Stop updates. @@ -419,7 +445,7 @@ public: if (val) { // update immediately and start regular updates - updateList(); + update(); mEventTimer.start(); } else @@ -431,7 +457,7 @@ public: /*virtual*/ BOOL tick() { - updateList(); + update(); return FALSE; } private: @@ -448,7 +474,7 @@ public: LLRecentListUpdater(callback_t cb) : LLAvatarListUpdater(cb, 0) { - LLRecentPeople::instance().setChangedCallback(boost::bind(&LLRecentListUpdater::updateList, this)); + LLRecentPeople::instance().setChangedCallback(boost::bind(&LLRecentListUpdater::update, this)); } }; @@ -464,16 +490,22 @@ LLPanelPeople::LLPanelPeople() mAllFriendList(NULL), mNearbyList(NULL), mRecentList(NULL), - mGroupList(NULL) + mGroupList(NULL), + mNearbyGearButton(NULL), + mFriendsGearButton(NULL), + mGroupsGearButton(NULL), + mRecentGearButton(NULL) { mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList, this)); mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList, this)); mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList, this)); + mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this)); mCommitCallbackRegistrar.add("People.addFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); } LLPanelPeople::~LLPanelPeople() { + delete mButtonsUpdater; delete mNearbyListUpdater; delete mFriendListUpdater; delete mRecentListUpdater; @@ -600,11 +632,6 @@ BOOL LLPanelPeople::postBuild() buttonSetAction("teleport_btn", boost::bind(&LLPanelPeople::onTeleportButtonClicked, this)); buttonSetAction("share_btn", boost::bind(&LLPanelPeople::onShareButtonClicked, this)); - getChild<LLPanel>(NEARBY_TAB_NAME)->childSetAction("nearby_view_sort_btn",boost::bind(&LLPanelPeople::onNearbyViewSortButtonClicked, this)); - getChild<LLPanel>(RECENT_TAB_NAME)->childSetAction("recent_viewsort_btn",boost::bind(&LLPanelPeople::onRecentViewSortButtonClicked, this)); - getChild<LLPanel>(FRIENDS_TAB_NAME)->childSetAction("friends_viewsort_btn",boost::bind(&LLPanelPeople::onFriendsViewSortButtonClicked, this)); - getChild<LLPanel>(GROUP_TAB_NAME)->childSetAction("groups_viewsort_btn",boost::bind(&LLPanelPeople::onGroupsViewSortButtonClicked, this)); - // Must go after setting commit callback and initializing all pointers to children. mTabContainer->selectTabByName(NEARBY_TAB_NAME); @@ -624,24 +651,41 @@ BOOL LLPanelPeople::postBuild() enable_registrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2)); enable_registrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2)); + mNearbyGearButton = getChild<LLMenuButton>("nearby_view_sort_btn"); + mFriendsGearButton = getChild<LLMenuButton>("friends_viewsort_btn"); + mGroupsGearButton = getChild<LLMenuButton>("groups_viewsort_btn"); + mRecentGearButton = getChild<LLMenuButton>("recent_viewsort_btn"); + LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); mGroupPlusMenuHandle = plus_menu->getHandle(); - LLMenuGL* nearby_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_nearby_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + LLToggleableMenu* nearby_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_nearby_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if(nearby_view_sort) + { mNearbyViewSortMenuHandle = nearby_view_sort->getHandle(); + mNearbyGearButton->setMenu(nearby_view_sort); + } - LLMenuGL* friend_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_friends_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + LLToggleableMenu* friend_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_friends_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if(friend_view_sort) + { mFriendsViewSortMenuHandle = friend_view_sort->getHandle(); + mFriendsGearButton->setMenu(friend_view_sort); + } - LLMenuGL* group_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_groups_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + LLToggleableMenu* group_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if(group_view_sort) + { mGroupsViewSortMenuHandle = group_view_sort->getHandle(); + mGroupsGearButton->setMenu(group_view_sort); + } - LLMenuGL* recent_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_recent_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + LLToggleableMenu* recent_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_recent_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if(recent_view_sort) + { mRecentViewSortMenuHandle = recent_view_sort->getHandle(); + mRecentGearButton->setMenu(recent_view_sort); + } LLVoiceClient::getInstance()->addObserver(this); @@ -911,7 +955,7 @@ void LLPanelPeople::showGroupMenu(LLMenuGL* menu) // Calculate its coordinates. // (assumes that groups panel is the current tab) - LLPanel* bottom_panel = mTabContainer->getCurrentPanel()->getChild<LLPanel>("bottom_panel"); + LLPanel* bottom_panel = mTabContainer->getCurrentPanel()->getChild<LLPanel>("bottom_panel"); LLPanel* parent_panel = mTabContainer->getCurrentPanel(); menu->arrangeAndClear(); S32 menu_height = menu->getRect().getHeight(); @@ -1346,44 +1390,9 @@ void LLPanelPeople::onMoreButtonClicked() // *TODO: not implemented yet } -void LLPanelPeople::onFriendsViewSortButtonClicked() -{ - LLMenuGL* menu = (LLMenuGL*)mFriendsViewSortMenuHandle.get(); - if (!menu) - return; - showGroupMenu(menu); -} - -void LLPanelPeople::onGroupsViewSortButtonClicked() -{ - LLMenuGL* menu = (LLMenuGL*)mGroupsViewSortMenuHandle.get(); - if (!menu) - return; - showGroupMenu(menu); -} - -void LLPanelPeople::onRecentViewSortButtonClicked() -{ - LLMenuGL* menu = (LLMenuGL*)mRecentViewSortMenuHandle.get(); - if (!menu) - return; - showGroupMenu(menu); -} - -void LLPanelPeople::onNearbyViewSortButtonClicked() -{ - LLMenuGL* menu = (LLMenuGL*)mNearbyViewSortMenuHandle.get(); - if (!menu) - return; - showGroupMenu(menu); -} - void LLPanelPeople::onOpen(const LLSD& key) { std::string tab_name = key["people_panel_tab_name"]; - mFilterEditor -> clear(); - onFilterEdit(""); - if (!tab_name.empty()) mTabContainer->selectTabByName(tab_name); } diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index f5ff09b038..b496bb3779 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -36,6 +36,7 @@ class LLAvatarList; class LLAvatarName; class LLFilterEditor; class LLGroupList; +class LLMenuButton; class LLTabContainer; class LLPanelPeople @@ -101,10 +102,6 @@ private: void onShareButtonClicked(); void onMoreButtonClicked(); void onActivateButtonClicked(); - void onRecentViewSortButtonClicked(); - void onNearbyViewSortButtonClicked(); - void onFriendsViewSortButtonClicked(); - void onGroupsViewSortButtonClicked(); void onAvatarListDoubleClicked(LLUICtrl* ctrl); void onAvatarListCommitted(LLAvatarList* list); void onGroupPlusButtonClicked(); @@ -155,6 +152,12 @@ private: Updater* mFriendListUpdater; Updater* mNearbyListUpdater; Updater* mRecentListUpdater; + Updater* mButtonsUpdater; + + LLMenuButton* mNearbyGearButton; + LLMenuButton* mFriendsGearButton; + LLMenuButton* mGroupsGearButton; + LLMenuButton* mRecentGearButton; std::string mFilterSubString; std::string mFilterSubStringOrig; diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index 271728220c..44cca21a76 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -204,9 +204,6 @@ void LLPanelPickInfo::sendParcelInfoRequest() { if (mParcelId != mRequestedId) { - //ext-4655, remove now incase this gets called twice without a remove - LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mRequestedId, this); - LLRemoteParcelInfoProcessor::getInstance()->addObserver(mParcelId, this); LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(mParcelId); diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index ccef563544..c4f3866cad 100644..100755 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -70,6 +70,111 @@ static const std::string CLASSIFIED_NAME("classified_name"); static LLRegisterPanelClassWrapper<LLPanelPicks> t_panel_picks("panel_picks"); +class LLPickHandler : public LLCommandHandler, + public LLAvatarPropertiesObserver +{ +public: + + std::set<LLUUID> mPickIds; + + // requires trusted browser to trigger + LLPickHandler() : LLCommandHandler("pick", UNTRUSTED_THROTTLE) { } + + bool handle(const LLSD& params, const LLSD& query_map, + LLMediaCtrl* web) + { + // handle app/classified/create urls first + if (params.size() == 1 && params[0].asString() == "create") + { + createPick(); + return true; + } + + // then handle the general app/pick/{UUID}/{CMD} urls + if (params.size() < 2) + { + return false; + } + + // get the ID for the pick_id + LLUUID pick_id; + if (!pick_id.set(params[0], FALSE)) + { + return false; + } + + // edit the pick in the side tray. + // need to ask the server for more info first though... + const std::string verb = params[1].asString(); + if (verb == "edit") + { + mPickIds.insert(pick_id); + LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); + LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest(gAgent.getID(),pick_id); + return true; + } + else + { + llwarns << "unknown verb " << verb << llendl; + return false; + } + } + + void createPick() + { + LLSD params; + params["id"] = gAgent.getID(); + params["open_tab_name"] = "panel_picks"; + params["show_tab_panel"] = "create_pick"; + LLSideTray::getInstance()->showPanel("panel_me", params); + } + + void editPick(LLPickData* pick_info) + { + LLSD params; + params["open_tab_name"] = "panel_picks"; + params["show_tab_panel"] = "edit_pick"; + params["pick_id"] = pick_info->pick_id; + params["avatar_id"] = pick_info->creator_id; + params["snapshot_id"] = pick_info->snapshot_id; + params["pick_name"] = pick_info->name; + params["pick_desc"] = pick_info->desc; + + LLSideTray::getInstance()->showPanel("panel_me", params); + } + + /*virtual*/ void processProperties(void* data, EAvatarProcessorType type) + { + if (APT_PICK_INFO != type) + { + return; + } + + // is this the pick that we asked for? + LLPickData* pick_info = static_cast<LLPickData*>(data); + if (!pick_info || mPickIds.find(pick_info->pick_id) == mPickIds.end()) + { + return; + } + + // open the edit side tray for this pick + if (pick_info->creator_id == gAgent.getID()) + { + editPick(pick_info); + } + else + { + llwarns << "Can't edit a pick you did not create" << llendl; + } + + // remove our observer now that we're done + mPickIds.erase(pick_info->pick_id); + LLAvatarPropertiesProcessor::getInstance()->removeObserver(LLUUID(), this); + } +}; + +LLPickHandler gPickHandler; + class LLClassifiedHandler : public LLCommandHandler, public LLAvatarPropertiesObserver @@ -80,6 +185,8 @@ public: std::set<LLUUID> mClassifiedIds; + std::string mRequestVerb; + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { // handle app/classified/create urls first @@ -107,6 +214,15 @@ public: const std::string verb = params[1].asString(); if (verb == "about") { + mRequestVerb = verb; + mClassifiedIds.insert(classified_id); + LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); + LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(classified_id); + return true; + } + else if (verb == "edit") + { + mRequestVerb = verb; mClassifiedIds.insert(classified_id); LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(classified_id); @@ -128,18 +244,39 @@ public: void openClassified(LLAvatarClassifiedInfo* c_info) { - // open the classified info panel on the Me > Picks sidetray - LLSD params; - params["id"] = c_info->creator_id; - params["open_tab_name"] = "panel_picks"; - params["show_tab_panel"] = "classified_details"; - params["classified_id"] = c_info->classified_id; - params["classified_creator_id"] = c_info->creator_id; - params["classified_snapshot_id"] = c_info->snapshot_id; - params["classified_name"] = c_info->name; - params["classified_desc"] = c_info->description; - params["from_search"] = true; - LLSideTray::getInstance()->showPanel("panel_profile_view", params); + if (mRequestVerb == "about") + { + // open the classified info panel on the Me > Picks sidetray + LLSD params; + params["id"] = c_info->creator_id; + params["open_tab_name"] = "panel_picks"; + params["show_tab_panel"] = "classified_details"; + params["classified_id"] = c_info->classified_id; + params["classified_creator_id"] = c_info->creator_id; + params["classified_snapshot_id"] = c_info->snapshot_id; + params["classified_name"] = c_info->name; + params["classified_desc"] = c_info->description; + params["from_search"] = true; + LLSideTray::getInstance()->showPanel("panel_profile_view", params); + } + else if (mRequestVerb == "edit") + { + if (c_info->creator_id == gAgent.getID()) + { + llwarns << "edit in progress" << llendl; + // open the new classified panel on the Me > Picks sidetray + LLSD params; + params["id"] = gAgent.getID(); + params["open_tab_name"] = "panel_picks"; + params["show_tab_panel"] = "edit_classified"; + params["classified_id"] = c_info->classified_id; + LLSideTray::getInstance()->showPanel("panel_me", params); + } + else + { + llwarns << "Can't edit a classified you did not create" << llendl; + } + } } /*virtual*/ void processProperties(void* data, EAvatarProcessorType type) @@ -212,7 +349,8 @@ void LLPanelPicks::updateData() mNoPicks = false; mNoClassifieds = false; - getChild<LLUICtrl>("picks_panel_text")->setValue(LLTrans::getString("PicksClassifiedsLoadingText")); + mNoItemsLabel->setValue(LLTrans::getString("PicksClassifiedsLoadingText")); + mNoItemsLabel->setVisible(TRUE); mPicksList->clear(); LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(getAvatarId()); @@ -298,7 +436,10 @@ void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type) pick_value.insert(CLASSIFIED_ID, c_data.classified_id); pick_value.insert(CLASSIFIED_NAME, c_data.name); - mClassifiedsList->addItem(c_item, pick_value); + if (!findClassifiedById(c_data.classified_id)) + { + mClassifiedsList->addItem(c_item, pick_value); + } c_item->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickClassifiedItem, this, _1)); c_item->setRightMouseUpCallback(boost::bind(&LLPanelPicks::onRightMouseUpItem, this, _1, _2, _3, _4)); @@ -314,15 +455,17 @@ void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type) mNoClassifieds = !mClassifiedsList->size(); } - if (mNoPicks && mNoClassifieds) + bool no_data = mNoPicks && mNoClassifieds; + mNoItemsLabel->setVisible(no_data); + if (no_data) { if(getAvatarId() == gAgentID) { - getChild<LLUICtrl>("picks_panel_text")->setValue(LLTrans::getString("NoPicksClassifiedsText")); + mNoItemsLabel->setValue(LLTrans::getString("NoPicksClassifiedsText")); } else { - getChild<LLUICtrl>("picks_panel_text")->setValue(LLTrans::getString("NoAvatarPicksClassifiedsText")); + mNoItemsLabel->setValue(LLTrans::getString("NoAvatarPicksClassifiedsText")); } } } @@ -359,6 +502,8 @@ BOOL LLPanelPicks::postBuild() mPicksList->setNoItemsCommentText(getString("no_picks")); mClassifiedsList->setNoItemsCommentText(getString("no_classifieds")); + mNoItemsLabel = getChild<LLUICtrl>("picks_panel_text"); + childSetAction(XML_BTN_NEW, boost::bind(&LLPanelPicks::onClickPlusBtn, this)); childSetAction(XML_BTN_DELETE, boost::bind(&LLPanelPicks::onClickDelete, this)); childSetAction(XML_BTN_TELEPORT, boost::bind(&LLPanelPicks::onClickTeleport, this)); @@ -771,6 +916,13 @@ void LLPanelPicks::openClassifiedInfo(const LLSD ¶ms) getProfilePanel()->openPanel(mPanelClassifiedInfo, params); } +void LLPanelPicks::openClassifiedEdit(const LLSD& params) +{ + LLUUID classified_id = params["classified_id"].asUUID();; + llinfos << "opening classified " << classified_id << " for edit" << llendl; + editClassified(classified_id); +} + void LLPanelPicks::showAccordion(const std::string& name, bool show) { LLAccordionCtrlTab* tab = getChild<LLAccordionCtrlTab>(name); @@ -781,7 +933,7 @@ void LLPanelPicks::showAccordion(const std::string& name, bool show) void LLPanelPicks::onPanelPickClose(LLPanel* panel) { - panel->setVisible(FALSE); + getProfilePanel()->closePanel(panel); } void LLPanelPicks::onPanelPickSave(LLPanel* panel) @@ -940,6 +1092,12 @@ void LLPanelPicks::createPickEditPanel() // getProfilePanel()->openPanel(mPanelPickInfo, params); // } +void LLPanelPicks::openPickEdit(const LLSD& params) +{ + createPickEditPanel(); + getProfilePanel()->openPanel(mPanelPickEdit, params); +} + void LLPanelPicks::onPanelPickEdit() { LLSD selected_value = mPicksList->getSelectedValue(); @@ -973,6 +1131,35 @@ void LLPanelPicks::onPanelClassifiedEdit() { return; } + editClassified(c_item->getClassifiedId()); +} + +LLClassifiedItem *LLPanelPicks::findClassifiedById(const LLUUID& classified_id) +{ + // HACK - find item by classified id. Should be a better way. + std::vector<LLPanel*> items; + mClassifiedsList->getItems(items); + LLClassifiedItem* c_item = NULL; + for(std::vector<LLPanel*>::iterator it = items.begin(); it != items.end(); ++it) + { + LLClassifiedItem *test_item = dynamic_cast<LLClassifiedItem*>(*it); + if (test_item && test_item->getClassifiedId() == classified_id) + { + c_item = test_item; + break; + } + } + return c_item; +} + +void LLPanelPicks::editClassified(const LLUUID& classified_id) +{ + LLClassifiedItem* c_item = findClassifiedById(classified_id); + if (!c_item) + { + llwarns << "item not found for classified_id " << classified_id << llendl; + return; + } LLSD params; params["classified_id"] = c_item->getClassifiedId(); diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h index 526ba48dcb..29db110523 100644..100755 --- a/indra/newview/llpanelpicks.h +++ b/indra/newview/llpanelpicks.h @@ -76,6 +76,7 @@ public: // returns the selected pick item LLPickItem* getSelectedPickItem(); LLClassifiedItem* getSelectedClassifiedItem(); + LLClassifiedItem* findClassifiedById(const LLUUID& classified_id); //*NOTE top down approch when panel toggling is done only by // parent panels failed to work (picks related code was in my profile panel) @@ -106,8 +107,10 @@ private: void onPanelPickSave(LLPanel* panel); void onPanelClassifiedSave(LLPanelClassifiedEdit* panel); void onPanelClassifiedClose(LLPanelClassifiedInfo* panel); + void openPickEdit(const LLSD& params); void onPanelPickEdit(); void onPanelClassifiedEdit(); + void editClassified(const LLUUID& classified_id); void onClickMenuEdit(); bool onEnableMenuItem(const LLSD& user_data); @@ -118,6 +121,7 @@ private: void openPickInfo(); void openClassifiedInfo(); void openClassifiedInfo(const LLSD& params); + void openClassifiedEdit(const LLSD& params); friend class LLPanelProfile; void showAccordion(const std::string& name, bool show); @@ -149,6 +153,7 @@ private: LLPanelClassifiedInfo* mPanelClassifiedInfo; LLPanelPickEdit* mPanelPickEdit; LLToggleableMenu* mPlusMenu; + LLUICtrl* mNoItemsLabel; // <classified_id, edit_panel> typedef std::map<LLUUID, LLPanelClassifiedEdit*> panel_classified_edit_map_t; diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index 9cbb512e70..4ae0c0eb12 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -128,10 +128,6 @@ void LLPanelPlaceInfo::sendParcelInfoRequest() { if (mParcelID != mRequestedID) { - //ext-4655, defensive. remove now incase this gets called twice without a remove - //as panel never closes its ok atm (but wrong :) - LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mRequestedID, this); - LLRemoteParcelInfoProcessor::getInstance()->addObserver(mParcelID, this); LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(mParcelID); diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index f0e60386b6..00ac34efa5 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -39,6 +39,7 @@ #include "llfiltereditor.h" #include "llfirstuse.h" #include "llfloaterreg.h" +#include "llmenubutton.h" #include "llnotificationsutil.h" #include "lltabcontainer.h" #include "lltexteditor.h" @@ -282,8 +283,8 @@ BOOL LLPanelPlaces::postBuild() mCloseBtn = getChild<LLButton>("close_btn"); mCloseBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this)); - mOverflowBtn = getChild<LLButton>("overflow_btn"); - mOverflowBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onOverflowButtonClicked, this)); + mOverflowBtn = getChild<LLMenuButton>("overflow_btn"); + mOverflowBtn->setMouseDownCallback(boost::bind(&LLPanelPlaces::onOverflowButtonClicked, this)); mPlaceInfoBtn = getChild<LLButton>("profile_btn"); mPlaceInfoBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onProfileButtonClicked, this)); @@ -330,8 +331,7 @@ BOOL LLPanelPlaces::postBuild() mPlaceProfileBackBtn = mPlaceProfile->getChild<LLButton>("back_btn"); mPlaceProfileBackBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this)); - mLandmarkInfoBackBtn = mLandmarkInfo->getChild<LLButton>("back_btn"); - mLandmarkInfoBackBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this)); + mLandmarkInfo->getChild<LLButton>("back_btn")->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this)); LLLineEditor* title_editor = mLandmarkInfo->getChild<LLLineEditor>("title_editor"); title_editor->setKeystrokeCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this), NULL); @@ -347,8 +347,6 @@ BOOL LLPanelPlaces::postBuild() void LLPanelPlaces::onOpen(const LLSD& key) { - LLFirstUse::notUsingDestinationGuide(false); - if (!mPlaceProfile || !mLandmarkInfo) return; @@ -384,12 +382,7 @@ void LLPanelPlaces::onOpen(const LLSD& key) mLandmarkInfo->displayParcelInfo(LLUUID(), mPosGlobal); - // Disabling "Save", "Close" and "Back" buttons to prevent closing "Create Landmark" - // panel before created landmark is loaded. - // These buttons will be enabled when created landmark is added to inventory. mSaveBtn->setEnabled(FALSE); - mCloseBtn->setEnabled(FALSE); - mLandmarkInfoBackBtn->setEnabled(FALSE); } else if (mPlaceInfoType == LANDMARK_INFO_TYPE) { @@ -497,8 +490,6 @@ void LLPanelPlaces::setItem(LLInventoryItem* item) mEditBtn->setEnabled(is_landmark_editable); mSaveBtn->setEnabled(is_landmark_editable); - mCloseBtn->setEnabled(TRUE); - mLandmarkInfoBackBtn->setEnabled(TRUE); if (is_landmark_editable) { @@ -762,6 +753,11 @@ void LLPanelPlaces::onOverflowButtonClicked() // there is no landmark already pointing to that parcel in agent's inventory. menu->getChild<LLMenuItemCallGL>("landmark")->setEnabled(is_agent_place_info_visible && !LLLandmarkActions::landmarkAlreadyExists()); + // STORM-411 + // Creating landmarks for remote locations is impossible. + // So hide menu item "Make a Landmark" in "Teleport History Profile" panel. + menu->setItemVisible("landmark", mPlaceInfoType != TELEPORT_HISTORY_INFO_TYPE); + menu->arrangeAndClear(); } else if (mPlaceInfoType == LANDMARK_INFO_TYPE && mLandmarkMenu != NULL) { @@ -783,16 +779,7 @@ void LLPanelPlaces::onOverflowButtonClicked() return; } - if (!menu->toggleVisibility()) - return; - - if (menu->getButtonRect().isEmpty()) - { - menu->setButtonRect(mOverflowBtn); - } - menu->updateParent(LLMenuGL::sMenuContainer); - LLRect rect = mOverflowBtn->getRect(); - LLMenuGL::showPopup(this, menu, rect.mRight, rect.mTop); + mOverflowBtn->setMenu(menu, LLMenuButton::MP_TOP_RIGHT); } void LLPanelPlaces::onProfileButtonClicked() @@ -1137,13 +1124,6 @@ void LLPanelPlaces::updateVerbs() { mTeleportBtn->setEnabled(have_3d_pos); } - - // Do not enable landmark info Back button when we are waiting - // for newly created landmark to load. - if (!is_create_landmark_visible) - { - mLandmarkInfoBackBtn->setEnabled(TRUE); - } } else { diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index c3b2ab806f..b335f88a48 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -47,6 +47,7 @@ class LLPlacesParcelObserver; class LLRemoteParcelInfoObserver; class LLTabContainer; class LLToggleableMenu; +class LLMenuButton; typedef std::pair<LLUUID, std::string> folder_pair_t; @@ -116,14 +117,13 @@ private: LLToggleableMenu* mLandmarkMenu; LLButton* mPlaceProfileBackBtn; - LLButton* mLandmarkInfoBackBtn; LLButton* mTeleportBtn; LLButton* mShowOnMapBtn; LLButton* mEditBtn; LLButton* mSaveBtn; LLButton* mCancelBtn; LLButton* mCloseBtn; - LLButton* mOverflowBtn; + LLMenuButton* mOverflowBtn; LLButton* mPlaceInfoBtn; LLPlacesInventoryObserver* mInventoryObserver; diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index b04971f980..82ff6c3487 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -59,6 +59,7 @@ #include "llvovolume.h" #include "llweb.h" #include "llwindow.h" +#include "llwindowshade.h" #include "llfloatertools.h" // to enable hide if build tools are up // Functions pulled from pipeline.cpp @@ -90,7 +91,9 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mTargetObjectNormal(LLVector3::zero), mZoomObjectID(LLUUID::null), mZoomObjectFace(0), - mVolumeSliderVisible(0) + mVolumeSliderVisible(0), + mWindowShade(NULL), + mHideImmediately(false) { mCommitCallbackRegistrar.add("MediaCtrl.Close", boost::bind(&LLPanelPrimMediaControls::onClickClose, this)); mCommitCallbackRegistrar.add("MediaCtrl.Back", boost::bind(&LLPanelPrimMediaControls::onClickBack, this)); @@ -205,6 +208,11 @@ BOOL LLPanelPrimMediaControls::postBuild() mMediaAddress->setFocusReceivedCallback(boost::bind(&LLPanelPrimMediaControls::onInputURL, _1, this )); + gAgent.setMouselookModeInCallback(boost::bind(&LLPanelPrimMediaControls::onMouselookModeIn, this)); + + LLWindowShade::Params window_shade_params; + window_shade_params.name = "window_shade"; + mCurrentZoom = ZOOM_NONE; // clicks on buttons do not remove keyboard focus from media setIsChrome(TRUE); @@ -519,7 +527,7 @@ void LLPanelPrimMediaControls::updateShape() if(LLPluginClassMediaOwner::MEDIA_LOADING == media_plugin->getStatus()) { mMediaProgressPanel->setVisible(true); - mMediaProgressBar->setPercent(media_plugin->getProgressPercent()); + mMediaProgressBar->setValue(media_plugin->getProgressPercent()); } else { @@ -620,12 +628,12 @@ void LLPanelPrimMediaControls::updateShape() // convert screenspace bbox to pixels (in screen coords) LLRect window_rect = gViewerWindow->getWorldViewRectScaled(); LLCoordGL screen_min; - screen_min.mX = llround((F32)window_rect.getWidth() * (min.mV[VX] + 1.f) * 0.5f); - screen_min.mY = llround((F32)window_rect.getHeight() * (min.mV[VY] + 1.f) * 0.5f); + screen_min.mX = llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (min.mV[VX] + 1.f) * 0.5f); + screen_min.mY = llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (min.mV[VY] + 1.f) * 0.5f); LLCoordGL screen_max; - screen_max.mX = llround((F32)window_rect.getWidth() * (max.mV[VX] + 1.f) * 0.5f); - screen_max.mY = llround((F32)window_rect.getHeight() * (max.mV[VY] + 1.f) * 0.5f); + screen_max.mX = llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (max.mV[VX] + 1.f) * 0.5f); + screen_max.mY = llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (max.mV[VY] + 1.f) * 0.5f); // grow panel so that screenspace bounding box fits inside "media_region" element of panel LLRect media_panel_rect; @@ -698,27 +706,41 @@ void LLPanelPrimMediaControls::updateShape() /*virtual*/ void LLPanelPrimMediaControls::draw() { + LLViewerMediaImpl* impl = getTargetMediaImpl(); + if (impl) + { + LLNotificationPtr notification = impl->getCurrentNotification(); + if (notification != mActiveNotification) + { + mActiveNotification = notification; + if (notification) + { + showNotification(notification); + } + else + { + hideNotification(); + } + } + } + F32 alpha = getDrawContext().mAlpha; - if(mFadeTimer.getStarted()) + if(mHideImmediately) + { + //hide this panel + clearFaceOnFade(); + + mHideImmediately = false; + } + else if(mFadeTimer.getStarted()) { F32 time = mFadeTimer.getElapsedTimeF32(); alpha *= llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); if(time >= mControlFadeTime) { - if(mClearFaceOnFade) - { - // Hiding this object makes scroll events go missing after it fades out - // (see DEV-41755 for a full description of the train wreck). - // Only hide the controls when we're untargeting. - setVisible(FALSE); - - mClearFaceOnFade = false; - mVolumeSliderVisible = 0; - mTargetImplID = LLUUID::null; - mTargetObjectID = LLUUID::null; - mTargetObjectFace = 0; - } + //hide this panel + clearFaceOnFade(); } } @@ -1295,3 +1317,62 @@ bool LLPanelPrimMediaControls::shouldVolumeSliderBeVisible() { return mVolumeSliderVisible > 0; } + + +void LLPanelPrimMediaControls::clearFaceOnFade() +{ + if(mClearFaceOnFade) + { + // Hiding this object makes scroll events go missing after it fades out + // (see DEV-41755 for a full description of the train wreck). + // Only hide the controls when we're untargeting. + setVisible(FALSE); + + mClearFaceOnFade = false; + mVolumeSliderVisible = 0; + mTargetImplID = LLUUID::null; + mTargetObjectID = LLUUID::null; + mTargetObjectFace = 0; + } +} + +void LLPanelPrimMediaControls::onMouselookModeIn() +{ + LLViewerMediaFocus::getInstance()->clearHover(); + mHideImmediately = true; +} + +void LLPanelPrimMediaControls::showNotification(LLNotificationPtr notify) +{ + delete mWindowShade; + LLWindowShade::Params params; + params.rect = mMediaRegion->getLocalRect(); + params.follows.flags = FOLLOWS_ALL; + params.notification = notify; + + //HACK: don't hardcode this + if (notify->getIcon() == "Popup_Caution") + { + params.bg_image.name = "Yellow_Gradient"; + params.text_color = LLColor4::black; + } + else + { + //HACK: make this a property of the notification itself, "cancellable" + params.can_close = false; + params.text_color.control = "LabelTextColor"; + } + + mWindowShade = LLUICtrlFactory::create<LLWindowShade>(params); + + mMediaRegion->addChild(mWindowShade); + mWindowShade->show(); +} + +void LLPanelPrimMediaControls::hideNotification() +{ + if (mWindowShade) + { + mWindowShade->hide(); + } +} diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h index 3ec24f0e24..66956181f2 100644 --- a/indra/newview/llpanelprimmediacontrols.h +++ b/indra/newview/llpanelprimmediacontrols.h @@ -29,6 +29,7 @@ #include "llpanel.h" #include "llviewermedia.h" +#include "llnotificationptr.h" class LLButton; class LLCoordWindow; @@ -37,6 +38,7 @@ class LLLayoutStack; class LLProgressBar; class LLSliderCtrl; class LLViewerMediaImpl; +class LLWindowShade; class LLPanelPrimMediaControls : public LLPanel { @@ -54,6 +56,10 @@ public: void updateShape(); bool isMouseOver(); + void showNotification(LLNotificationPtr notify); + void hideNotification(); + + enum EZoomLevel { ZOOM_NONE = 0, @@ -131,7 +137,11 @@ private: LLPluginClassMedia* getTargetMediaPlugin(); private: - + + void clearFaceOnFade(); + + void onMouselookModeIn(); + LLView *mMediaRegion; LLUICtrl *mBackCtrl; LLUICtrl *mFwdCtrl; @@ -162,6 +172,7 @@ private: LLUICtrl *mRightBookend; LLUIImage* mBackgroundImage; LLUIImage* mVolumeSliderBackgroundImage; + LLWindowShade* mWindowShade; F32 mSkipStep; S32 mMinWidth; S32 mMinHeight; @@ -179,6 +190,7 @@ private: bool mPauseFadeout; bool mUpdateSlider; bool mClearFaceOnFade; + bool mHideImmediately; LLMatrix4 mLastCameraMat; EZoomLevel mCurrentZoom; @@ -204,6 +216,8 @@ private: S32 mZoomObjectFace; S32 mVolumeSliderVisible; + + LLNotificationPtr mActiveNotification; }; #endif // LL_PANELPRIMMEDIACONTROLS_H diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 4e63563979..4f13c0c022 100644..100755 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -33,10 +33,41 @@ #include "llcommandhandler.h" #include "llpanelpicks.h" #include "lltabcontainer.h" +#include "llviewercontrol.h" static const std::string PANEL_PICKS = "panel_picks"; static const std::string PANEL_PROFILE = "panel_profile"; +std::string getProfileURL(const std::string& agent_name) +{ + std::string url = gSavedSettings.getString("WebProfileURL"); + LLSD subs; + subs["AGENT_NAME"] = agent_name; + url = LLWeb::expandURLSubstitutions(url,subs); + LLStringUtil::toLower(url); + return url; +} + +class LLProfileHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLProfileHandler() : LLCommandHandler("profile", UNTRUSTED_THROTTLE) { } + + bool handle(const LLSD& params, const LLSD& query_map, + LLMediaCtrl* web) + { + if (params.size() < 1) return false; + std::string agent_name = params[0]; + llinfos << "Profile, agent_name " << agent_name << llendl; + std::string url = getProfileURL(agent_name); + LLWeb::loadWebURLInternal(url); + + return true; + } +}; +LLProfileHandler gProfileHandler; + class LLAgentHandler : public LLCommandHandler { public: @@ -114,11 +145,109 @@ public: LLAgentHandler gAgentHandler; +//-- LLPanelProfile::ChildStack begins ---------------------------------------- +LLPanelProfile::ChildStack::ChildStack() +: mParent(NULL) +{ +} + +void LLPanelProfile::ChildStack::setParent(LLPanel* parent) +{ + llassert_always(parent != NULL); + mParent = parent; +} + +/// Save current parent's child views and remove them from the child list. +bool LLPanelProfile::ChildStack::push() +{ + view_list_t vlist = *mParent->getChildList(); + + for (view_list_t::const_iterator it = vlist.begin(); it != vlist.end(); ++it) + { + LLView* viewp = *it; + mParent->removeChild(viewp); + } + + mStack.push_back(vlist); + dump(); + return true; +} + +/// Restore saved children (adding them back to the child list). +bool LLPanelProfile::ChildStack::pop() +{ + if (mStack.size() == 0) + { + llwarns << "Empty stack" << llendl; + llassert(mStack.size() == 0); + return false; + } + + view_list_t& top = mStack.back(); + for (view_list_t::const_iterator it = top.begin(); it != top.end(); ++it) + { + LLView* viewp = *it; + mParent->addChild(viewp); + } + + mStack.pop_back(); + dump(); + return true; +} + +/// Temporarily add all saved children back. +void LLPanelProfile::ChildStack::preParentReshape() +{ + mSavedStack = mStack; + while(mStack.size() > 0) + { + pop(); + } +} + +/// Add the temporarily saved children back. +void LLPanelProfile::ChildStack::postParentReshape() +{ + mStack = mSavedStack; + mSavedStack = stack_t(); + + for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it) + { + const view_list_t& vlist = (*stack_it); + for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it) + { + LLView* viewp = *list_it; + lldebugs << "removing " << viewp->getName() << llendl; + mParent->removeChild(viewp); + } + } +} + +void LLPanelProfile::ChildStack::dump() +{ + unsigned lvl = 0; + lldebugs << "child stack dump:" << llendl; + for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it, ++lvl) + { + std::ostringstream dbg_line; + dbg_line << "lvl #" << lvl << ":"; + const view_list_t& vlist = (*stack_it); + for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it) + { + dbg_line << " " << (*list_it)->getName(); + } + lldebugs << dbg_line.str() << llendl; + } +} + +//-- LLPanelProfile::ChildStack ends ------------------------------------------ + LLPanelProfile::LLPanelProfile() : LLPanel() , mTabCtrl(NULL) , mAvatarId(LLUUID::null) { + mChildStack.setParent(this); } BOOL LLPanelProfile::postBuild() @@ -136,6 +265,15 @@ BOOL LLPanelProfile::postBuild() return TRUE; } +// virtual +void LLPanelProfile::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + // Temporarily add saved children back and reshape them. + mChildStack.preParentReshape(); + LLPanel::reshape(width, height, called_from_parent); + mChildStack.postParentReshape(); +} + void LLPanelProfile::onOpen(const LLSD& key) { // open the desired panel @@ -174,10 +312,39 @@ void LLPanelProfile::onOpen(const LLSD& key) picks->openClassifiedInfo(params); } } + else if (panel == "edit_classified") + { + LLPanelPicks* picks = dynamic_cast<LLPanelPicks *>(getTabContainer()[PANEL_PICKS]); + if (picks) + { + LLSD params = key; + params.erase("show_tab_panel"); + params.erase("open_tab_name"); + picks->openClassifiedEdit(params); + } + } + else if (panel == "create_pick") + { + LLPanelPicks* picks = dynamic_cast<LLPanelPicks *>(getTabContainer()[PANEL_PICKS]); + if (picks) + { + picks->createNewPick(); + } + } + else if (panel == "edit_pick") + { + LLPanelPicks* picks = dynamic_cast<LLPanelPicks *>(getTabContainer()[PANEL_PICKS]); + if (picks) + { + LLSD params = key; + params.erase("show_tab_panel"); + params.erase("open_tab_name"); + picks->openPickEdit(params); + } + } } } -//*TODO redo panel toggling void LLPanelProfile::togglePanel(LLPanel* panel, const LLSD& key) { // TRUE - we need to open/expand "panel" @@ -204,19 +371,12 @@ void LLPanelProfile::onTabSelected(const LLSD& param) } } -void LLPanelProfile::setAllChildrenVisible(BOOL visible) -{ - const child_list_t* child_list = getChildList(); - child_list_const_iter_t child_it = child_list->begin(); - for (; child_it != child_list->end(); ++child_it) - { - LLView* viewp = *child_it; - viewp->setVisible(visible); - } -} - void LLPanelProfile::openPanel(LLPanel* panel, const LLSD& params) { + // Hide currently visible panel (STORM-690). + mChildStack.push(); + + // Add the panel or bring it to front. if (panel->getParent() != this) { addChild(panel); @@ -227,7 +387,7 @@ void LLPanelProfile::openPanel(LLPanel* panel, const LLSD& params) } panel->setVisible(TRUE); - + panel->setFocus(TRUE); // prevent losing focus by the floater panel->onOpen(params); LLRect new_rect = getRect(); @@ -243,6 +403,20 @@ void LLPanelProfile::closePanel(LLPanel* panel) if (panel->getParent() == this) { removeChild(panel); + + // Make the underlying panel visible. + mChildStack.pop(); + + // Prevent losing focus by the floater + const child_list_t* child_list = getChildList(); + if (child_list->size() > 0) + { + child_list->front()->setFocus(TRUE); + } + else + { + llwarns << "No underlying panel to focus." << llendl; + } } } diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index d2bcee8076..fca359f51e 100644..100755 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -32,6 +32,8 @@ class LLTabContainer; +std::string getProfileURL(const std::string& agent_name); + /** * Base class for Profile View and My Profile. */ @@ -41,7 +43,7 @@ class LLPanelProfile : public LLPanel public: /*virtual*/ BOOL postBuild(); - + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); /*virtual*/ void onOpen(const LLSD& key); virtual void togglePanel(LLPanel*, const LLSD& key = LLSD()); @@ -58,8 +60,6 @@ protected: virtual void onTabSelected(const LLSD& param); - virtual void setAllChildrenVisible(BOOL visible); - LLTabContainer* getTabCtrl() { return mTabCtrl; } const LLUUID& getAvatarId() { return mAvatarId; } @@ -72,8 +72,34 @@ protected: private: + //-- ChildStack begins ---------------------------------------------------- + class ChildStack + { + LOG_CLASS(LLPanelProfile::ChildStack); + public: + ChildStack(); + void setParent(LLPanel* parent); + + bool push(); + bool pop(); + void preParentReshape(); + void postParentReshape(); + + private: + void dump(); + + typedef LLView::child_list_t view_list_t; + typedef std::list<view_list_t> stack_t; + + stack_t mStack; + stack_t mSavedStack; + LLPanel* mParent; + }; + //-- ChildStack ends ------------------------------------------------------ + LLTabContainer* mTabCtrl; profile_tabs_t mTabContainer; + ChildStack mChildStack; LLUUID mAvatarId; }; diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 9b8167b15a..9b35e78134 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llfloaterreg.h" +#include "llmenubutton.h" #include "llfloaterworldmap.h" #include "llpanelteleporthistory.h" @@ -40,6 +41,7 @@ #include "llflatlistview.h" #include "llnotificationsutil.h" #include "lltextbox.h" +#include "lltoggleablemenu.h" #include "llviewermenu.h" #include "lllandmarkactions.h" #include "llclipboard.h" @@ -179,9 +181,11 @@ void LLTeleportHistoryFlatItem::setRegionName(const std::string& name) void LLTeleportHistoryFlatItem::updateTitle() { + static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", LLColor4U(255, 255, 255)); + LLTextUtil::textboxSetHighlightedVal( mTitle, - LLStyle::Params(), + LLStyle::Params().color(sFgColor), mRegionName, mHighlight); } @@ -375,7 +379,8 @@ LLTeleportHistoryPanel::LLTeleportHistoryPanel() mHistoryAccordion(NULL), mAccordionTabMenu(NULL), mLastSelectedFlatlList(NULL), - mLastSelectedItemIndex(-1) + mLastSelectedItemIndex(-1), + mMenuGearButton(NULL) { buildFromFile( "panel_teleport_history.xml"); } @@ -439,8 +444,6 @@ BOOL LLTeleportHistoryPanel::postBuild() } } - getChild<LLPanel>("bottom_panel")->childSetAction("gear_btn",boost::bind(&LLTeleportHistoryPanel::onGearButtonClicked, this)); - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; registrar.add("TeleportHistory.ExpandAllFolders", boost::bind(&LLTeleportHistoryPanel::onExpandAllFolders, this)); @@ -448,9 +451,14 @@ BOOL LLTeleportHistoryPanel::postBuild() registrar.add("TeleportHistory.ClearTeleportHistory", boost::bind(&LLTeleportHistoryPanel::onClearTeleportHistory, this)); mEnableCallbackRegistrar.add("TeleportHistory.GearMenu.Enable", boost::bind(&LLTeleportHistoryPanel::isActionEnabled, this, _2)); - LLMenuGL* gear_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_teleport_history_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mMenuGearButton = getChild<LLMenuButton>("gear_btn"); + + LLToggleableMenu* gear_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_teleport_history_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());; if(gear_menu) + { mGearMenuHandle = gear_menu->getHandle(); + mMenuGearButton->setMenu(gear_menu); + } return TRUE; } @@ -985,27 +993,6 @@ LLFlatListView* LLTeleportHistoryPanel::getFlatListViewFromTab(LLAccordionCtrlTa return NULL; } -void LLTeleportHistoryPanel::onGearButtonClicked() -{ - LLMenuGL* menu = (LLMenuGL*)mGearMenuHandle.get(); - if (!menu) - return; - - // Shows the menu at the top of the button bar. - - // Calculate its coordinates. - LLPanel* bottom_panel = getChild<LLPanel>("bottom_panel"); - menu->arrangeAndClear(); - S32 menu_height = menu->getRect().getHeight(); - S32 menu_x = -2; // *HACK: compensates HPAD in showPopup() - S32 menu_y = bottom_panel->getRect().mTop + menu_height; - - // Actually show the menu. - menu->buildDrawLabels(); - menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, menu, menu_x, menu_y); -} - bool LLTeleportHistoryPanel::isActionEnabled(const LLSD& userdata) const { S32 tabs_cnt = mItemContainers.size(); diff --git a/indra/newview/llpanelteleporthistory.h b/indra/newview/llpanelteleporthistory.h index b5a025b39b..3d29454d15 100644 --- a/indra/newview/llpanelteleporthistory.h +++ b/indra/newview/llpanelteleporthistory.h @@ -38,6 +38,7 @@ class LLTeleportHistoryStorage; class LLAccordionCtrl; class LLAccordionCtrlTab; class LLFlatListView; +class LLMenuButton; class LLTeleportHistoryPanel : public LLPanelPlacesTab { @@ -94,7 +95,6 @@ private: void showTeleportHistory(); void handleItemSelect(LLFlatListView* ); LLFlatListView* getFlatListViewFromTab(LLAccordionCtrlTab *); - void onGearButtonClicked(); bool isActionEnabled(const LLSD& userdata) const; void setAccordionCollapsedByUser(LLUICtrl* acc_tab, bool collapsed); @@ -118,6 +118,7 @@ private: ContextMenu mContextMenu; LLContextMenu* mAccordionTabMenu; LLHandle<LLView> mGearMenuHandle; + LLMenuButton* mMenuGearButton; }; diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp index a9ca7314ce..30949f8f02 100644 --- a/indra/newview/llpaneltopinfobar.cpp +++ b/indra/newview/llpaneltopinfobar.cpp @@ -38,6 +38,7 @@ #include "llsidetray.h" #include "llslurl.h" #include "llstatusbar.h" +#include "lltrans.h" #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewermenu.h" @@ -102,6 +103,13 @@ void LLPanelTopInfoBar::initParcelIcons() mParcelIcon[SCRIPTS_ICON] = getChild<LLIconCtrl>("scripts_icon"); mParcelIcon[DAMAGE_ICON] = getChild<LLIconCtrl>("damage_icon"); + mParcelIcon[VOICE_ICON]->setToolTip(LLTrans::getString("LocationCtrlVoiceTooltip")); + mParcelIcon[FLY_ICON]->setToolTip(LLTrans::getString("LocationCtrlFlyTooltip")); + mParcelIcon[PUSH_ICON]->setToolTip(LLTrans::getString("LocationCtrlPushTooltip")); + mParcelIcon[BUILD_ICON]->setToolTip(LLTrans::getString("LocationCtrlBuildTooltip")); + mParcelIcon[SCRIPTS_ICON]->setToolTip(LLTrans::getString("LocationCtrlScriptsTooltip")); + mParcelIcon[DAMAGE_ICON]->setToolTip(LLTrans::getString("LocationCtrlDamageTooltip")); + mParcelIcon[VOICE_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, VOICE_ICON)); mParcelIcon[FLY_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, FLY_ICON)); mParcelIcon[PUSH_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, PUSH_ICON)); @@ -129,6 +137,7 @@ BOOL LLPanelTopInfoBar::postBuild() { mInfoBtn = getChild<LLButton>("place_info_btn"); mInfoBtn->setClickedCallback(boost::bind(&LLPanelTopInfoBar::onInfoButtonClicked, this)); + mInfoBtn->setToolTip(LLTrans::getString("LocationCtrlInfoBtnTooltip")); mParcelInfoText = getChild<LLTextBox>("parcel_info_text"); mDamageText = getChild<LLTextBox>("damage_text"); diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp index 860470cd73..911a9e5dda 100644 --- a/indra/newview/llpanelwearing.cpp +++ b/indra/newview/llpanelwearing.cpp @@ -28,10 +28,13 @@ #include "llpanelwearing.h" +#include "lltoggleablemenu.h" + #include "llappearancemgr.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" +#include "llmenubutton.h" #include "llsidetray.h" #include "llviewermenu.h" #include "llwearableitemslist.h" @@ -58,21 +61,12 @@ public: enable_registrar.add("Gear.OnEnable", boost::bind(&LLPanelWearing::isActionEnabled, mPanelWearing, _2)); - mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>( + mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( "menu_wearing_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); llassert(mMenu); } - void show(LLView* spawning_view) - { - if (!mMenu) return; - - mMenu->buildDrawLabels(); - mMenu->updateParent(LLMenuGL::sMenuContainer); - S32 menu_x = 0; - S32 menu_y = spawning_view->getRect().getHeight() + mMenu->getRect().getHeight(); - LLMenuGL::showPopup(spawning_view, mMenu, menu_x, menu_y); - } + LLToggleableMenu* getMenu() { return mMenu; } private: @@ -87,8 +81,8 @@ private: } } - LLMenuGL* mMenu; - LLPanelWearing* mPanelWearing; + LLToggleableMenu* mMenu; + LLPanelWearing* mPanelWearing; }; ////////////////////////////////////////////////////////////////////////// @@ -189,6 +183,10 @@ BOOL LLPanelWearing::postBuild() mCOFItemsList = getChild<LLWearableItemsList>("cof_items_list"); mCOFItemsList->setRightMouseDownCallback(boost::bind(&LLPanelWearing::onWearableItemsListRightClick, this, _1, _2, _3)); + LLMenuButton* menu_gear_btn = getChild<LLMenuButton>("options_gear_btn"); + + menu_gear_btn->setMenu(mGearMenu->getMenu()); + return TRUE; } @@ -253,13 +251,6 @@ bool LLPanelWearing::isActionEnabled(const LLSD& userdata) return false; } -// virtual -void LLPanelWearing::showGearMenu(LLView* spawning_view) -{ - if (!mGearMenu) return; - mGearMenu->show(spawning_view); -} - boost::signals2::connection LLPanelWearing::setSelectionChangeCallback(commit_callback_t cb) { if (!mCOFItemsList) return boost::signals2::connection(); diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h index 1fa97735b1..157b2c4c5f 100644 --- a/indra/newview/llpanelwearing.h +++ b/indra/newview/llpanelwearing.h @@ -58,8 +58,6 @@ public: /*virtual*/ bool isActionEnabled(const LLSD& userdata); - /*virtual*/ void showGearMenu(LLView* spawning_view); - /*virtual*/ void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const; boost::signals2::connection setSelectionChangeCallback(commit_callback_t cb); diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 01b3b5572e..54053cf89f 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -197,17 +197,20 @@ private: uuid_set_t mAvalineCallers; }; -LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu/* = true*/, - bool exclude_agent /*= true*/, bool can_toggle_icons /*= true*/): +LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, + LLAvatarList* avatar_list, + bool use_context_menu/* = true*/, + bool exclude_agent /*= true*/, + bool can_toggle_icons /*= true*/) : mSpeakerMgr(data_source), mAvatarList(avatar_list), - mSortOrder(E_SORT_BY_NAME) -, mParticipantListMenu(NULL) -, mExcludeAgent(exclude_agent) -, mValidateSpeakerCallback(NULL) + mParticipantListMenu(NULL), + mExcludeAgent(exclude_agent), + mValidateSpeakerCallback(NULL) { + mAvalineUpdater = new LLAvalineUpdater(boost::bind(&LLParticipantList::onAvalineCallerFound, this, _1), - boost::bind(&LLParticipantList::onAvalineCallerRemoved, this, _1)); + boost::bind(&LLParticipantList::onAvalineCallerRemoved, this, _1)); mSpeakerAddListener = new SpeakerAddListener(*this); mSpeakerRemoveListener = new SpeakerRemoveListener(*this); @@ -393,15 +396,15 @@ void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param) } /* -Seems this method is not necessary after onAvalineCallerRemoved was implemented; + Seems this method is not necessary after onAvalineCallerRemoved was implemented; -It does nothing because list item is always created with correct class type for Avaline caller. -For now Avaline Caller is removed from the LLSpeakerMgr List when it is removed from the Voice Client -session. -This happens in two cases: if Avaline Caller ends call itself or if Resident ends group call. + It does nothing because list item is always created with correct class type for Avaline caller. + For now Avaline Caller is removed from the LLSpeakerMgr List when it is removed from the Voice Client + session. + This happens in two cases: if Avaline Caller ends call itself or if Resident ends group call. -Probably Avaline caller should be removed from the LLSpeakerMgr list ONLY if it ends call itself. -Asked in EXT-4301. + Probably Avaline caller should be removed from the LLSpeakerMgr list ONLY if it ends call itself. + Asked in EXT-4301. */ void LLParticipantList::onAvalineCallerFound(const LLUUID& participant_id) { @@ -443,16 +446,19 @@ void LLParticipantList::onAvalineCallerRemoved(const LLUUID& participant_id) void LLParticipantList::setSortOrder(EParticipantSortOrder order) { - if ( mSortOrder != order ) + const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder"); + + if ( speaker_sort_order != order ) { - mSortOrder = order; + gSavedSettings.setU32("SpeakerParticipantDefaultOrder", (U32)order); sort(); } } -LLParticipantList::EParticipantSortOrder LLParticipantList::getSortOrder() +const LLParticipantList::EParticipantSortOrder LLParticipantList::getSortOrder() const { - return mSortOrder; + const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder"); + return EParticipantSortOrder(speaker_sort_order); } void LLParticipantList::setValidateSpeakerCallback(validate_speaker_callback_t cb) @@ -551,28 +557,29 @@ void LLParticipantList::sort() if ( !mAvatarList ) return; - switch ( mSortOrder ) { - case E_SORT_BY_NAME : - // if mExcludeAgent == true , then no need to keep agent on top of the list - if(mExcludeAgent) - { - mAvatarList->sortByName(); - } - else - { - mAvatarList->setComparator(&AGENT_ON_TOP_NAME_COMPARATOR); + switch ( getSortOrder() ) + { + case E_SORT_BY_NAME : + // if mExcludeAgent == true , then no need to keep agent on top of the list + if(mExcludeAgent) + { + mAvatarList->sortByName(); + } + else + { + mAvatarList->setComparator(&AGENT_ON_TOP_NAME_COMPARATOR); + mAvatarList->sort(); + } + break; + case E_SORT_BY_RECENT_SPEAKERS: + if (mSortByRecentSpeakers.isNull()) + mSortByRecentSpeakers = new LLAvatarItemRecentSpeakerComparator(*this); + mAvatarList->setComparator(mSortByRecentSpeakers.get()); mAvatarList->sort(); - } - break; - case E_SORT_BY_RECENT_SPEAKERS: - if (mSortByRecentSpeakers.isNull()) - mSortByRecentSpeakers = new LLAvatarItemRecentSpeakerComparator(*this); - mAvatarList->setComparator(mSortByRecentSpeakers.get()); - mAvatarList->sort(); - break; - default : - llwarns << "Unrecognized sort order for " << mAvatarList->getName() << llendl; - return; + break; + default : + llwarns << "Unrecognized sort order for " << mAvatarList->getName() << llendl; + return; } } @@ -645,7 +652,7 @@ bool LLParticipantList::SpeakerClearListener::handleEvent(LLPointer<LLOldEvents: // bool LLParticipantList::SpeakerModeratorUpdateListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) { - return mParent.onModeratorUpdateEvent(event, userdata); + return mParent.onModeratorUpdateEvent(event, userdata); } bool LLParticipantList::SpeakerMuteListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) @@ -867,7 +874,7 @@ void LLParticipantList::LLParticipantListMenu::confirmMuteAllCallback(const LLSD const LLUUID& session_id = payload["session_id"]; LLIMSpeakerMgr * speaker_manager = dynamic_cast<LLIMSpeakerMgr*> ( - LLIMModel::getInstance()->getSpeakerManager(session_id)); + LLIMModel::getInstance()->getSpeakerManager(session_id)); if (speaker_manager) { speaker_manager->moderateVoiceAllParticipants(false); @@ -925,9 +932,9 @@ bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& } /* -Processed menu items with such parameters: - can_allow_text_chat - can_moderate_voice + Processed menu items with such parameters: + can_allow_text_chat + can_moderate_voice */ bool LLParticipantList::LLParticipantListMenu::enableModerateContextMenuItem(const LLSD& userdata) { @@ -978,11 +985,11 @@ bool LLParticipantList::LLParticipantListMenu::checkContextMenuItem(const LLSD& } else if(item == "is_sorted_by_name") { - return E_SORT_BY_NAME == mParent.mSortOrder; + return E_SORT_BY_NAME == mParent.getSortOrder(); } else if(item == "is_sorted_by_recent_speakers") { - return E_SORT_BY_RECENT_SPEAKERS == mParent.mSortOrder; + return E_SORT_BY_RECENT_SPEAKERS == mParent.getSortOrder(); } return false; diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 722a749d19..e0b3d42c25 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -24,6 +24,9 @@ * $/LicenseInfo$ */ +#ifndef LL_PARTICIPANTLIST_H +#define LL_PARTICIPANTLIST_H + #include "llviewerprecompiledheaders.h" #include "llevent.h" #include "llavatarlist.h" // for LLAvatarItemRecentSpeakerComparator @@ -37,239 +40,247 @@ class LLAvalineUpdater; class LLParticipantList { LOG_CLASS(LLParticipantList); +public: + + typedef boost::function<bool (const LLUUID& speaker_id)> validate_speaker_callback_t; + + LLParticipantList(LLSpeakerMgr* data_source, + LLAvatarList* avatar_list, + bool use_context_menu = true, + bool exclude_agent = true, + bool can_toggle_icons = true); + ~LLParticipantList(); + void setSpeakingIndicatorsVisible(BOOL visible); + + enum EParticipantSortOrder + { + E_SORT_BY_NAME = 0, + E_SORT_BY_RECENT_SPEAKERS = 1, + }; + + /** + * Adds specified avatar ID to the existing list if it is not Agent's ID + * + * @param[in] avatar_id - Avatar UUID to be added into the list + */ + void addAvatarIDExceptAgent(const LLUUID& avatar_id); + + /** + * Set and sort Avatarlist by given order + */ + void setSortOrder(EParticipantSortOrder order = E_SORT_BY_NAME); + const EParticipantSortOrder getSortOrder() const; + + /** + * Refreshes the participant list if it's in sort by recent speaker order. + */ + void updateRecentSpeakersOrder(); + + /** + * Set a callback to be called before adding a speaker. Invalid speakers will not be added. + * + * If the callback is unset all speakers are considered as valid. + * + * @see onAddItemEvent() + */ + void setValidateSpeakerCallback(validate_speaker_callback_t cb); + +protected: + /** + * LLSpeakerMgr event handlers + */ + bool onAddItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + bool onRemoveItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + bool onClearListEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + bool onModeratorUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + bool onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + + /** + * Sorts the Avatarlist by stored order + */ + void sort(); + + /** + * List of listeners implementing LLOldEvents::LLSimpleListener. + * There is no way to handle all the events in one listener as LLSpeakerMgr registers + * listeners in such a way that one listener can handle only one type of event + **/ + class BaseSpeakerListener : public LLOldEvents::LLSimpleListener + { public: + BaseSpeakerListener(LLParticipantList& parent) : mParent(parent) {} + protected: + LLParticipantList& mParent; + }; - typedef boost::function<bool (const LLUUID& speaker_id)> validate_speaker_callback_t; - - LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu = true, bool exclude_agent = true, bool can_toggle_icons = true); - ~LLParticipantList(); - void setSpeakingIndicatorsVisible(BOOL visible); - - typedef enum e_participant_sort_oder { - E_SORT_BY_NAME = 0, - E_SORT_BY_RECENT_SPEAKERS = 1, - } EParticipantSortOrder; + class SpeakerAddListener : public BaseSpeakerListener + { + public: + SpeakerAddListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + }; - /** - * Adds specified avatar ID to the existing list if it is not Agent's ID - * - * @param[in] avatar_id - Avatar UUID to be added into the list - */ - void addAvatarIDExceptAgent(const LLUUID& avatar_id); + class SpeakerRemoveListener : public BaseSpeakerListener + { + public: + SpeakerRemoveListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + }; - /** - * Set and sort Avatarlist by given order - */ - void setSortOrder(EParticipantSortOrder order = E_SORT_BY_NAME); - EParticipantSortOrder getSortOrder(); + class SpeakerClearListener : public BaseSpeakerListener + { + public: + SpeakerClearListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + }; - /** - * Refreshes the participant list if it's in sort by recent speaker order. - */ - void updateRecentSpeakersOrder(); + class SpeakerModeratorUpdateListener : public BaseSpeakerListener + { + public: + SpeakerModeratorUpdateListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + }; + + class SpeakerMuteListener : public BaseSpeakerListener + { + public: + SpeakerMuteListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} - /** - * Set a callback to be called before adding a speaker. Invalid speakers will not be added. - * - * If the callback is unset all speakers are considered as valid. - * - * @see onAddItemEvent() - */ - void setValidateSpeakerCallback(validate_speaker_callback_t cb); + /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + }; + /** + * Menu used in the participant list. + */ + class LLParticipantListMenu : public LLListContextMenu + { + public: + LLParticipantListMenu(LLParticipantList& parent):mParent(parent){}; + /*virtual*/ LLContextMenu* createMenu(); + /*virtual*/ void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y); protected: + LLParticipantList& mParent; + private: + bool enableContextMenuItem(const LLSD& userdata); + bool enableModerateContextMenuItem(const LLSD& userdata); + bool checkContextMenuItem(const LLSD& userdata); + + void sortParticipantList(const LLSD& userdata); + void toggleAllowTextChat(const LLSD& userdata); + void toggleMute(const LLSD& userdata, U32 flags); + void toggleMuteText(const LLSD& userdata); + void toggleMuteVoice(const LLSD& userdata); + /** - * LLSpeakerMgr event handlers + * Return true if Agent is group moderator(and moderator of group call). */ - bool onAddItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); - bool onRemoveItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); - bool onClearListEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); - bool onModeratorUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); - bool onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + bool isGroupModerator(); + // Voice moderation support /** - * Sorts the Avatarlist by stored order + * Check whether specified by argument avatar is muted for group chat or not. */ - void sort(); - - //List of listeners implementing LLOldEvents::LLSimpleListener. - //There is no way to handle all the events in one listener as LLSpeakerMgr registers listeners in such a way - //that one listener can handle only one type of event - class BaseSpeakerListner : public LLOldEvents::LLSimpleListener - { - public: - BaseSpeakerListner(LLParticipantList& parent) : mParent(parent) {} - protected: - LLParticipantList& mParent; - }; - - class SpeakerAddListener : public BaseSpeakerListner - { - public: - SpeakerAddListener(LLParticipantList& parent) : BaseSpeakerListner(parent) {} - /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); - }; - - class SpeakerRemoveListener : public BaseSpeakerListner - { - public: - SpeakerRemoveListener(LLParticipantList& parent) : BaseSpeakerListner(parent) {} - /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); - }; - - class SpeakerClearListener : public BaseSpeakerListner - { - public: - SpeakerClearListener(LLParticipantList& parent) : BaseSpeakerListner(parent) {} - /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); - }; - - class SpeakerModeratorUpdateListener : public BaseSpeakerListner - { - public: - SpeakerModeratorUpdateListener(LLParticipantList& parent) : BaseSpeakerListner(parent) {} - /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); - }; - - class SpeakerMuteListener : public BaseSpeakerListner - { - public: - SpeakerMuteListener(LLParticipantList& parent) : BaseSpeakerListner(parent) {} - - /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); - }; + bool isMuted(const LLUUID& avatar_id); /** - * Menu used in the participant list. + * Processes Voice moderation menu items. + * + * It calls either moderateVoiceParticipant() or moderateVoiceParticipant() depend on + * passed parameter. + * + * @param userdata can be "selected" or "others". + * + * @see moderateVoiceParticipant() + * @see moderateVoiceAllParticipants() */ - class LLParticipantListMenu : public LLListContextMenu - { - public: - LLParticipantListMenu(LLParticipantList& parent):mParent(parent){}; - /*virtual*/ LLContextMenu* createMenu(); - /*virtual*/ void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y); - protected: - LLParticipantList& mParent; - private: - bool enableContextMenuItem(const LLSD& userdata); - bool enableModerateContextMenuItem(const LLSD& userdata); - bool checkContextMenuItem(const LLSD& userdata); - - void sortParticipantList(const LLSD& userdata); - void toggleAllowTextChat(const LLSD& userdata); - void toggleMute(const LLSD& userdata, U32 flags); - void toggleMuteText(const LLSD& userdata); - void toggleMuteVoice(const LLSD& userdata); - - /** - * Return true if Agent is group moderator(and moderator of group call). - */ - bool isGroupModerator(); - - // Voice moderation support - /** - * Check whether specified by argument avatar is muted for group chat or not. - */ - bool isMuted(const LLUUID& avatar_id); - - /** - * Processes Voice moderation menu items. - * - * It calls either moderateVoiceParticipant() or moderateVoiceParticipant() depend on - * passed parameter. - * - * @param userdata can be "selected" or "others". - * - * @see moderateVoiceParticipant() - * @see moderateVoiceAllParticipants() - */ - void moderateVoice(const LLSD& userdata); - - /** - * Mutes/Unmutes avatar for current group voice chat. - * - * It only marks avatar as muted for session and does not use local Agent's Block list. - * It does not mute Agent itself. - * - * @param[in] avatar_id UUID of avatar to be processed - * @param[in] unmute if true - specified avatar will be muted, otherwise - unmuted. - * - * @see moderateVoiceAllParticipants() - */ - void moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute); - - /** - * Mutes/Unmutes all avatars for current group voice chat. - * - * It only marks avatars as muted for session and does not use local Agent's Block list. - * - * @param[in] unmute if true - avatars will be muted, otherwise - unmuted. - * - * @see moderateVoiceParticipant() - */ - void moderateVoiceAllParticipants(bool unmute); - - static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response); - }; + void moderateVoice(const LLSD& userdata); /** - * Comparator for comparing avatar items by last spoken time + * Mutes/Unmutes avatar for current group voice chat. + * + * It only marks avatar as muted for session and does not use local Agent's Block list. + * It does not mute Agent itself. + * + * @param[in] avatar_id UUID of avatar to be processed + * @param[in] unmute if true - specified avatar will be muted, otherwise - unmuted. + * + * @see moderateVoiceAllParticipants() */ - class LLAvatarItemRecentSpeakerComparator : public LLAvatarItemNameComparator, public LLRefCount - { - LOG_CLASS(LLAvatarItemRecentSpeakerComparator); - public: - LLAvatarItemRecentSpeakerComparator(LLParticipantList& parent):mParent(parent){}; - virtual ~LLAvatarItemRecentSpeakerComparator() {}; - protected: - virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const; - private: - LLParticipantList& mParent; - }; - - private: - void onAvatarListDoubleClicked(LLUICtrl* ctrl); - void onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param); - - void onAvalineCallerFound(const LLUUID& participant_id); - void onAvalineCallerRemoved(const LLUUID& participant_id); + void moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute); /** - * Adjusts passed participant to work properly. + * Mutes/Unmutes all avatars for current group voice chat. * - * Adds SpeakerMuteListener to process moderation actions. - */ - void adjustParticipant(const LLUUID& speaker_id); - - LLSpeakerMgr* mSpeakerMgr; - LLAvatarList* mAvatarList; - - std::set<LLUUID> mModeratorList; - std::set<LLUUID> mModeratorToRemoveList; - - LLPointer<SpeakerAddListener> mSpeakerAddListener; - LLPointer<SpeakerRemoveListener> mSpeakerRemoveListener; - LLPointer<SpeakerClearListener> mSpeakerClearListener; - LLPointer<SpeakerModeratorUpdateListener> mSpeakerModeratorListener; - LLPointer<SpeakerMuteListener> mSpeakerMuteListener; - - LLParticipantListMenu* mParticipantListMenu; - - EParticipantSortOrder mSortOrder; - /* - * This field manages an adding a new avatar_id in the mAvatarList - * If true, then agent_id wont be added into mAvatarList - * Also by default this field is controlling a sort procedure, @c sort() + * It only marks avatars as muted for session and does not use local Agent's Block list. + * + * @param[in] unmute if true - avatars will be muted, otherwise - unmuted. + * + * @see moderateVoiceParticipant() */ - bool mExcludeAgent; + void moderateVoiceAllParticipants(bool unmute); - // boost::connections - boost::signals2::connection mAvatarListDoubleClickConnection; - boost::signals2::connection mAvatarListRefreshConnection; - boost::signals2::connection mAvatarListReturnConnection; - boost::signals2::connection mAvatarListToggleIconsConnection; + static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response); + }; - LLPointer<LLAvatarItemRecentSpeakerComparator> mSortByRecentSpeakers; - validate_speaker_callback_t mValidateSpeakerCallback; - LLAvalineUpdater* mAvalineUpdater; + /** + * Comparator for comparing avatar items by last spoken time + */ + class LLAvatarItemRecentSpeakerComparator : public LLAvatarItemNameComparator, public LLRefCount + { + LOG_CLASS(LLAvatarItemRecentSpeakerComparator); + public: + LLAvatarItemRecentSpeakerComparator(LLParticipantList& parent):mParent(parent){}; + virtual ~LLAvatarItemRecentSpeakerComparator() {}; + protected: + virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const; + private: + LLParticipantList& mParent; + }; + +private: + void onAvatarListDoubleClicked(LLUICtrl* ctrl); + void onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param); + + void onAvalineCallerFound(const LLUUID& participant_id); + void onAvalineCallerRemoved(const LLUUID& participant_id); + + /** + * Adjusts passed participant to work properly. + * + * Adds SpeakerMuteListener to process moderation actions. + */ + void adjustParticipant(const LLUUID& speaker_id); + + LLSpeakerMgr* mSpeakerMgr; + LLAvatarList* mAvatarList; + + std::set<LLUUID> mModeratorList; + std::set<LLUUID> mModeratorToRemoveList; + + LLPointer<SpeakerAddListener> mSpeakerAddListener; + LLPointer<SpeakerRemoveListener> mSpeakerRemoveListener; + LLPointer<SpeakerClearListener> mSpeakerClearListener; + LLPointer<SpeakerModeratorUpdateListener> mSpeakerModeratorListener; + LLPointer<SpeakerMuteListener> mSpeakerMuteListener; + + LLParticipantListMenu* mParticipantListMenu; + + /** + * This field manages an adding a new avatar_id in the mAvatarList + * If true, then agent_id wont be added into mAvatarList + * Also by default this field is controlling a sort procedure, @c sort() + */ + bool mExcludeAgent; + + // boost::connections + boost::signals2::connection mAvatarListDoubleClickConnection; + boost::signals2::connection mAvatarListRefreshConnection; + boost::signals2::connection mAvatarListReturnConnection; + boost::signals2::connection mAvatarListToggleIconsConnection; + + LLPointer<LLAvatarItemRecentSpeakerComparator> mSortByRecentSpeakers; + validate_speaker_callback_t mValidateSpeakerCallback; + LLAvalineUpdater* mAvalineUpdater; }; + +#endif // LL_PARTICIPANTLIST_H diff --git a/indra/newview/llplacesinventorypanel.cpp b/indra/newview/llplacesinventorypanel.cpp index 408270a1a0..29e262199e 100644 --- a/indra/newview/llplacesinventorypanel.cpp +++ b/indra/newview/llplacesinventorypanel.cpp @@ -205,24 +205,6 @@ BOOL LLPlacesFolderView::handleRightMouseDown(S32 x, S32 y, MASK mask) return LLFolderView::handleRightMouseDown(x, y, mask); } -BOOL LLPlacesFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - // Don't accept anything except landmarks and folders to be dropped - // in places folder view. See STORM-296. - if (cargo_type != DAD_LANDMARK && cargo_type != DAD_CATEGORY) - { - *accept = ACCEPT_NO; - return FALSE; - } - - return LLFolderView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, - accept, tooltip_msg); -} - void LLPlacesFolderView::setupMenuHandle(LLInventoryType::EType asset_type, LLHandle<LLView> menu_handle) { mMenuHandlesByInventoryType[asset_type] = menu_handle; diff --git a/indra/newview/llplacesinventorypanel.h b/indra/newview/llplacesinventorypanel.h index a44776d18b..6641871a0b 100644 --- a/indra/newview/llplacesinventorypanel.h +++ b/indra/newview/llplacesinventorypanel.h @@ -70,12 +70,6 @@ public: */ /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - void setupMenuHandle(LLInventoryType::EType asset_type, LLHandle<LLView> menu_handle); void setParentLandmarksPanel(LLLandmarksPanel* panel) diff --git a/indra/newview/llpopupview.cpp b/indra/newview/llpopupview.cpp index 499b6a8f5f..9fbb67a63a 100644 --- a/indra/newview/llpopupview.cpp +++ b/indra/newview/llpopupview.cpp @@ -40,7 +40,8 @@ bool view_visible(LLView* viewp) } -LLPopupView::LLPopupView() +LLPopupView::LLPopupView(const LLPopupView::Params& p) +: LLPanel(p) { // register ourself as handler of UI popups LLUI::setPopupFuncs(boost::bind(&LLPopupView::addPopup, this, _1), boost::bind(&LLPopupView::removePopup, this, _1), boost::bind(&LLPopupView::clearPopups, this)); @@ -137,64 +138,102 @@ BOOL LLPopupView::handleMouseEvent(boost::function<BOOL(LLView*, S32, S32)> func BOOL LLPopupView::handleMouseDown(S32 x, S32 y, MASK mask) { - if (!handleMouseEvent(boost::bind(&LLMouseHandler::handleMouseDown, _1, _2, _3, mask), view_visible_and_enabled, x, y, true)) + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleMouseDown, _1, _2, _3, mask), view_visible_and_enabled, x, y, true); + if (!handled) { - return FALSE; + handled = LLPanel::handleMouseDown(x, y, mask); } - return TRUE; + return handled; } BOOL LLPopupView::handleMouseUp(S32 x, S32 y, MASK mask) { - return handleMouseEvent(boost::bind(&LLMouseHandler::handleMouseUp, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleMouseUp, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + if (!handled) + { + handled = LLPanel::handleMouseUp(x, y, mask); + } + return handled; } BOOL LLPopupView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { - if (!handleMouseEvent(boost::bind(&LLMouseHandler::handleMiddleMouseDown, _1, _2, _3, mask), view_visible_and_enabled, x, y, true)) + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleMiddleMouseDown, _1, _2, _3, mask), view_visible_and_enabled, x, y, true); + if (!handled) { - return FALSE; + handled = LLPanel::handleMiddleMouseDown(x, y, mask); } - return TRUE; + return handled; } BOOL LLPopupView::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { - return handleMouseEvent(boost::bind(&LLMouseHandler::handleMiddleMouseUp, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleMiddleMouseUp, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + if (!handled) + { + handled = LLPanel::handleMiddleMouseUp(x, y, mask); + } + return handled; } BOOL LLPopupView::handleRightMouseDown(S32 x, S32 y, MASK mask) { - if (!handleMouseEvent(boost::bind(&LLMouseHandler::handleRightMouseDown, _1, _2, _3, mask), view_visible_and_enabled, x, y, true)) + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleRightMouseDown, _1, _2, _3, mask), view_visible_and_enabled, x, y, true); + if (!handled) { - return FALSE; + handled = LLPanel::handleRightMouseDown(x, y, mask); } - return TRUE; + return handled; } BOOL LLPopupView::handleRightMouseUp(S32 x, S32 y, MASK mask) { - return handleMouseEvent(boost::bind(&LLMouseHandler::handleRightMouseUp, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleRightMouseUp, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + if (!handled) + { + handled = LLPanel::handleRightMouseUp(x, y, mask); + } + return handled; } BOOL LLPopupView::handleDoubleClick(S32 x, S32 y, MASK mask) { - return handleMouseEvent(boost::bind(&LLMouseHandler::handleDoubleClick, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleDoubleClick, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + if (!handled) + { + handled = LLPanel::handleDoubleClick(x, y, mask); + } + return handled; } BOOL LLPopupView::handleHover(S32 x, S32 y, MASK mask) { - return handleMouseEvent(boost::bind(&LLMouseHandler::handleHover, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleHover, _1, _2, _3, mask), view_visible_and_enabled, x, y, false); + if (!handled) + { + handled = LLPanel::handleHover(x, y, mask); + } + return handled; } BOOL LLPopupView::handleScrollWheel(S32 x, S32 y, S32 clicks) { - return handleMouseEvent(boost::bind(&LLMouseHandler::handleScrollWheel, _1, _2, _3, clicks), view_visible_and_enabled, x, y, false); + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleScrollWheel, _1, _2, _3, clicks), view_visible_and_enabled, x, y, false); + if (!handled) + { + handled = LLPanel::handleScrollWheel(x, y, clicks); + } + return handled; } BOOL LLPopupView::handleToolTip(S32 x, S32 y, MASK mask) { - return handleMouseEvent(boost::bind(&LLMouseHandler::handleToolTip, _1, _2, _3, mask), view_visible, x, y, false); + BOOL handled = handleMouseEvent(boost::bind(&LLMouseHandler::handleToolTip, _1, _2, _3, mask), view_visible, x, y, false); + if (!handled) + { + handled = LLPanel::handleToolTip(x, y, mask); + } + return handled; } void LLPopupView::addPopup(LLView* popup) diff --git a/indra/newview/llpopupview.h b/indra/newview/llpopupview.h index fec4afd79c..b378f61984 100644 --- a/indra/newview/llpopupview.h +++ b/indra/newview/llpopupview.h @@ -32,7 +32,7 @@ class LLPopupView : public LLPanel { public: - LLPopupView(); + LLPopupView(const Params& p = LLPanel::Params()); ~LLPopupView(); /*virtual*/ void draw(); diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index 69542764d2..a90f23d637 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -454,12 +454,13 @@ LLMultiPreview::LLMultiPreview() { // start with a rect in the top-left corner ; will get resized LLRect rect; - rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 200, 200); + rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 200, 400); setRect(rect); } setTitle(LLTrans::getString("MultiPreviewTitle")); buildTabContainer(); setCanResize(TRUE); + mAutoResize = FALSE; } void LLMultiPreview::onOpen(const LLSD& key) diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index cf2ea38288..22ff362b5a 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -34,11 +34,13 @@ #include "llcheckboxctrl.h" #include "llcombobox.h" #include "lldir.h" +#include "llexternaleditor.h" #include "llfloaterreg.h" #include "llinventorydefines.h" #include "llinventorymodel.h" #include "llkeyboard.h" #include "lllineeditor.h" +#include "lllivefile.h" #include "llhelp.h" #include "llnotificationsutil.h" #include "llresmgr.h" @@ -116,6 +118,50 @@ static bool have_script_upload_cap(LLUUID& object_id) } /// --------------------------------------------------------------------------- +/// LLLiveLSLFile +/// --------------------------------------------------------------------------- +class LLLiveLSLFile : public LLLiveFile +{ +public: + typedef boost::function<bool (const std::string& filename)> change_callback_t; + + LLLiveLSLFile(std::string file_path, change_callback_t change_cb); + ~LLLiveLSLFile(); + + void ignoreNextUpdate() { mIgnoreNextUpdate = true; } + +protected: + /*virtual*/ bool loadFile(); + + change_callback_t mOnChangeCallback; + bool mIgnoreNextUpdate; +}; + +LLLiveLSLFile::LLLiveLSLFile(std::string file_path, change_callback_t change_cb) +: mOnChangeCallback(change_cb) +, mIgnoreNextUpdate(false) +, LLLiveFile(file_path, 1.0) +{ + llassert(mOnChangeCallback); +} + +LLLiveLSLFile::~LLLiveLSLFile() +{ + LLFile::remove(filename()); +} + +bool LLLiveLSLFile::loadFile() +{ + if (mIgnoreNextUpdate) + { + mIgnoreNextUpdate = false; + return true; + } + + return mOnChangeCallback(filename()); +} + +/// --------------------------------------------------------------------------- /// LLFloaterScriptSearch /// --------------------------------------------------------------------------- class LLFloaterScriptSearch : public LLFloater @@ -277,6 +323,7 @@ struct LLSECKeywordCompare }; LLScriptEdCore::LLScriptEdCore( + LLScriptEdContainer* container, const std::string& sample, const LLHandle<LLFloater>& floater_handle, void (*load_callback)(void*), @@ -296,12 +343,15 @@ LLScriptEdCore::LLScriptEdCore( mLastHelpToken(NULL), mLiveHelpHistorySize(0), mEnableSave(FALSE), + mLiveFile(NULL), + mContainer(container), mHasScriptData(FALSE) { setFollowsAll(); setBorderVisible(FALSE); setXMLFilename("panel_script_ed.xml"); + llassert_always(mContainer != NULL); } LLScriptEdCore::~LLScriptEdCore() @@ -315,6 +365,8 @@ LLScriptEdCore::~LLScriptEdCore() script_search->closeFloater(); delete script_search; } + + delete mLiveFile; } BOOL LLScriptEdCore::postBuild() @@ -329,6 +381,7 @@ BOOL LLScriptEdCore::postBuild() childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this); childSetAction("Save_btn", boost::bind(&LLScriptEdCore::doSave,this,FALSE)); + childSetAction("Edit_btn", boost::bind(&LLScriptEdCore::openInExternalEditor, this)); initMenu(); @@ -461,6 +514,79 @@ void LLScriptEdCore::setScriptText(const std::string& text, BOOL is_valid) } } +bool LLScriptEdCore::loadScriptText(const std::string& filename) +{ + if (filename.empty()) + { + llwarns << "Empty file name" << llendl; + return false; + } + + LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ + if (!file) + { + llwarns << "Error opening " << filename << llendl; + return false; + } + + // read in the whole file + fseek(file, 0L, SEEK_END); + size_t file_length = (size_t) ftell(file); + fseek(file, 0L, SEEK_SET); + char* buffer = new char[file_length+1]; + size_t nread = fread(buffer, 1, file_length, file); + if (nread < file_length) + { + llwarns << "Short read" << llendl; + } + buffer[nread] = '\0'; + fclose(file); + + mEditor->setText(LLStringExplicit(buffer)); + delete[] buffer; + + return true; +} + +bool LLScriptEdCore::writeToFile(const std::string& filename) +{ + LLFILE* fp = LLFile::fopen(filename, "wb"); + if (!fp) + { + llwarns << "Unable to write to " << filename << llendl; + + LLSD row; + row["columns"][0]["value"] = "Error writing to local file. Is your hard drive full?"; + row["columns"][0]["font"] = "SANSSERIF_SMALL"; + mErrorList->addElement(row); + return false; + } + + std::string utf8text = mEditor->getText(); + + // Special case for a completely empty script - stuff in one space so it can store properly. See SL-46889 + if (utf8text.size() == 0) + { + utf8text = " "; + } + + fputs(utf8text.c_str(), fp); + fclose(fp); + return true; +} + +void LLScriptEdCore::sync() +{ + // Sync with external editor. + std::string tmp_file = mContainer->getTmpFileName(); + llstat s; + if (LLFile::stat(tmp_file, &s) == 0) // file exists + { + if (mLiveFile) mLiveFile->ignoreNextUpdate(); + writeToFile(tmp_file); + } +} + bool LLScriptEdCore::hasChanged() { if (!mEditor) return false; @@ -637,6 +763,12 @@ BOOL LLScriptEdCore::canClose() } } +void LLScriptEdCore::setEnableEditing(bool enable) +{ + mEditor->setEnabled(enable); + getChildView("Edit_btn")->setEnabled(enable); +} + bool LLScriptEdCore::handleSaveChangesDialog(const LLSD& notification, const LLSD& response ) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -809,6 +941,33 @@ void LLScriptEdCore::doSave( BOOL close_after_save ) } } +void LLScriptEdCore::openInExternalEditor() +{ + delete mLiveFile; // deletes file + + // Save the script to a temporary file. + std::string filename = mContainer->getTmpFileName(); + writeToFile(filename); + + // Start watching file changes. + mLiveFile = new LLLiveLSLFile(filename, boost::bind(&LLScriptEdContainer::onExternalChange, mContainer, _1)); + mLiveFile->addToEventTimer(); + + // Open it in external editor. + { + LLExternalEditor ed; + + if (!ed.setCommand("LL_SCRIPT_EDITOR")) + { + std::string msg = "Select an editor by setting the environment variable LL_SCRIPT_EDITOR " + "or the ExternalEditor setting"; // *TODO: localize + LLNotificationsUtil::add("GenericAlert", LLSD().with("MESSAGE", msg)); + return; + } + + ed.run(filename); + } +} void LLScriptEdCore::onBtnUndoChanges() { @@ -923,6 +1082,43 @@ BOOL LLScriptEdCore::handleKeyHere(KEY key, MASK mask) } /// --------------------------------------------------------------------------- +/// LLScriptEdContainer +/// --------------------------------------------------------------------------- + +LLScriptEdContainer::LLScriptEdContainer(const LLSD& key) +: LLPreview(key) +, mScriptEd(NULL) +{ +} + +std::string LLScriptEdContainer::getTmpFileName() +{ + // Take script inventory item id (within the object inventory) + // to consideration so that it's possible to edit multiple scripts + // in the same object inventory simultaneously (STORM-781). + std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString(); + + // Use MD5 sum to make the file name shorter and not exceed maximum path length. + char script_id_hash_str[33]; /* Flawfinder: ignore */ + LLMD5 script_id_hash((const U8 *)script_id.c_str()); + script_id_hash.hex_digest(script_id_hash_str); + + return std::string(LLFile::tmpdir()) + "sl_script_" + script_id_hash_str + ".lsl"; +} + +bool LLScriptEdContainer::onExternalChange(const std::string& filename) +{ + if (!mScriptEd->loadScriptText(filename)) + { + return false; + } + + // Disable sync to avoid recursive load->save->load calls. + saveIfNeeded(false); + return true; +} + +/// --------------------------------------------------------------------------- /// LLPreviewLSL /// --------------------------------------------------------------------------- @@ -945,6 +1141,7 @@ void* LLPreviewLSL::createScriptEdPanel(void* userdata) LLPreviewLSL *self = (LLPreviewLSL*)userdata; self->mScriptEd = new LLScriptEdCore( + self, HELLO_LSL, self->getHandle(), LLPreviewLSL::onLoad, @@ -958,7 +1155,7 @@ void* LLPreviewLSL::createScriptEdPanel(void* userdata) LLPreviewLSL::LLPreviewLSL(const LLSD& key ) - : LLPreview( key ), +: LLScriptEdContainer(key), mPendingUploads(0) { mFactoryMap["script panel"] = LLCallbackMap(LLPreviewLSL::createScriptEdPanel, this); @@ -1049,7 +1246,6 @@ void LLPreviewLSL::loadAsset() { mScriptEd->setScriptText(mScriptEd->getString("can_not_view"), FALSE); mScriptEd->mEditor->makePristine(); - mScriptEd->mEditor->setEnabled(FALSE); mScriptEd->mFunctions->setEnabled(FALSE); mAssetStatus = PREVIEW_ASSET_LOADED; } @@ -1059,6 +1255,7 @@ void LLPreviewLSL::loadAsset() else { mScriptEd->setScriptText(std::string(HELLO_LSL), TRUE); + mScriptEd->setEnableEditing(TRUE); mAssetStatus = PREVIEW_ASSET_LOADED; } } @@ -1105,7 +1302,7 @@ void LLPreviewLSL::onSave(void* userdata, BOOL close_after_save) // Save needs to compile the text in the buffer. If the compile // succeeds, then save both assets out to the database. If the compile // fails, go ahead and save the text anyway. -void LLPreviewLSL::saveIfNeeded() +void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) { // llinfos << "LLPreviewLSL::saveIfNeeded()" << llendl; if(!mScriptEd->hasChanged()) @@ -1124,23 +1321,13 @@ void LLPreviewLSL::saveIfNeeded() std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,asset_id.asString()); std::string filename = filepath + ".lsl"; - LLFILE* fp = LLFile::fopen(filename, "wb"); - if(!fp) - { - llwarns << "Unable to write to " << filename << llendl; + mScriptEd->writeToFile(filename); - LLSD row; - row["columns"][0]["value"] = "Error writing to local file. Is your hard drive full?"; - row["columns"][0]["font"] = "SANSSERIF_SMALL"; - mScriptEd->mErrorList->addElement(row); - return; + if (sync) + { + mScriptEd->sync(); } - std::string utf8text = mScriptEd->mEditor->getText(); - fputs(utf8text.c_str(), fp); - fclose(fp); - fp = NULL; - const LLInventoryItem *inv_item = getItem(); // save it out to asset server std::string url = gAgent.getRegion()->getCapability("UpdateScriptAgent"); @@ -1372,7 +1559,7 @@ void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAsset { is_modifiable = TRUE; } - preview->mScriptEd->mEditor->setEnabled(is_modifiable); + preview->mScriptEd->setEnableEditing(is_modifiable); preview->mAssetStatus = PREVIEW_ASSET_LOADED; } else @@ -1413,6 +1600,7 @@ void* LLLiveLSLEditor::createScriptEdPanel(void* userdata) LLLiveLSLEditor *self = (LLLiveLSLEditor*)userdata; self->mScriptEd = new LLScriptEdCore( + self, HELLO_LSL, self->getHandle(), &LLLiveLSLEditor::onLoad, @@ -1426,8 +1614,7 @@ void* LLLiveLSLEditor::createScriptEdPanel(void* userdata) LLLiveLSLEditor::LLLiveLSLEditor(const LLSD& key) : - LLPreview(key), - mScriptEd(NULL), + LLScriptEdContainer(key), mAskedForRunningInfo(FALSE), mHaveRunningInfo(FALSE), mCloseAfterSave(FALSE), @@ -1456,10 +1643,6 @@ BOOL LLLiveLSLEditor::postBuild() return LLPreview::postBuild(); } -LLLiveLSLEditor::~LLLiveLSLEditor() -{ -} - // virtual void LLLiveLSLEditor::callbackLSLCompileSucceeded(const LLUUID& task_id, const LLUUID& item_id, @@ -1516,7 +1699,6 @@ void LLLiveLSLEditor::loadAsset() mItem = new LLViewerInventoryItem(item); mScriptEd->setScriptText(getString("not_allowed"), FALSE); mScriptEd->mEditor->makePristine(); - mScriptEd->mEditor->setEnabled(FALSE); mScriptEd->enableSave(FALSE); mAssetStatus = PREVIEW_ASSET_LOADED; } @@ -1554,10 +1736,6 @@ void LLLiveLSLEditor::loadAsset() mIsModifiable = item && gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE); - if(!mIsModifiable) - { - mScriptEd->mEditor->setEnabled(FALSE); - } // This is commented out, because we don't completely // handle script exports yet. @@ -1613,6 +1791,7 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, if( LL_ERR_NOERR == status ) { instance->loadScriptText(vfs, asset_id, type); + instance->mScriptEd->setEnableEditing(TRUE); instance->mAssetStatus = PREVIEW_ASSET_LOADED; } else @@ -1639,39 +1818,6 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, delete xored_id; } -// unused -// void LLLiveLSLEditor::loadScriptText(const std::string& filename) -// { -// if(!filename) -// { -// llerrs << "Filename is Empty!" << llendl; -// return; -// } -// LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ -// if(file) -// { -// // read in the whole file -// fseek(file, 0L, SEEK_END); -// long file_length = ftell(file); -// fseek(file, 0L, SEEK_SET); -// char* buffer = new char[file_length+1]; -// size_t nread = fread(buffer, 1, file_length, file); -// if (nread < (size_t) file_length) -// { -// llwarns << "Short read" << llendl; -// } -// buffer[nread] = '\0'; -// fclose(file); -// mScriptEd->mEditor->setText(LLStringExplicit(buffer)); -// mScriptEd->mEditor->makePristine(); -// delete[] buffer; -// } -// else -// { -// llwarns << "Error opening " << filename << llendl; -// } -// } - void LLLiveLSLEditor::loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type) { LLVFile file(vfs, uuid, type); @@ -1825,9 +1971,9 @@ LLLiveLSLSaveData::LLLiveLSLSaveData(const LLUUID& id, mItem = new LLViewerInventoryItem(item); } -void LLLiveLSLEditor::saveIfNeeded() +// virtual +void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) { - llinfos << "LLLiveLSLEditor::saveIfNeeded()" << llendl; LLViewerObject* object = gObjectList.findObject(mObjectUUID); if(!object) { @@ -1877,29 +2023,12 @@ void LLLiveLSLEditor::saveIfNeeded() mItem->setAssetUUID(asset_id); mItem->setTransactionID(tid); - // write out the data, and store it in the asset database - LLFILE* fp = LLFile::fopen(filename, "wb"); - if(!fp) - { - llwarns << "Unable to write to " << filename << llendl; - - LLSD row; - row["columns"][0]["value"] = "Error writing to local file. Is your hard drive full?"; - row["columns"][0]["font"] = "SANSSERIF_SMALL"; - mScriptEd->mErrorList->addElement(row); - return; - } - std::string utf8text = mScriptEd->mEditor->getText(); + mScriptEd->writeToFile(filename); - // Special case for a completely empty script - stuff in one space so it can store properly. See SL-46889 - if ( utf8text.size() == 0 ) + if (sync) { - utf8text = " "; + mScriptEd->sync(); } - - fputs(utf8text.c_str(), fp); - fclose(fp); - fp = NULL; // save it out to asset server std::string url = object->getRegion()->getCapability("UpdateScriptTask"); @@ -2138,6 +2267,7 @@ void LLLiveLSLEditor::onSave(void* userdata, BOOL close_after_save) self->saveIfNeeded(); } + // static void LLLiveLSLEditor::processScriptRunningReply(LLMessageSystem* msg, void**) { diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index f4b31e5962..f86be615c4 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -35,6 +35,7 @@ #include "lliconctrl.h" #include "llframetimer.h" +class LLLiveLSLFile; class LLMessageSystem; class LLTextEditor; class LLButton; @@ -47,6 +48,7 @@ class LLFloaterScriptSearch; class LLKeywordToken; class LLVFS; class LLViewerInventoryItem; +class LLScriptEdContainer; // Inner, implementation class. LLPreviewScript and LLLiveLSLEditor each own one of these. class LLScriptEdCore : public LLPanel @@ -55,9 +57,12 @@ class LLScriptEdCore : public LLPanel friend class LLPreviewLSL; friend class LLLiveLSLEditor; friend class LLFloaterScriptSearch; + friend class LLScriptEdContainer; -public: +protected: + // Supposed to be invoked only by the container. LLScriptEdCore( + LLScriptEdContainer* container, const std::string& sample, const LLHandle<LLFloater>& floater_handle, void (*load_callback)(void* userdata), @@ -65,6 +70,7 @@ public: void (*search_replace_callback)(void* userdata), void* userdata, S32 bottom_pad = 0); // pad below bottom row of buttons +public: ~LLScriptEdCore(); void initMenu(); @@ -72,14 +78,20 @@ public: virtual void draw(); /*virtual*/ BOOL postBuild(); BOOL canClose(); + void setEnableEditing(bool enable); void setScriptText(const std::string& text, BOOL is_valid); + bool loadScriptText(const std::string& filename); + bool writeToFile(const std::string& filename); + void sync(); void doSave( BOOL close_after_save ); bool handleSaveChangesDialog(const LLSD& notification, const LLSD& response); bool handleReloadFromServerDialog(const LLSD& notification, const LLSD& response); + void openInExternalEditor(); + static void onCheckLock(LLUICtrl*, void*); static void onHelpComboCommit(LLUICtrl* ctrl, void* userdata); static void onClickBack(void* userdata); @@ -127,11 +139,28 @@ private: S32 mLiveHelpHistorySize; BOOL mEnableSave; BOOL mHasScriptData; + LLLiveLSLFile* mLiveFile; + + LLScriptEdContainer* mContainer; // parent view }; +class LLScriptEdContainer : public LLPreview +{ + friend class LLScriptEdCore; + +public: + LLScriptEdContainer(const LLSD& key); + +protected: + std::string getTmpFileName(); + bool onExternalChange(const std::string& filename); + virtual void saveIfNeeded(bool sync = true) = 0; + + LLScriptEdCore* mScriptEd; +}; // Used to view and edit a LSL from your inventory. -class LLPreviewLSL : public LLPreview +class LLPreviewLSL : public LLScriptEdContainer { public: LLPreviewLSL(const LLSD& key ); @@ -145,7 +174,7 @@ protected: void closeIfNeeded(); virtual void loadAsset(); - void saveIfNeeded(); + /*virtual*/ void saveIfNeeded(bool sync = true); void uploadAssetViaCaps(const std::string& url, const std::string& filename, const LLUUID& item_id); @@ -169,7 +198,6 @@ protected: protected: - LLScriptEdCore* mScriptEd; // Can safely close only after both text and bytecode are uploaded S32 mPendingUploads; @@ -177,11 +205,11 @@ protected: // Used to view and edit an LSL that is attached to an object. -class LLLiveLSLEditor : public LLPreview +class LLLiveLSLEditor : public LLScriptEdContainer { + friend class LLLiveLSLFile; public: LLLiveLSLEditor(const LLSD& key); - ~LLLiveLSLEditor(); static void processScriptRunningReply(LLMessageSystem* msg, void**); @@ -202,7 +230,7 @@ private: virtual void loadAsset(); void loadAsset(BOOL is_new); - void saveIfNeeded(); + /*virtual*/ void saveIfNeeded(bool sync = true); void uploadAssetViaCaps(const std::string& url, const std::string& filename, const LLUUID& task_id, @@ -227,7 +255,6 @@ private: static void onRunningCheckboxClicked(LLUICtrl*, void* userdata); static void onReset(void* userdata); -// void loadScriptText(const std::string& filename); // unused void loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type); static void onErrorList(LLUICtrl*, void* user_data); @@ -238,7 +265,6 @@ private: private: bool mIsNew; - LLScriptEdCore* mScriptEd; //LLUUID mTransmitID; LLCheckBoxCtrl* mRunningCheckbox; BOOL mAskedForRunningInfo; diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index fd6b326ef1..7657cccd4e 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -318,7 +318,7 @@ void LLPreviewTexture::reshape(S32 width, S32 height, BOOL called_from_parent) } } - mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() + (client_height / 2), client_width, client_height); + mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() + (client_height / 2), client_width, client_height); } @@ -400,7 +400,6 @@ void LLPreviewTexture::updateDimensions() { return; } - mUpdateDimensions = FALSE; @@ -408,80 +407,12 @@ void LLPreviewTexture::updateDimensions() getChild<LLUICtrl>("dimensions")->setTextArg("[HEIGHT]", llformat("%d", mImage->getFullHeight())); - LLRect dim_rect(getChildView("dimensions")->getRect()); - - S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) + PREVIEW_RESIZE_HANDLE_SIZE; - - // add space for dimensions and aspect ratio - S32 info_height = dim_rect.mTop + CLIENT_RECT_VPAD; - - S32 screen_width = gFloaterView->getSnapRect().getWidth(); - S32 screen_height = gFloaterView->getSnapRect().getHeight(); - - S32 max_image_width = screen_width - 2*horiz_pad; - S32 max_image_height = screen_height - (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD) - - (PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height); - - S32 client_width = llmin(max_image_width,mImage->getFullWidth()); - S32 client_height = llmin(max_image_height,mImage->getFullHeight()); - - if (mAspectRatio > 0.f) - { - if(mAspectRatio > 1.f) - { - client_height = llceil((F32)client_width / mAspectRatio); - if(client_height > max_image_height) - { - client_height = max_image_height; - client_width = llceil((F32)client_height * mAspectRatio); - } - } - else//mAspectRatio < 1.f - { - client_width = llceil((F32)client_height * mAspectRatio); - if(client_width > max_image_width) - { - client_width = max_image_width; - client_height = llceil((F32)client_width / mAspectRatio); - } - } - } - else - { - - if(client_height > max_image_height) - { - F32 ratio = (F32)max_image_height/client_height; - client_height = max_image_height; - client_width = llceil((F32)client_height * ratio); - } - - if(client_width > max_image_width) - { - F32 ratio = (F32)max_image_width/client_width; - client_width = max_image_width; - client_height = llceil((F32)client_width * ratio); - } - } - - //now back to whole floater - S32 floater_width = llmax(getMinWidth(),client_width + 2*horiz_pad); - S32 floater_height = llmax(getMinHeight(),client_height + (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD) - + (PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height)); - //reshape floater - reshape( floater_width, floater_height ); - gFloaterView->adjustToFitScreen(this, FALSE); + reshape(getRect().getWidth(), getRect().getHeight()); - //setup image rect... - LLRect client_rect(horiz_pad, getRect().getHeight(), getRect().getWidth() - horiz_pad, 0); - client_rect.mTop -= (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD); - client_rect.mBottom += PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height ; - - mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() + (client_height / 2), client_width, client_height); + gFloaterView->adjustToFitScreen(this, FALSE); - // Hide the aspect ratio label if the window is too narrow - // Assumes the label should be to the right of the dimensions + LLRect dim_rect(getChildView("dimensions")->getRect()); LLRect aspect_label_rect(getChildView("aspect_ratio")->getRect()); getChildView("aspect_ratio")->setVisible( dim_rect.mRight < aspect_label_rect.mLeft); } diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp index e9504cbba0..31fde5d58a 100644 --- a/indra/newview/llprogressview.cpp +++ b/indra/newview/llprogressview.cpp @@ -133,13 +133,13 @@ void LLProgressView::setVisible(BOOL visible) mFadeTimer.start(); } // showing progress view - else if (!getVisible() && visible) + else if (visible && (!getVisible() || mFadeTimer.getStarted())) { setFocus(TRUE); mFadeTimer.stop(); mProgressTimer.start(); LLPanel::setVisible(TRUE); - } + } } @@ -207,7 +207,7 @@ void LLProgressView::setText(const std::string& text) void LLProgressView::setPercent(const F32 percent) { - mProgressBar->setPercent(percent); + mProgressBar->setValue(percent); } void LLProgressView::setMessage(const std::string& msg) diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp index d63a48647d..e5ef51bdd1 100644 --- a/indra/newview/llremoteparcelrequest.cpp +++ b/indra/newview/llremoteparcelrequest.cpp @@ -77,23 +77,20 @@ void LLRemoteParcelRequestResponder::error(U32 status, const std::string& reason void LLRemoteParcelInfoProcessor::addObserver(const LLUUID& parcel_id, LLRemoteParcelInfoObserver* observer) { - // Check if the observer is already in observers list for this UUID observer_multimap_t::iterator it; + observer_multimap_t::iterator start = mObservers.lower_bound(parcel_id); + observer_multimap_t::iterator end = mObservers.upper_bound(parcel_id); - it = mObservers.find(parcel_id); - while (it != mObservers.end()) + // Check if the observer is already in observers list for this UUID + for(it = start; it != end; ++it) { - if (it->second == observer) + if (it->second.get() == observer) { return; } - else - { - ++it; - } } - mObservers.insert(std::pair<LLUUID, LLRemoteParcelInfoObserver*>(parcel_id, observer)); + mObservers.insert(std::make_pair(parcel_id, observer->getObserverHandle())); } void LLRemoteParcelInfoProcessor::removeObserver(const LLUUID& parcel_id, LLRemoteParcelInfoObserver* observer) @@ -104,19 +101,16 @@ void LLRemoteParcelInfoProcessor::removeObserver(const LLUUID& parcel_id, LLRemo } observer_multimap_t::iterator it; + observer_multimap_t::iterator start = mObservers.lower_bound(parcel_id); + observer_multimap_t::iterator end = mObservers.upper_bound(parcel_id); - it = mObservers.find(parcel_id); - while (it != mObservers.end()) + for(it = start; it != end; ++it) { - if (it->second == observer) + if (it->second.get() == observer) { mObservers.erase(it); break; } - else - { - ++it; - } } } @@ -141,13 +135,38 @@ void LLRemoteParcelInfoProcessor::processParcelInfoReply(LLMessageSystem* msg, v msg->getS32 ("Data", "SalePrice", parcel_data.sale_price); msg->getS32 ("Data", "AuctionID", parcel_data.auction_id); - LLRemoteParcelInfoProcessor::observer_multimap_t observers = LLRemoteParcelInfoProcessor::getInstance()->mObservers; + LLRemoteParcelInfoProcessor::observer_multimap_t & observers = LLRemoteParcelInfoProcessor::getInstance()->mObservers; - observer_multimap_t::iterator oi = observers.find(parcel_data.parcel_id); + typedef std::vector<observer_multimap_t::iterator> deadlist_t; + deadlist_t dead_iters; + + observer_multimap_t::iterator oi = observers.lower_bound(parcel_data.parcel_id); observer_multimap_t::iterator end = observers.upper_bound(parcel_data.parcel_id); - for (; oi != end; ++oi) + + while (oi != end) + { + // increment the loop iterator now since it may become invalid below + observer_multimap_t::iterator cur_oi = oi++; + + LLRemoteParcelInfoObserver * observer = cur_oi->second.get(); + if(observer) + { + // may invalidate cur_oi if the observer removes itself + observer->processParcelInfo(parcel_data); + } + else + { + // the handle points to an expired observer, so don't keep it + // around anymore + dead_iters.push_back(cur_oi); + } + } + + deadlist_t::iterator i; + deadlist_t::iterator end_dead = dead_iters.end(); + for(i = dead_iters.begin(); i != end_dead; ++i) { - oi->second->processParcelInfo(parcel_data); + observers.erase(*i); } } diff --git a/indra/newview/llremoteparcelrequest.h b/indra/newview/llremoteparcelrequest.h index a6c62995a9..74cf1616df 100644 --- a/indra/newview/llremoteparcelrequest.h +++ b/indra/newview/llremoteparcelrequest.h @@ -98,7 +98,7 @@ public: static void processParcelInfoReply(LLMessageSystem* msg, void**); private: - typedef std::multimap<LLUUID, LLRemoteParcelInfoObserver*> observer_multimap_t; + typedef std::multimap<LLUUID, LLHandle<LLRemoteParcelInfoObserver> > observer_multimap_t; observer_multimap_t mObservers; }; diff --git a/indra/newview/llrootview.h b/indra/newview/llrootview.h index 4b1ba15a0b..5223a314f3 100644 --- a/indra/newview/llrootview.h +++ b/indra/newview/llrootview.h @@ -42,27 +42,5 @@ public: LLRootView(const Params& p) : LLView(p) {} - - // added to provide possibility to handle mouse click event inside all application - // window without creating any floater - typedef boost::signals2::signal<void(S32 x, S32 y, MASK mask)> - mouse_signal_t; - - private: - mouse_signal_t mMouseDownSignal; - - public: - /*virtual*/ - BOOL handleMouseDown(S32 x, S32 y, MASK mask) - { - mMouseDownSignal(x, y, mask); - return LLView::handleMouseDown(x, y, mask); - } - - boost::signals2::connection addMouseDownCallback( - const mouse_signal_t::slot_type& cb) - { - return mMouseDownSignal.connect(cb); - } }; #endif //LL_LLROOTVIEW_H diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index 18c9ac28c1..e3bc67a414 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -83,11 +83,10 @@ bool LLScreenChannelBase::isHovering() return mHoveredToast->isHovered(); } -bool LLScreenChannelBase::resetPositionAndSize(const LLSD& newvalue) +void LLScreenChannelBase::resetPositionAndSize() { LLRect rc = gViewerWindow->getWorldViewRectScaled(); updatePositionAndSize(rc, rc); - return true; } void LLScreenChannelBase::updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect) @@ -99,10 +98,7 @@ void LLScreenChannelBase::updatePositionAndSize(LLRect old_world_rect, LLRect ne if (gSavedSettings.getBOOL("SidebarCameraMovement") == FALSE && LLSideTray::instanceCreated ()) { - LLSideTray* side_bar = LLSideTray::getInstance(); - - if (side_bar->getVisible() && !side_bar->getCollapsed()) - world_rect_padding += side_bar->getRect().getWidth(); + world_rect_padding += LLSideTray::getInstance()->getVisibleWidth(); } @@ -133,15 +129,25 @@ void LLScreenChannelBase::init(S32 channel_left, S32 channel_right) if(LLSideTray::instanceCreated()) { LLSideTray* side_bar = LLSideTray::getInstance(); - side_bar->getCollapseSignal().connect(boost::bind(&LLScreenChannelBase::resetPositionAndSize, this, _2)); + side_bar->setVisibleWidthChangeCallback(boost::bind(&LLScreenChannelBase::resetPositionAndSize, this)); } + // top and bottom set by updateBottom() + setRect(LLRect(channel_left, 0, channel_right, 0)); + updateBottom(); + setVisible(TRUE); +} + +void LLScreenChannelBase::updateBottom() +{ S32 channel_top = gViewerWindow->getWorldViewRectScaled().getHeight(); - S32 channel_bottom = gViewerWindow->getWorldViewRectScaled().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin"); + S32 channel_bottom = gSavedSettings.getS32("ChannelBottomPanelMargin"); + S32 channel_left = getRect().mLeft; + S32 channel_right = getRect().mRight; setRect(LLRect(channel_left, channel_top, channel_right, channel_bottom)); - setVisible(TRUE); } + //-------------------------------------------------------------------------- ////////////////////// // LLScreenChannel @@ -204,10 +210,7 @@ void LLScreenChannel::updatePositionAndSize(LLRect old_world_rect, LLRect new_wo if (gSavedSettings.getBOOL("SidebarCameraMovement") == FALSE && LLSideTray::instanceCreated ()) { - LLSideTray* side_bar = LLSideTray::getInstance(); - - if (side_bar->getVisible() && !side_bar->getCollapsed()) - world_rect_padding += side_bar->getRect().getWidth(); + world_rect_padding += LLSideTray::getInstance()->getVisibleWidth(); } @@ -253,8 +256,8 @@ void LLScreenChannel::addToast(const LLToast::Params& p) if(mControlHovering) { new_toast_elem.toast->setOnToastHoverCallback(boost::bind(&LLScreenChannel::onToastHover, this, _1, _2)); - new_toast_elem.toast->setMouseEnterCallback(boost::bind(&LLScreenChannel::stopFadingToast, this, new_toast_elem.toast)); - new_toast_elem.toast->setMouseLeaveCallback(boost::bind(&LLScreenChannel::startFadingToast, this, new_toast_elem.toast)); + new_toast_elem.toast->setMouseEnterCallback(boost::bind(&LLScreenChannel::stopToastTimer, this, new_toast_elem.toast)); + new_toast_elem.toast->setMouseLeaveCallback(boost::bind(&LLScreenChannel::startToastTimer, this, new_toast_elem.toast)); } if(show_toast) @@ -369,7 +372,7 @@ void LLScreenChannel::loadStoredToastsToChannel() for(it = mStoredToastList.begin(); it != mStoredToastList.end(); ++it) { (*it).toast->setIsHidden(false); - (*it).toast->resetTimer(); + (*it).toast->startTimer(); mToastList.push_back((*it)); } @@ -394,7 +397,7 @@ void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) } toast->setIsHidden(false); - toast->resetTimer(); + toast->startTimer(); mToastList.push_back((*it)); redrawToasts(); @@ -477,7 +480,7 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel) toast->removeChild(old_panel); delete old_panel; toast->insertPanel(panel); - toast->resetTimer(); + toast->startTimer(); redrawToasts(); } } @@ -485,7 +488,7 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel) //-------------------------------------------------------------------------- void LLScreenChannel::redrawToasts() { - if(mToastList.size() == 0 || isHovering()) + if(mToastList.size() == 0) return; switch(mToastAlignment) @@ -511,6 +514,8 @@ void LLScreenChannel::showToastsBottom() S32 toast_margin = 0; std::vector<ToastElem>::reverse_iterator it; + updateBottom(); + LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get()); for(it = mToastList.rbegin(); it != mToastList.rend(); ++it) @@ -583,20 +588,15 @@ void LLScreenChannel::showToastsBottom() } } + // Dismiss toasts we don't have space for (STORM-391). if(it != mToastList.rend()) { mHiddenToastsNum = 0; for(; it != mToastList.rend(); it++) { - (*it).toast->stopTimer(); - (*it).toast->setVisible(FALSE); - mHiddenToastsNum++; + (*it).toast->hide(); } } - else - { - closeOverflowToastPanel(); - } } //-------------------------------------------------------------------------- @@ -697,15 +697,15 @@ void LLScreenChannel::closeStartUpToast() } } -void LLNotificationsUI::LLScreenChannel::stopFadingToast(LLToast* toast) +void LLNotificationsUI::LLScreenChannel::stopToastTimer(LLToast* toast) { if (!toast || toast != mHoveredToast) return; // Pause fade timer of the hovered toast. - toast->stopFading(); + toast->stopTimer(); } -void LLNotificationsUI::LLScreenChannel::startFadingToast(LLToast* toast) +void LLNotificationsUI::LLScreenChannel::startToastTimer(LLToast* toast) { if (!toast || toast == mHoveredToast) { @@ -713,13 +713,12 @@ void LLNotificationsUI::LLScreenChannel::startFadingToast(LLToast* toast) } // Reset its fade timer. - toast->startFading(); + toast->startTimer(); } //-------------------------------------------------------------------------- void LLScreenChannel::hideToastsFromScreen() { - closeOverflowToastPanel(); for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++) (*it).toast->setVisible(FALSE); } @@ -835,8 +834,7 @@ void LLScreenChannel::onToastHover(LLToast* toast, bool mouse_enter) } } - if(!isHovering()) - redrawToasts(); + redrawToasts(); } //-------------------------------------------------------------------------- @@ -850,13 +848,7 @@ void LLScreenChannel::updateShowToastsState() return; } - S32 channel_bottom = gViewerWindow->getWorldViewRectScaled().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin");; - LLRect this_rect = getRect(); - - if(channel_bottom != this_rect.mBottom) - { - setRect(LLRect(this_rect.mLeft, this_rect.mTop, this_rect.mRight, channel_bottom)); - } + updateBottom(); } //-------------------------------------------------------------------------- diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h index a1fdd6e32c..d207d13981 100644 --- a/indra/newview/llscreenchannel.h +++ b/indra/newview/llscreenchannel.h @@ -59,8 +59,8 @@ public: // Channel's outfit-functions // update channel's size and position in the World View virtual void updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect); + void resetPositionAndSize(); - bool resetPositionAndSize(const LLSD& newvalue); // initialization of channel's shape and position virtual void init(S32 channel_left, S32 channel_right); @@ -81,9 +81,6 @@ public: // show all toasts in a channel virtual void redrawToasts() {}; - virtual void closeOverflowToastPanel() {}; - virtual void hideOverflowToastPanel() {}; - // Channel's behavior-functions // set whether a channel will control hovering inside itself or not @@ -111,6 +108,8 @@ public: LLUUID getChannelID() { return mID; } protected: + void updateBottom(); + // Channel's flags bool mControlHovering; LLToast* mHoveredToast; @@ -194,10 +193,10 @@ public: /** Stop fading given toast */ - virtual void stopFadingToast(LLToast* toast); + virtual void stopToastTimer(LLToast* toast); /** Start fading given toast */ - virtual void startFadingToast(LLToast* toast); + virtual void startToastTimer(LLToast* toast); // get StartUp Toast's state static bool getStartUpToastShown() { return mWasStartUpToastShown; } diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp index 2334f0cde5..170e23e4c5 100644 --- a/indra/newview/llscriptfloater.cpp +++ b/indra/newview/llscriptfloater.cpp @@ -32,11 +32,13 @@ #include "llchannelmanager.h" #include "llchiclet.h" #include "llfloaterreg.h" +#include "lllslconstants.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llscreenchannel.h" #include "llsyswellwindow.h" #include "lltoastnotifypanel.h" +#include "lltoastscripttextbox.h" #include "lltrans.h" #include "llviewerwindow.h" #include "llimfloater.h" @@ -151,10 +153,18 @@ void LLScriptFloater::createForm(const LLUUID& notification_id) // create new form LLRect toast_rect = getRect(); - // LLToastNotifyPanel will fit own content in vertical direction, - // but it needs an initial rect to properly calculate its width - // Use an initial rect of the script floater to make the floater window more configurable. - mScriptForm = new LLToastNotifyPanel(notification, toast_rect); + if (isScriptTextbox(notification)) + { + mScriptForm = new LLToastScriptTextbox(notification); + } + else + { + // LLToastNotifyPanel will fit own content in vertical direction, + // but it needs an initial rect to properly calculate its width + // Use an initial rect of the script floater to make the floater + // window more configurable. + mScriptForm = new LLToastNotifyPanel(notification, toast_rect); + } addChild(mScriptForm); // position form on floater @@ -564,4 +574,32 @@ void LLScriptFloaterManager::setFloaterVisible(const LLUUID& notification_id, bo } } +////////////////////////////////////////////////////////////////// + +bool LLScriptFloater::isScriptTextbox(LLNotificationPtr notification) +{ + // get a form for the notification + LLNotificationFormPtr form(notification->getForm()); + + if (form) + { + // get number of elements in the form + int num_options = form->getNumElements(); + + // if ANY of the buttons have the magic lltextbox string as + // name, then treat the whole dialog as a simple text entry + // box (i.e. mixed button and textbox forms are not supported) + for (int i=0; i<num_options; ++i) + { + LLSD form_element = form->getElement(i); + if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN) + { + return true; + } + } + } + + return false; +} + // EOF diff --git a/indra/newview/llscriptfloater.h b/indra/newview/llscriptfloater.h index da70bb4334..8e959a3d0e 100644 --- a/indra/newview/llscriptfloater.h +++ b/indra/newview/llscriptfloater.h @@ -28,8 +28,9 @@ #define LL_SCRIPTFLOATER_H #include "lltransientdockablefloater.h" +#include "llnotificationptr.h" -class LLToastNotifyPanel; +class LLToastPanel; /** * Handles script notifications ("ScriptDialog" and "ScriptDialogGroup") @@ -203,7 +204,9 @@ protected: void dockToChiclet(bool dock); private: - LLToastNotifyPanel* mScriptForm; + bool isScriptTextbox(LLNotificationPtr notification); + + LLToastPanel* mScriptForm; LLUUID mNotificationId; LLUUID mObjectId; bool mSaveFloaterPosition; diff --git a/indra/newview/llscrollingpanelparam.cpp b/indra/newview/llscrollingpanelparam.cpp index 05b273cd29..f8c20dada0 100644 --- a/indra/newview/llscrollingpanelparam.cpp +++ b/indra/newview/llscrollingpanelparam.cpp @@ -165,12 +165,16 @@ void LLScrollingPanelParam::draw() getChildView("max param text")->setVisible( FALSE ); LLPanel::draw(); + // If we're in a focused floater, don't apply the floater's alpha to visual param hint, + // making its behavior similar to texture controls'. + F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); + // Draw the hints over the "less" and "more" buttons. gGL.pushUIMatrix(); { const LLRect& r = mHintMin->getRect(); gGL.translateUI((F32)r.mLeft, (F32)r.mBottom, 0.f); - mHintMin->draw(); + mHintMin->draw(alpha); } gGL.popUIMatrix(); @@ -178,7 +182,7 @@ void LLScrollingPanelParam::draw() { const LLRect& r = mHintMax->getRect(); gGL.translateUI((F32)r.mLeft, (F32)r.mBottom, 0.f); - mHintMax->draw(); + mHintMax->draw(alpha); } gGL.popUIMatrix(); diff --git a/indra/newview/llsearchcombobox.cpp b/indra/newview/llsearchcombobox.cpp index db531b5695..6558c9a7fa 100644 --- a/indra/newview/llsearchcombobox.cpp +++ b/indra/newview/llsearchcombobox.cpp @@ -131,6 +131,9 @@ void LLSearchComboBox::focusTextEntry() if (mTextEntry) { gFocusMgr.setKeyboardFocus(mTextEntry); + + // Let the editor handle editing hotkeys (STORM-431). + LLEditMenuHandler::gEditMenuHandler = mTextEntry; } } diff --git a/indra/newview/llshareavatarhandler.cpp b/indra/newview/llshareavatarhandler.cpp new file mode 100644 index 0000000000..34194970b8 --- /dev/null +++ b/indra/newview/llshareavatarhandler.cpp @@ -0,0 +1,59 @@ +/** + * @file llshareavatarhandler.cpp + * @brief slapp to handle sharing with an avatar + * + * $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 "llcommandhandler.h" +#include "llavataractions.h" + +class LLShareWithAvatarHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLShareWithAvatarHandler() : LLCommandHandler("sharewithavatar", UNTRUSTED_THROTTLE) + { + } + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + //Make sure we have some parameters + if (params.size() == 0) + { + return false; + } + + //Get the ID + LLUUID id; + if (!id.set( params[0], FALSE )) + { + return false; + } + + //instigate share with this avatar + LLAvatarActions::share( id ); + return true; + } +}; +LLShareWithAvatarHandler gShareWithAvatar; diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index 1999f14828..b316171604 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -183,12 +183,15 @@ void LLSidepanelAppearance::onOpen(const LLSD& key) void LLSidepanelAppearance::onVisibilityChange(const LLSD &new_visibility) { - updateToVisibility(new_visibility); + LLSD visibility; + visibility["visible"] = new_visibility.asBoolean(); + visibility["reset_accordion"] = true; + updateToVisibility(visibility); } void LLSidepanelAppearance::updateToVisibility(const LLSD &new_visibility) { - if (new_visibility.asBoolean()) + if (new_visibility["visible"].asBoolean()) { bool is_outfit_edit_visible = mOutfitEdit && mOutfitEdit->getVisible(); bool is_wearable_edit_visible = mEditWearable && mEditWearable->getVisible(); @@ -209,7 +212,7 @@ void LLSidepanelAppearance::updateToVisibility(const LLSD &new_visibility) } } - if (is_outfit_edit_visible) + if (is_outfit_edit_visible && new_visibility["reset_accordion"].asBoolean()) { mOutfitEdit->resetAccordionState(); } diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index f9c0fd398e..c8c6858b81 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -68,10 +68,22 @@ private: void LLItemPropertiesObserver::changed(U32 mask) { - // if there's a change we're interested in. - if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) + const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs(); + std::set<LLUUID>::const_iterator it; + + const LLUUID& item_id = mFloater->getItemID(); + + for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++) { - mFloater->dirty(); + // set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288) + if (*it == item_id) + { + // if there's a change we're interested in. + if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) + { + mFloater->dirty(); + } + } } } @@ -179,6 +191,16 @@ void LLSidepanelItemInfo::setItemID(const LLUUID& item_id) mItemID = item_id; } +const LLUUID& LLSidepanelItemInfo::getObjectID() const +{ + return mObjectID; +} + +const LLUUID& LLSidepanelItemInfo::getItemID() const +{ + return mItemID; +} + void LLSidepanelItemInfo::reset() { LLSidepanelInventorySubpanel::reset(); diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h index 10e93dd7de..25be145f64 100644 --- a/indra/newview/llsidepaneliteminfo.h +++ b/indra/newview/llsidepaneliteminfo.h @@ -54,6 +54,9 @@ public: void setItemID(const LLUUID& item_id); void setEditMode(BOOL edit); + const LLUUID& getObjectID() const; + const LLUUID& getItemID() const; + protected: /*virtual*/ void refresh(); /*virtual*/ void save(); diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp index 47d904dfcc..8774482acd 100644 --- a/indra/newview/llsidepaneltaskinfo.cpp +++ b/indra/newview/llsidepaneltaskinfo.cpp @@ -1120,17 +1120,17 @@ void LLSidepanelTaskInfo::updateVerbs() */ LLSafeHandle<LLObjectSelection> object_selection = LLSelectMgr::getInstance()->getSelection(); - const BOOL multi_select = (object_selection->getNumNodes() > 1); + const BOOL any_selected = (object_selection->getNumNodes() > 0); - mOpenBtn->setVisible(!multi_select); - mPayBtn->setVisible(!multi_select); - mBuyBtn->setVisible(!multi_select); - mDetailsBtn->setVisible(multi_select); - mDetailsBtn->setEnabled(multi_select); + mOpenBtn->setVisible(true); + mPayBtn->setVisible(true); + mBuyBtn->setVisible(true); + mDetailsBtn->setVisible(true); mOpenBtn->setEnabled(enable_object_open()); mPayBtn->setEnabled(enable_pay_object()); mBuyBtn->setEnabled(enable_buy_object()); + mDetailsBtn->setEnabled(any_selected); } void LLSidepanelTaskInfo::onOpenButtonClicked() diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp index 81b2fc0ae0..aef665a35c 100644 --- a/indra/newview/llsidetray.cpp +++ b/indra/newview/llsidetray.cpp @@ -118,7 +118,7 @@ public: protected: LLSideTrayTab(const Params& params); - void dock(); + void dock(LLFloater* floater_tab); void undock(LLFloater* floater_tab); LLSideTray* getSideTray(); @@ -159,8 +159,6 @@ LLSideTrayTab::LLSideTrayTab(const Params& p) mDescription(p.description), mMainPanel(NULL) { - // Necessary for focus movement among child controls - setFocusRoot(TRUE); } LLSideTrayTab::~LLSideTrayTab() @@ -259,7 +257,7 @@ void LLSideTrayTab::toggleTabDocked() if (docking) { - dock(); + dock(floater_tab); } else { @@ -271,11 +269,14 @@ void LLSideTrayTab::toggleTabDocked() LLFloaterReg::toggleInstance("side_bar_tab", tab_name); } -void LLSideTrayTab::dock() +void LLSideTrayTab::dock(LLFloater* floater_tab) { LLSideTray* side_tray = getSideTray(); if (!side_tray) return; + // Before docking the tab, reset its (and its children's) transparency to default (STORM-688). + floater_tab->updateTransparency(TT_DEFAULT); + if (!side_tray->addTab(this)) { llwarns << "Failed to add tab " << getName() << " to side tray" << llendl; @@ -298,7 +299,11 @@ static void on_minimize(LLSidepanelAppearance* panel, LLSD minimized) { if (!panel) return; bool visible = !minimized.asBoolean(); - panel->updateToVisibility(LLSD(visible)); + LLSD visibility; + visibility["visible"] = visible; + // Do not reset accordion state on minimize (STORM-375) + visibility["reset_accordion"] = false; + panel->updateToVisibility(visibility); } void LLSideTrayTab::undock(LLFloater* floater_tab) @@ -556,7 +561,7 @@ BOOL LLSideTray::postBuild() { if ((*it).channel) { - getCollapseSignal().connect(boost::bind(&LLScreenChannelBase::resetPositionAndSize, (*it).channel, _2)); + setVisibleWidthChangeCallback(boost::bind(&LLScreenChannelBase::resetPositionAndSize, (*it).channel)); } } @@ -910,7 +915,6 @@ void LLSideTray::createButtons () } } LLHints::registerHintTarget("inventory_btn", mTabButtons["sidebar_inventory"]->getHandle()); - LLHints::registerHintTarget("dest_guide_btn", mTabButtons["sidebar_places"]->getHandle()); } void LLSideTray::processTriState () @@ -976,9 +980,6 @@ void LLSideTray::reflectCollapseChange() } gFloaterView->refresh(); - - LLSD new_value = mCollapsed; - mCollapseSignal(this,new_value); } void LLSideTray::arrange() @@ -1028,7 +1029,8 @@ void LLSideTray::arrange() } // The tab buttons should be shown only if there is at least one non-detached tab. - mButtonsPanel->setVisible(hasTabs()); + // Also hide them in mouse-look mode. + mButtonsPanel->setVisible(hasTabs() && !gAgentCamera.cameraMouselook()); } // Detach those tabs that were detached when the viewer exited last time. @@ -1257,9 +1259,29 @@ bool LLSideTray::isPanelActive(const std::string& panel_name) void LLSideTray::updateSidetrayVisibility() { // set visibility of parent container based on collapsed state - if (getParent()) + LLView* parent = getParent(); + if (parent) { - getParent()->setVisible(!mCollapsed && !gAgentCamera.cameraMouselook()); + bool old_visibility = parent->getVisible(); + bool new_visibility = !mCollapsed && !gAgentCamera.cameraMouselook(); + + if (old_visibility != new_visibility) + { + parent->setVisible(new_visibility); + + // Signal change of visible width. + llinfos << "Visible: " << new_visibility << llendl; + mVisibleWidthChangeSignal(this, new_visibility); + } } } +S32 LLSideTray::getVisibleWidth() +{ + return (isInVisibleChain() && !mCollapsed) ? getRect().getWidth() : 0; +} + +void LLSideTray::setVisibleWidthChangeCallback(const commit_signal_t::slot_type& cb) +{ + mVisibleWidthChangeSignal.connect(cb); +} diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h index 4c23a1920b..184d78845f 100644 --- a/indra/newview/llsidetray.h +++ b/indra/newview/llsidetray.h @@ -40,6 +40,8 @@ class LLSideTray : public LLPanel, private LLDestroyClass<LLSideTray> { friend class LLUICtrlFactory; friend class LLDestroyClass<LLSideTray>; + friend class LLSideTrayTab; + friend class LLSideTrayButton; public: LOG_CLASS(LLSideTray); @@ -126,11 +128,6 @@ public: } /* - * get currently active tab - */ - const LLSideTrayTab* getActiveTab() const { return mActiveTab; } - - /* * collapse SideBar, hiding visible tab and moving tab buttons * to the right corner of the screen */ @@ -163,32 +160,37 @@ public: virtual BOOL postBuild(); - void onTabButtonClick(std::string name); - void onToggleCollapse(); - - bool addChild (LLView* view, S32 tab_group); - bool removeTab (LLSideTrayTab* tab); // Used to detach tabs temporarily - bool addTab (LLSideTrayTab* tab); // Used to re-attach tabs - BOOL handleMouseDown (S32 x, S32 y, MASK mask); void reshape (S32 width, S32 height, BOOL called_from_parent = TRUE); - void processTriState (); - - void updateSidetrayVisibility(); - commit_signal_t& getCollapseSignal() { return mCollapseSignal; } + /** + * @return side tray width if it's visible and expanded, 0 otherwise. + * + * Not that width of the tab buttons is not included. + * + * @see setVisibleWidthChangeCallback() + */ + S32 getVisibleWidth(); - void handleLoginComplete(); + void setVisibleWidthChangeCallback(const commit_signal_t::slot_type& cb); - LLSideTrayTab* getTab (const std::string& name); + void updateSidetrayVisibility(); + + void handleLoginComplete(); bool isTabAttached (const std::string& name); protected: + bool addChild (LLView* view, S32 tab_group); + bool removeTab (LLSideTrayTab* tab); // Used to detach tabs temporarily + bool addTab (LLSideTrayTab* tab); // Used to re-attach tabs bool hasTabs (); + const LLSideTrayTab* getActiveTab() const { return mActiveTab; } + LLSideTrayTab* getTab(const std::string& name); + void createButtons (); LLButton* createButton (const std::string& name,const std::string& image,const std::string& tooltip, @@ -196,11 +198,15 @@ protected: void arrange (); void detachTabs (); void reflectCollapseChange(); + void processTriState (); void toggleTabButton (LLSideTrayTab* tab); LLPanel* openChildPanel (LLSideTrayTab* tab, const std::string& panel_name, const LLSD& params); + void onTabButtonClick(std::string name); + void onToggleCollapse(); + private: // Implementation of LLDestroyClass<LLSideTray> static void destroyClass() @@ -219,7 +225,7 @@ private: tab_order_vector_t mOriginalTabOrder; LLSideTrayTab* mActiveTab; - commit_signal_t mCollapseSignal; + commit_signal_t mVisibleWidthChangeSignal; LLButton* mCollapseButton; bool mCollapsed; diff --git a/indra/newview/llsimplestat.h b/indra/newview/llsimplestat.h new file mode 100644 index 0000000000..a90e503adb --- /dev/null +++ b/indra/newview/llsimplestat.h @@ -0,0 +1,158 @@ +/** + * @file llsimplestat.h + * @brief Runtime statistics accumulation. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_SIMPLESTAT_H +#define LL_SIMPLESTAT_H + +// History +// +// The original source for this code is the server repositories' +// llcommon/llstat.h file. This particular code was added after the +// viewer/server code schism but before the effort to convert common +// code to libraries was complete. Rather than add to merge issues, +// the needed code was cut'n'pasted into this new header as it isn't +// too awful a burden. Post-modularization, we can look at removing +// this redundancy. + + +/** + * @class LLSimpleStatCounter + * @brief Just counts events. + * + * Really not needed but have a pattern in mind in the future. + * Interface limits what can be done at that's just fine. + * + * *TODO: Update/transfer unit tests + * Unit tests: indra/test/llcommon_llstat_tut.cpp + */ +class LLSimpleStatCounter +{ +public: + inline LLSimpleStatCounter() { reset(); } + // Default destructor and assignment operator are valid + + inline void reset() { mCount = 0; } + + inline void merge(const LLSimpleStatCounter & src) + { mCount += src.mCount; } + + inline U32 operator++() { return ++mCount; } + + inline U32 getCount() const { return mCount; } + +protected: + U32 mCount; +}; + + +/** + * @class LLSimpleStatMMM + * @brief Templated collector of min, max and mean data for stats. + * + * Fed a stream of data samples, keeps a running account of the + * min, max and mean seen since construction or the last reset() + * call. A freshly-constructed or reset instance returns counts + * and values of zero. + * + * Overflows and underflows (integer, inf or -inf) and NaN's + * are the caller's problem. As is loss of precision when + * the running sum's exponent (when parameterized by a floating + * point of some type) differs from a given data sample's. + * + * Unit tests: indra/test/llcommon_llstat_tut.cpp + */ +template <typename VALUE_T = F32> +class LLSimpleStatMMM +{ +public: + typedef VALUE_T Value; + +public: + LLSimpleStatMMM() { reset(); } + // Default destructor and assignment operator are valid + + /** + * Resets the object returning all counts and derived + * values back to zero. + */ + void reset() + { + mCount = 0; + mMin = Value(0); + mMax = Value(0); + mTotal = Value(0); + } + + void record(Value v) + { + if (mCount) + { + mMin = llmin(mMin, v); + mMax = llmax(mMax, v); + } + else + { + mMin = v; + mMax = v; + } + mTotal += v; + ++mCount; + } + + void merge(const LLSimpleStatMMM<VALUE_T> & src) + { + if (! mCount) + { + *this = src; + } + else if (src.mCount) + { + mMin = llmin(mMin, src.mMin); + mMax = llmax(mMax, src.mMax); + mCount += src.mCount; + mTotal += src.mTotal; + } + } + + inline U32 getCount() const { return mCount; } + inline Value getMin() const { return mMin; } + inline Value getMax() const { return mMax; } + inline Value getMean() const { return mCount ? mTotal / mCount : mTotal; } + +protected: + U32 mCount; + Value mMin; + Value mMax; + Value mTotal; +}; + +#endif // LL_SIMPLESTAT_H diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index fb984a7c62..960e72ee42 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -1555,7 +1555,9 @@ void LLSpatialGroup::doOcclusion(LLCamera* camera) { if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1) { - if (earlyFail(camera, this)) + // Don't cull hole/edge water, unless we have the GL_ARB_depth_clamp extension + if ((mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER && !gGLManager.mHasDepthClamp) || + earlyFail(camera, this)) { setOcclusionState(LLSpatialGroup::DISCARD_QUERY); assert_states_valid(this); @@ -1576,7 +1578,18 @@ void LLSpatialGroup::doOcclusion(LLCamera* camera) { buildOcclusion(); } - + + // Depth clamp all water to avoid it being culled as a result of being + // behind the far clip plane, and in the case of edge water to avoid + // it being culled while still visible. + bool const use_depth_clamp = gGLManager.mHasDepthClamp && + (mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER || + mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER); + if (use_depth_clamp) + { + glEnable(GL_DEPTH_CLAMP); + } + glBeginQueryARB(GL_SAMPLES_PASSED_ARB, mOcclusionQuery[LLViewerCamera::sCurCameraID]); glVertexPointer(3, GL_FLOAT, 0, mOcclusionVerts); if (camera->getOrigin().isExactlyZero()) @@ -1592,6 +1605,11 @@ void LLSpatialGroup::doOcclusion(LLCamera* camera) GL_UNSIGNED_BYTE, get_box_fan_indices(camera, mBounds[0])); } glEndQueryARB(GL_SAMPLES_PASSED_ARB); + + if (use_depth_clamp) + { + glDisable(GL_DEPTH_CLAMP); + } } setOcclusionState(LLSpatialGroup::QUERY_PENDING); @@ -2591,9 +2609,10 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE) gGL.color4f(0.5f,0.5f,0.5f,1.0f); break; case LLViewerObject::LL_VO_PART_GROUP: - case LLViewerObject::LL_VO_HUD_PART_GROUP: + case LLViewerObject::LL_VO_HUD_PART_GROUP: gGL.color4f(0,0,1,1); break; + case LLViewerObject::LL_VO_VOID_WATER: case LLViewerObject::LL_VO_WATER: gGL.color4f(0,0.5f,1,1); break; diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h index 1a25f3f85d..2b9cf6c630 100644 --- a/indra/newview/llspatialpartition.h +++ b/indra/newview/llspatialpartition.h @@ -551,6 +551,13 @@ public: virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32& index_count) { } }; +//spatial partition for hole and edge water (implemented in LLVOWater.cpp) +class LLVoidWaterPartition : public LLWaterPartition +{ +public: + LLVoidWaterPartition(); +}; + //spatial partition for terrain (impelmented in LLVOSurfacePatch.cpp) class LLTerrainPartition : public LLSpatialPartition { diff --git a/indra/newview/llspeakbutton.cpp b/indra/newview/llspeakbutton.cpp index 3dce66f394..c76ecae4a2 100644 --- a/indra/newview/llspeakbutton.cpp +++ b/indra/newview/llspeakbutton.cpp @@ -134,8 +134,11 @@ LLSpeakButton::LLSpeakButton(const Params& p) LLSpeakButton::~LLSpeakButton() { - LLTransientFloaterMgr::getInstance()->removeControlView(mSpeakBtn); - LLTransientFloaterMgr::getInstance()->removeControlView(mShowBtn); + if(LLTransientFloaterMgr::instanceExists()) + { + LLTransientFloaterMgr::getInstance()->removeControlView(mSpeakBtn); + LLTransientFloaterMgr::getInstance()->removeControlView(mShowBtn); + } } void LLSpeakButton::setSpeakToolTip(const std::string& msg) diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp index ede1d6bebe..9b38bf22ff 100644 --- a/indra/newview/llspeakingindicatormanager.cpp +++ b/indra/newview/llspeakingindicatormanager.cpp @@ -308,7 +308,10 @@ void LLSpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker void LLSpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator) { - SpeakingIndicatorManager::instance().unregisterSpeakingIndicator(speaker_id, speaking_indicator); + if(SpeakingIndicatorManager::instanceExists()) + { + SpeakingIndicatorManager::instance().unregisterSpeakingIndicator(speaker_id, speaking_indicator); + } } // EOF diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 5ee4599200..611f9de2e6 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -198,6 +198,7 @@ // exported globals // bool gAgentMovementCompleted = false; +S32 gMaxAgentGroups; std::string SCREEN_HOME_FILENAME = "screen_home.bmp"; std::string SCREEN_LAST_FILENAME = "screen_last.bmp"; @@ -232,6 +233,8 @@ static LLHost gFirstSim; static std::string gFirstSimSeedCap; static LLVector3 gAgentStartLookAt(1.0f, 0.f, 0.f); static std::string gAgentStartLocation = "safe"; +static bool mLoginStatePastUI = false; + boost::scoped_ptr<LLEventPump> LLStartUp::sStateWatcher(new LLEventStream("StartupState")); boost::scoped_ptr<LLStartupListener> LLStartUp::sListener(new LLStartupListener()); @@ -705,7 +708,15 @@ bool idle_startup() if (STATE_LOGIN_SHOW == LLStartUp::getStartupState()) { LL_DEBUGS("AppInit") << "Initializing Window" << LL_ENDL; - + + // if we've gone backwards in the login state machine, to this state where we show the UI + // AND the debug setting to exit in this case is true, then go ahead and bail quickly + if ( mLoginStatePastUI && gSavedSettings.getBOOL("QuitOnLoginActivated") ) + { + // no requirement for notification here - just exit + LLAppViewer::instance()->earlyExitNoNotify(); + } + gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW); timeout_count = 0; @@ -783,6 +794,11 @@ bool idle_startup() if (STATE_LOGIN_WAIT == LLStartUp::getStartupState()) { + // when we get to this state, we've already been past the login UI + // (possiblely automatically) - flag this so we can test in the + // STATE_LOGIN_SHOW state if we've gone backwards + mLoginStatePastUI = true; + // Don't do anything. Wait for the login view to call the login_callback, // which will push us to the next state. @@ -809,6 +825,11 @@ bool idle_startup() gKeyboard->resetKeys(); } + // when we get to this state, we've already been past the login UI + // (possiblely automatically) - flag this so we can test in the + // STATE_LOGIN_SHOW state if we've gone backwards + mLoginStatePastUI = true; + // save the credentials std::string userid = "unknown"; if(gUserCredential.notNull()) @@ -3074,7 +3095,16 @@ bool process_login_success_response() std::string map_server_url = response["map-server-url"]; if(!map_server_url.empty()) { - gSavedSettings.setString("MapServerURL", map_server_url); + // We got an answer from the grid -> use that for map for the current session + gSavedSettings.setString("CurrentMapServerURL", map_server_url); + LL_INFOS("LLStartup") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL; + } + else + { + // No answer from the grid -> use the default setting for current session + map_server_url = gSavedSettings.getString("MapServerURL"); + gSavedSettings.setString("CurrentMapServerURL", map_server_url); + LL_INFOS("LLStartup") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL; } // Default male and female avatars allowing the user to choose their avatar on first login. @@ -3151,6 +3181,18 @@ bool process_login_success_response() LLViewerMedia::openIDSetup(openid_url, openid_token); } + if(response.has("max-agent-groups")) { + std::string max_agent_groups(response["max-agent-groups"]); + gMaxAgentGroups = atoi(max_agent_groups.c_str()); + LL_INFOS("LLStartup") << "gMaxAgentGroups read from login.cgi: " + << gMaxAgentGroups << LL_ENDL; + } + else { + gMaxAgentGroups = DEFAULT_MAX_AGENT_GROUPS; + LL_INFOS("LLStartup") << "using gMaxAgentGroups default: " + << gMaxAgentGroups << LL_ENDL; + } + bool success = false; // JC: gesture loading done below, when we have an asset system // in place. Don't delete/clear gUserCredentials until then. diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index be1043cf91..b3d9ef1dcc 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -70,6 +70,7 @@ typedef enum { // exported symbols extern bool gAgentMovementCompleted; +extern S32 gMaxAgentGroups; extern LLPointer<LLViewerTexture> gStartTexture; class LLStartUp diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index e9fc25404a..1b8be7a5b2 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -115,6 +115,7 @@ LLStatusBar::LLStatusBar(const LLRect& rect) mSGBandwidth(NULL), mSGPacketLoss(NULL), mBtnVolume(NULL), + mBoxBalance(NULL), mBalance(0), mHealth(100), mSquareMetersCredit(0), @@ -168,6 +169,9 @@ BOOL LLStatusBar::postBuild() getChild<LLUICtrl>("buyL")->setCommitCallback( boost::bind(&LLStatusBar::onClickBuyCurrency, this)); + mBoxBalance = getChild<LLTextBox>("balance"); + mBoxBalance->setClickedCallback( &LLStatusBar::onClickBalance, this ); + mBtnVolume = getChild<LLButton>( "volume_btn" ); mBtnVolume->setClickedCallback( onClickVolume, this ); mBtnVolume->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterVolume, this)); @@ -304,6 +308,7 @@ void LLStatusBar::setVisibleForMouselook(bool visible) { mTextTime->setVisible(visible); getChild<LLUICtrl>("balance_bg")->setVisible(visible); + mBoxBalance->setVisible(visible); mBtnVolume->setVisible(visible); mMediaToggle->setVisible(visible); mSGBandwidth->setVisible(visible); @@ -330,16 +335,15 @@ void LLStatusBar::setBalance(S32 balance) std::string money_str = LLResMgr::getInstance()->getMonetaryString( balance ); - LLTextBox* balance_box = getChild<LLTextBox>("balance"); LLStringUtil::format_map_t string_args; string_args["[AMT]"] = llformat("%s", money_str.c_str()); std::string label_str = getString("buycurrencylabel", string_args); - balance_box->setValue(label_str); + mBoxBalance->setValue(label_str); // Resize the L$ balance background to be wide enough for your balance plus the buy button { const S32 HPAD = 24; - LLRect balance_rect = balance_box->getTextBoundingRect(); + LLRect balance_rect = mBoxBalance->getTextBoundingRect(); LLRect buy_rect = getChildView("buyL")->getRect(); LLView* balance_bg_view = getChildView("balance_bg"); LLRect balance_bg_rect = balance_bg_view->getRect(); @@ -506,6 +510,14 @@ static void onClickVolume(void* data) } //static +void LLStatusBar::onClickBalance(void* ) +{ + // Force a balance request message: + LLStatusBar::sendMoneyBalanceRequest(); + // The refresh of the display (call to setBalance()) will be done by process_money_balance_reply() +} + +//static void LLStatusBar::onClickMediaToggle(void* data) { LLStatusBar *status_bar = (LLStatusBar*)data; diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h index 2388aeb0c8..4ea3183d18 100644 --- a/indra/newview/llstatusbar.h +++ b/indra/newview/llstatusbar.h @@ -94,6 +94,7 @@ private: void onClickScreen(S32 x, S32 y); static void onClickMediaToggle(void* data); + static void onClickBalance(void* data); private: LLTextBox *mTextTime; @@ -102,6 +103,7 @@ private: LLStatGraph *mSGPacketLoss; LLButton *mBtnVolume; + LLTextBox *mBoxBalance; LLButton *mMediaToggle; LLView* mScriptOut; LLFrameTimer mClockUpdateTimer; diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp index af4d9fa7b9..6fc8153b77 100644 --- a/indra/newview/llsurface.cpp +++ b/indra/newview/llsurface.cpp @@ -1162,8 +1162,13 @@ void LLSurface::setWaterHeight(F32 height) if (!mWaterObjp.isNull()) { LLVector3 water_pos_region = mWaterObjp->getPositionRegion(); + bool changed = water_pos_region.mV[VZ] != height; water_pos_region.mV[VZ] = height; mWaterObjp->setPositionRegion(water_pos_region); + if (changed) + { + LLWorld::getInstance()->updateWaterObjects(); + } } else { diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 328298bda4..56e9739350 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -564,25 +564,27 @@ void LLFloaterTexturePicker::draw() LLRect interior = border; interior.stretch( -1 ); + // If the floater is focused, don't apply its alpha to the texture (STORM-677). + const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); if( mTexturep ) { if( mTexturep->getComponents() == 4 ) { - gl_rect_2d_checkerboard( interior ); + gl_rect_2d_checkerboard( interior, alpha ); } - gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep ); + gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha ); // Pump the priority mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) ); } else if (!mFallbackImage.isNull()) { - mFallbackImage->draw(interior); + mFallbackImage->draw(interior, UI_VERTEX_COLOR % alpha); } else { - gl_rect_2d( interior, LLColor4::grey, TRUE ); + gl_rect_2d( interior, LLColor4::grey % alpha, TRUE ); // Draw X gl_draw_x(interior, LLColor4::black ); @@ -1263,23 +1265,25 @@ void LLTextureCtrl::draw() LLRect interior = border; interior.stretch( -1 ); + // If we're in a focused floater, don't apply the floater's alpha to the texture (STORM-677). + const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); if( mTexturep ) { if( mTexturep->getComponents() == 4 ) { - gl_rect_2d_checkerboard( interior ); + gl_rect_2d_checkerboard( interior, alpha ); } - gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep); + gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha); mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) ); } else if (!mFallbackImage.isNull()) { - mFallbackImage->draw(interior); + mFallbackImage->draw(interior, UI_VERTEX_COLOR % alpha); } else { - gl_rect_2d( interior, LLColor4::grey, TRUE ); + gl_rect_2d( interior, LLColor4::grey % alpha, TRUE ); // Draw X gl_draw_x( interior, LLColor4::black ); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index fafef84aa2..4f63abb152 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include <iostream> +#include <map> #include "llstl.h" @@ -49,6 +50,7 @@ #include "llviewertexture.h" #include "llviewerregion.h" #include "llviewerstats.h" +#include "llviewerassetstats.h" #include "llworld.h" ////////////////////////////////////////////////////////////////////////////// @@ -143,7 +145,7 @@ public: /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) ~LLTextureFetchWorker(); - void relese() { --mActiveCount; } + // void relese() { --mActiveCount; } S32 callbackHttpGet(const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer, @@ -161,9 +163,11 @@ public: mGetReason = reason; } - void setCanUseHTTP(bool can_use_http) {mCanUseHTTP = can_use_http;} - bool getCanUseHTTP()const {return mCanUseHTTP ;} + void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; } + bool getCanUseHTTP() const { return mCanUseHTTP; } + LLTextureFetch & getFetcher() { return *mFetcher; } + protected: LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 discard, S32 size); @@ -277,6 +281,8 @@ private: S32 mLastPacket; U16 mTotalPackets; U8 mImageCodec; + + LLViewerAssetStats::duration_t mMetricsStartTime; }; ////////////////////////////////////////////////////////////////////////////// @@ -299,6 +305,7 @@ public: { static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); + static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; if (log_to_viewer_log || log_to_sim) { @@ -332,7 +339,29 @@ public: } S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success); + + if(log_texture_traffic && data_size > 0) + { + LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ; + if(tex) + { + gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; + } + } + mFetcher->removeFromHTTPQueue(mID, data_size); + + if (worker->mMetricsStartTime) + { + LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, + true, + LLImageBase::TYPE_AVATAR_BAKE == worker->mType, + LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime); + worker->mMetricsStartTime = 0; + } + LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, + true, + LLImageBase::TYPE_AVATAR_BAKE == worker->mType); } else { @@ -357,6 +386,229 @@ private: ////////////////////////////////////////////////////////////////////////////// +// Cross-thread messaging for asset metrics. + +/** + * @brief Base class for cross-thread requests made of the fetcher + * + * I believe the intent of the LLQueuedThread class was to + * have these operations derived from LLQueuedThread::QueuedRequest + * but the texture fetcher has elected to manage the queue + * in its own manner. So these are free-standing objects which are + * managed in simple FIFO order on the mCommands queue of the + * LLTextureFetch object. + * + * What each represents is a simple command sent from an + * outside thread into the TextureFetch thread to be processed + * in order and in a timely fashion (though not an absolute + * higher priority than other operations of the thread). + * Each operation derives a new class from the base customizing + * members, constructors and the doWork() method to effect + * the command. + * + * The flow is one-directional. There are two global instances + * of the LLViewerAssetStats collector, one for the main program's + * thread pointed to by gViewerAssetStatsMain and one for the + * TextureFetch thread pointed to by gViewerAssetStatsThread1. + * Common operations has each thread recording metrics events + * into the respective collector unconcerned with locking and + * the state of any other thread. But when the agent moves into + * a different region or the metrics timer expires and a report + * needs to be sent back to the grid, messaging across threads + * is required to distribute data and perform global actions. + * In pseudo-UML, it looks like: + * + * Main Thread1 + * . . + * . . + * +-----+ . + * | AM | . + * +--+--+ . + * +-------+ | . + * | Main | +--+--+ . + * | | | SRE |---. . + * | Stats | +-----+ \ . + * | | | \ (uuid) +-----+ + * | Coll. | +--+--+ `-------->| SR | + * +-------+ | MSC | +--+--+ + * | ^ +-----+ | + * | | (uuid) / . +-----+ (uuid) + * | `--------' . | MSC |---------. + * | . +-----+ | + * | +-----+ . v + * | | TE | . +-------+ + * | +--+--+ . | Thd1 | + * | | . | | + * | +-----+ . | Stats | + * `--------->| RSC | . | | + * +--+--+ . | Coll. | + * | . +-------+ + * +--+--+ . | + * | SME |---. . | + * +-----+ \ . | + * . \ (clone) +-----+ | + * . `-------->| SM | | + * . +--+--+ | + * . | | + * . +-----+ | + * . | RSC |<--------' + * . +-----+ + * . | + * . +-----+ + * . | CP |--> HTTP POST + * . +-----+ + * . . + * . . + * + * + * Key: + * + * SRE - Set Region Enqueued. Enqueue a 'Set Region' command in + * the other thread providing the new UUID of the region. + * TFReqSetRegion carries the data. + * SR - Set Region. New region UUID is sent to the thread-local + * collector. + * SME - Send Metrics Enqueued. Enqueue a 'Send Metrics' command + * including an ownership transfer of a cloned LLViewerAssetStats. + * TFReqSendMetrics carries the data. + * SM - Send Metrics. Global metrics reporting operation. Takes + * the cloned stats from the command, merges it with the + * thread's local stats, converts to LLSD and sends it on + * to the grid. + * AM - Agent Moved. Agent has completed some sort of move to a + * new region. + * TE - Timer Expired. Metrics timer has expired (on the order + * of 10 minutes). + * CP - CURL Post + * MSC - Modify Stats Collector. State change in the thread-local + * collector. Typically a region change which affects the + * global pointers used to find the 'current stats'. + * RSC - Read Stats Collector. Extract collector data cloning it + * (i.e. deep copy) when necessary. + * + */ +class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest +{ +public: + // Default ctors and assignment operator are correct. + + virtual ~TFRequest() + {} + + // Patterned after QueuedRequest's method but expected behavior + // is different. Always expected to complete on the first call + // and work dispatcher will assume the same and delete the + // request after invocation. + virtual bool doWork(LLTextureFetch * fetcher) = 0; +}; + +namespace +{ + +/** + * @brief Implements a 'Set Region' cross-thread command. + * + * When an agent moves to a new region, subsequent metrics need + * to be binned into a new or existing stats collection in 1:1 + * relationship with the region. We communicate this region + * change across the threads involved in the communication with + * this message. + * + * Corresponds to LLTextureFetch::commandSetRegion() + */ +class TFReqSetRegion : public LLTextureFetch::TFRequest +{ +public: + TFReqSetRegion(U64 region_handle) + : LLTextureFetch::TFRequest(), + mRegionHandle(region_handle) + {} + TFReqSetRegion & operator=(const TFReqSetRegion &); // Not defined + + virtual ~TFReqSetRegion() + {} + + virtual bool doWork(LLTextureFetch * fetcher); + +public: + const U64 mRegionHandle; +}; + + +/** + * @brief Implements a 'Send Metrics' cross-thread command. + * + * This is the big operation. The main thread gathers metrics + * for a period of minutes into LLViewerAssetStats and other + * objects then makes a snapshot of the data by cloning the + * collector. This command transfers the clone, along with a few + * additional arguments (UUIDs), handing ownership to the + * TextureFetch thread. It then merges its own data into the + * cloned copy, converts to LLSD and kicks off an HTTP POST of + * the resulting data to the currently active metrics collector. + * + * Corresponds to LLTextureFetch::commandSendMetrics() + */ +class TFReqSendMetrics : public LLTextureFetch::TFRequest +{ +public: + /** + * Construct the 'Send Metrics' command to have the TextureFetch + * thread add and log metrics data. + * + * @param caps_url URL of a "ViewerMetrics" Caps target + * to receive the data. Does not have to + * be associated with a particular region. + * + * @param session_id UUID of the agent's session. + * + * @param agent_id UUID of the agent. (Being pure here...) + * + * @param main_stats Pointer to a clone of the main thread's + * LLViewerAssetStats data. Thread1 takes + * ownership of the copy and disposes of it + * when done. + */ + TFReqSendMetrics(const std::string & caps_url, + const LLUUID & session_id, + const LLUUID & agent_id, + LLViewerAssetStats * main_stats) + : LLTextureFetch::TFRequest(), + mCapsURL(caps_url), + mSessionID(session_id), + mAgentID(agent_id), + mMainStats(main_stats) + {} + TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined + + virtual ~TFReqSendMetrics(); + + virtual bool doWork(LLTextureFetch * fetcher); + +public: + const std::string mCapsURL; + const LLUUID mSessionID; + const LLUUID mAgentID; + LLViewerAssetStats * mMainStats; +}; + +/* + * Examines the merged viewer metrics report and if found to be too long, + * will attempt to truncate it in some reasonable fashion. + * + * @param max_regions Limit of regions allowed in report. + * + * @param metrics Full, merged viewer metrics report. + * + * @returns If data was truncated, returns true. + */ +bool truncate_viewer_metrics(int max_regions, LLSD & metrics); + +} // end of anonymous namespace + + +////////////////////////////////////////////////////////////////////////////// + //static const char* LLTextureFetchWorker::sStateDescs[] = { "INVALID", @@ -374,6 +626,9 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "DONE", }; +// static +volatile bool LLTextureFetch::svMetricsDataBreak(true); // Start with a data break + // called from MAIN THREAD LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, @@ -423,7 +678,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mFirstPacket(0), mLastPacket(-1), mTotalPackets(0), - mImageCodec(IMG_CODEC_INVALID) + mImageCodec(IMG_CODEC_INVALID), + mMetricsStartTime(0) { mCanUseNET = mUrl.empty() ; @@ -591,6 +847,7 @@ bool LLTextureFetchWorker::doWork(S32 param) return true; // abort } } + if(mImagePriority < F_ALMOST_ZERO) { if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR) @@ -800,7 +1057,15 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedDiscard = mDesiredDiscard; mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); + if (! mMetricsStartTime) + { + mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, + false, + LLImageBase::TYPE_AVATAR_BAKE == mType); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + return false; } else @@ -809,6 +1074,12 @@ bool LLTextureFetchWorker::doWork(S32 param) //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); + //if (! mMetricsStartTime) + //{ + // mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + //} + //LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false, + // LLImageBase::TYPE_AVATAR_BAKE == mType); //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -832,11 +1103,30 @@ bool LLTextureFetchWorker::doWork(S32 param) } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = DECODE_IMAGE; - mWriteToCacheState = SHOULD_WRITE ; + mWriteToCacheState = SHOULD_WRITE; + + if (mMetricsStartTime) + { + LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, + false, + LLImageBase::TYPE_AVATAR_BAKE == mType, + LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); + mMetricsStartTime = 0; + } + LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, + false, + LLImageBase::TYPE_AVATAR_BAKE == mType); } else { mFetcher->addToNetworkQueue(this); // failsafe + if (! mMetricsStartTime) + { + mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, + false, + LLImageBase::TYPE_AVATAR_BAKE == mType); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); } return false; @@ -898,6 +1188,14 @@ bool LLTextureFetchWorker::doWork(S32 param) mState = WAIT_HTTP_REQ; mFetcher->addToHTTPQueue(mID); + if (! mMetricsStartTime) + { + mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, + true, + LLImageBase::TYPE_AVATAR_BAKE == mType); + // Will call callbackHttpGet when curl request completes std::vector<std::string> headers; headers.push_back("Accept: image/x-j2c"); @@ -1512,7 +1810,7 @@ bool LLTextureFetchWorker::writeToCacheComplete() ////////////////////////////////////////////////////////////////////////////// // public -LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded) +LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode) : LLWorkerThread("TextureFetch", threaded), mDebugCount(0), mDebugPause(FALSE), @@ -1524,8 +1822,10 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mImageDecodeThread(imagedecodethread), mTextureBandwidth(0), mHTTPTextureBits(0), - mCurlGetRequest(NULL) + mCurlGetRequest(NULL), + mQAMode(qa_mode) { + mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); } @@ -1534,6 +1834,13 @@ LLTextureFetch::~LLTextureFetch() { clearDeleteList() ; + while (! mCommands.empty()) + { + TFRequest * req(mCommands.front()); + mCommands.erase(mCommands.begin()); + delete req; + } + // ~LLQueuedThread() called here } @@ -1563,7 +1870,6 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)) { // Only do partial requests for J2C at the moment - //llinfos << "Merov : LLTextureFetch::createRequest(), blocking fetch on " << url << llendl; desired_size = MAX_IMAGE_DATA_SIZE; desired_discard = 0; } @@ -1815,8 +2121,76 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) return res; } +// Replicates and expands upon the base class's +// getPending() implementation. getPending() and +// runCondition() replicate one another's logic to +// an extent and are sometimes used for the same +// function (deciding whether or not to sleep/pause +// a thread). So the implementations need to stay +// in step, at least until this can be refactored and +// the redundancy eliminated. +// +// May be called from any thread + +//virtual +S32 LLTextureFetch::getPending() +{ + S32 res; + lockData(); + { + LLMutexLock lock(&mQueueMutex); + + res = mRequestQueue.size(); + res += mCurlPOSTRequestCount; + res += mCommands.size(); + } + unlockData(); + return res; +} + +// virtual +bool LLTextureFetch::runCondition() +{ + // Caller is holding the lock on LLThread's condition variable. + + // LLQueuedThread, unlike its base class LLThread, makes this a + // private method which is unfortunate. I want to use it directly + // but I'm going to have to re-implement the logic here (or change + // declarations, which I don't want to do right now). + // + // Changes here may need to be reflected in getPending(). + + bool have_no_commands(false); + { + LLMutexLock lock(&mQueueMutex); + + have_no_commands = mCommands.empty(); + } + + bool have_no_curl_requests(0 == mCurlPOSTRequestCount); + + return ! (have_no_commands + && have_no_curl_requests + && (mRequestQueue.empty() && mIdleThread)); // From base class +} + ////////////////////////////////////////////////////////////////////////////// +// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs) +void LLTextureFetch::commonUpdate() +{ + // Run a cross-thread command, if any. + cmdDoWork(); + + // Update Curl on same thread as mCurlGetRequest was constructed + S32 processed = mCurlGetRequest->process(); + if (processed > 0) + { + lldebugs << "processed: " << processed << " messages." << llendl; + } +} + + // MAIN THREAD //virtual S32 LLTextureFetch::update(U32 max_time_ms) @@ -1842,12 +2216,7 @@ S32 LLTextureFetch::update(U32 max_time_ms) if (!mThreaded) { - // Update Curl on same thread as mCurlGetRequest was constructed - S32 processed = mCurlGetRequest->process(); - if (processed > 0) - { - lldebugs << "processed: " << processed << " messages." << llendl; - } + commonUpdate(); } return res; @@ -1902,12 +2271,7 @@ void LLTextureFetch::threadedUpdate() } process_timer.reset(); - // Update Curl on same thread as mCurlGetRequest was constructed - S32 processed = mCurlGetRequest->process(); - if (processed > 0) - { - lldebugs << "processed: " << processed << " messages." << llendl; - } + commonUpdate(); #if 0 const F32 INFO_TIME = 1.0f; @@ -2357,3 +2721,280 @@ void LLTextureFetch::dump() } } +////////////////////////////////////////////////////////////////////////////// + +// cross-thread command methods + +void LLTextureFetch::commandSetRegion(U64 region_handle) +{ + TFReqSetRegion * req = new TFReqSetRegion(region_handle); + + cmdEnqueue(req); +} + +void LLTextureFetch::commandSendMetrics(const std::string & caps_url, + const LLUUID & session_id, + const LLUUID & agent_id, + LLViewerAssetStats * main_stats) +{ + TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats); + + cmdEnqueue(req); +} + +void LLTextureFetch::commandDataBreak() +{ + // The pedantically correct way to implement this is to create a command + // request object in the above fashion and enqueue it. However, this is + // simple data of an advisorial not operational nature and this case + // of shared-write access is tolerable. + + LLTextureFetch::svMetricsDataBreak = true; +} + +void LLTextureFetch::cmdEnqueue(TFRequest * req) +{ + lockQueue(); + mCommands.push_back(req); + unlockQueue(); + + unpause(); +} + +LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue() +{ + TFRequest * ret = 0; + + lockQueue(); + if (! mCommands.empty()) + { + ret = mCommands.front(); + mCommands.erase(mCommands.begin()); + } + unlockQueue(); + + return ret; +} + +void LLTextureFetch::cmdDoWork() +{ + if (mDebugPause) + { + return; // debug: don't do any work + } + + TFRequest * req = cmdDequeue(); + if (req) + { + // One request per pass should really be enough for this. + req->doWork(this); + delete req; + } +} + + +////////////////////////////////////////////////////////////////////////////// + +// Private (anonymous) class methods implementing the command scheme. + +namespace +{ + +/** + * Implements the 'Set Region' command. + * + * Thread: Thread1 (TextureFetch) + */ +bool +TFReqSetRegion::doWork(LLTextureFetch *) +{ + LLViewerAssetStatsFF::set_region_thread1(mRegionHandle); + + return true; +} + + +TFReqSendMetrics::~TFReqSendMetrics() +{ + delete mMainStats; + mMainStats = 0; +} + + +/** + * Implements the 'Send Metrics' command. Takes over + * ownership of the passed LLViewerAssetStats pointer. + * + * Thread: Thread1 (TextureFetch) + */ +bool +TFReqSendMetrics::doWork(LLTextureFetch * fetcher) +{ + /* + * HTTP POST responder. Doesn't do much but tries to + * detect simple breaks in recording the metrics stream. + * + * The 'volatile' modifiers don't indicate signals, + * mmap'd memory or threads, really. They indicate that + * the referenced data is part of a pseudo-closure for + * this responder rather than being required for correct + * operation. + * + * We don't try very hard with the POST request. We give + * it one shot and that's more-or-less it. With a proper + * refactoring of the LLQueuedThread usage, these POSTs + * could be put in a request object and made more reliable. + */ + class lcl_responder : public LLCurl::Responder + { + public: + lcl_responder(LLTextureFetch * fetcher, + S32 expected_sequence, + volatile const S32 & live_sequence, + volatile bool & reporting_break, + volatile bool & reporting_started) + : LLCurl::Responder(), + mFetcher(fetcher), + mExpectedSequence(expected_sequence), + mLiveSequence(live_sequence), + mReportingBreak(reporting_break), + mReportingStarted(reporting_started) + { + mFetcher->incrCurlPOSTCount(); + } + + ~lcl_responder() + { + mFetcher->decrCurlPOSTCount(); + } + + // virtual + void error(U32 status_num, const std::string & reason) + { + if (mLiveSequence == mExpectedSequence) + { + mReportingBreak = true; + } + LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service. Reason: " + << reason << LL_ENDL; + } + + // virtual + void result(const LLSD & content) + { + if (mLiveSequence == mExpectedSequence) + { + mReportingBreak = false; + mReportingStarted = true; + } + } + + private: + LLTextureFetch * mFetcher; + S32 mExpectedSequence; + volatile const S32 & mLiveSequence; + volatile bool & mReportingBreak; + volatile bool & mReportingStarted; + + }; // class lcl_responder + + if (! gViewerAssetStatsThread1) + return true; + + static volatile bool reporting_started(false); + static volatile S32 report_sequence(0); + + // We've taken over ownership of the stats copy at this + // point. Get a working reference to it for merging here + // but leave it in 'this'. Destructor will rid us of it. + LLViewerAssetStats & main_stats = *mMainStats; + + // Merge existing stats into those from main, convert to LLSD + main_stats.merge(*gViewerAssetStatsThread1); + LLSD merged_llsd = main_stats.asLLSD(true); + + // Add some additional meta fields to the content + merged_llsd["session_id"] = mSessionID; + merged_llsd["agent_id"] = mAgentID; + merged_llsd["message"] = "ViewerAssetMetrics"; // Identifies the type of metrics + merged_llsd["sequence"] = report_sequence; // Sequence number + merged_llsd["initial"] = ! reporting_started; // Initial data from viewer + merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak; // Break in data prior to this report + + // Update sequence number + if (S32_MAX == ++report_sequence) + report_sequence = 0; + + // Limit the size of the stats report if necessary. + merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd); + + if (! mCapsURL.empty()) + { + LLCurlRequest::headers_t headers; + fetcher->getCurlRequest().post(mCapsURL, + headers, + merged_llsd, + new lcl_responder(fetcher, + report_sequence, + report_sequence, + LLTextureFetch::svMetricsDataBreak, + reporting_started)); + } + else + { + LLTextureFetch::svMetricsDataBreak = true; + } + + // In QA mode, Metrics submode, log the result for ease of testing + if (fetcher->isQAMode()) + { + LL_INFOS("Textures") << merged_llsd << LL_ENDL; + } + + gViewerAssetStatsThread1->reset(); + + return true; +} + + +bool +truncate_viewer_metrics(int max_regions, LLSD & metrics) +{ + static const LLSD::String reg_tag("regions"); + static const LLSD::String duration_tag("duration"); + + LLSD & reg_map(metrics[reg_tag]); + if (reg_map.size() <= max_regions) + { + return false; + } + + // Build map of region hashes ordered by duration + typedef std::multimap<LLSD::Real, int> reg_ordered_list_t; + reg_ordered_list_t regions_by_duration; + + int ind(0); + LLSD::array_const_iterator it_end(reg_map.endArray()); + for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind) + { + LLSD::Real duration = (*it)[duration_tag].asReal(); + regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind)); + } + + // Build a replacement regions array with the longest-persistence regions + LLSD new_region(LLSD::emptyArray()); + reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend()); + reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin()); + for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2) + { + new_region.append(reg_map[it2->second]); + } + reg_map = new_region; + + return true; +} + +} // end of anonymous namespace + + + diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 796109df06..ad00a7ea36 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -33,6 +33,7 @@ #include "llworkerthread.h" #include "llcurl.h" #include "lltextureinfo.h" +#include "llapr.h" class LLViewerTexture; class LLTextureFetchWorker; @@ -40,6 +41,7 @@ class HTTPGetResponder; class LLTextureCache; class LLImageDecodeThread; class LLHost; +class LLViewerAssetStats; // Interface class class LLTextureFetch : public LLWorkerThread @@ -48,9 +50,11 @@ class LLTextureFetch : public LLWorkerThread friend class HTTPGetResponder; public: - LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded); + LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode); ~LLTextureFetch(); + class TFRequest; + /*virtual*/ S32 update(U32 max_time_ms); void shutDownTextureCacheThread() ; //called in the main thread after the TextureCacheThread shuts down. void shutDownImageDecodeThread() ; //called in the main thread after the ImageDecodeThread shuts down. @@ -77,28 +81,77 @@ public: S32 getNumHTTPRequests() ; // Public for access by callbacks + S32 getPending(); void lockQueue() { mQueueMutex.lock(); } void unlockQueue() { mQueueMutex.unlock(); } LLTextureFetchWorker* getWorker(const LLUUID& id); LLTextureFetchWorker* getWorkerAfterLock(const LLUUID& id); LLTextureInfo* getTextureInfo() { return &mTextureInfo; } - + + // Commands available to other threads to control metrics gathering operations. + void commandSetRegion(U64 region_handle); + void commandSendMetrics(const std::string & caps_url, + const LLUUID & session_id, + const LLUUID & agent_id, + LLViewerAssetStats * main_stats); + void commandDataBreak(); + + LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; } + + bool isQAMode() const { return mQAMode; } + + // Curl POST counter maintenance + inline void incrCurlPOSTCount() { mCurlPOSTRequestCount++; } + inline void decrCurlPOSTCount() { mCurlPOSTRequestCount--; } + protected: void addToNetworkQueue(LLTextureFetchWorker* worker); void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); void addToHTTPQueue(const LLUUID& id); void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0); void removeRequest(LLTextureFetchWorker* worker, bool cancel); - // Called from worker thread (during doWork) - void processCurlRequests(); + + // Overrides from the LLThread tree + bool runCondition(); private: void sendRequestListToSimulators(); /*virtual*/ void startThread(void); /*virtual*/ void endThread(void); /*virtual*/ void threadedUpdate(void); - + void commonUpdate(); + + // Metrics command helpers + /** + * Enqueues a command request at the end of the command queue + * and wakes up the thread as needed. + * + * Takes ownership of the TFRequest object. + * + * Method locks the command queue. + */ + void cmdEnqueue(TFRequest *); + + /** + * Returns the first TFRequest object in the command queue or + * NULL if none is present. + * + * Caller acquires ownership of the object and must dispose of it. + * + * Method locks the command queue. + */ + TFRequest * cmdDequeue(); + + /** + * Processes the first command in the queue disposing of the + * request on completion. Successive calls are needed to perform + * additional commands. + * + * Method locks the command queue. + */ + void cmdDoWork(); + public: LLUUID mDebugID; S32 mDebugCount; @@ -107,7 +160,7 @@ public: S32 mBadPacketCount; private: - LLMutex mQueueMutex; //to protect mRequestMap only + LLMutex mQueueMutex; //to protect mRequestMap and mCommands only LLMutex mNetworkQueueMutex; //to protect mNetworkQueue, mHTTPTextureQueue and mCancelQueue. LLTextureCache* mTextureCache; @@ -129,6 +182,29 @@ private: LLTextureInfo mTextureInfo; U32 mHTTPTextureBits; + + // Out-of-band cross-thread command queue. This command queue + // is logically tied to LLQueuedThread's list of + // QueuedRequest instances and so must be covered by the + // same locks. + typedef std::vector<TFRequest *> command_queue_t; + command_queue_t mCommands; + + // If true, modifies some behaviors that help with QA tasks. + const bool mQAMode; + + // Count of POST requests outstanding. We maintain the count + // indirectly in the CURL request responder's ctor and dtor and + // use it when determining whether or not to sleep the thread. Can't + // use the LLCurl module's request counter as it isn't thread compatible. + // *NOTE: Don't mix Atomic and static, apr_initialize must be called first. + LLAtomic32<S32> mCurlPOSTRequestCount; + +public: + // A probabilistically-correct indicator that the current + // attempt to log metrics follows a break in the metrics stream + // reporting due to either startup or a problem POSTing data. + static volatile bool svMetricsDataBreak; }; #endif // LL_LLTEXTUREFETCH_H diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index c87aff022f..b9a15fd1f4 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -514,7 +514,8 @@ void LLGLTexMemBar::draw() F32 cache_max_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getMaxUsage()) ; S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f); S32 v_offset = (S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f); - S32 total_downloaded = BYTES_TO_MEGA_BYTES(gTotalTextureBytes); + F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024); + F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024); //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); @@ -525,13 +526,13 @@ void LLGLTexMemBar::draw() LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6, text_color, LLFontGL::LEFT, LLFontGL::TOP); - text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot: %d MB", + text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB", total_mem, max_total_mem, bound_mem, max_bound_mem, LLImageRaw::sGlobalRawMemory >> 20, discard_bias, - cache_usage, cache_max_usage, total_downloaded); + cache_usage, cache_max_usage, total_texture_downloaded, total_object_downloaded); //, cache_entries, cache_max_entries LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index c3090cb1fc..fd5582d6f7 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -35,6 +35,13 @@ using namespace LLNotificationsUI; +//-------------------------------------------------------------------------- +LLToastLifeTimer::LLToastLifeTimer(LLToast* toast, F32 period) + : mToast(toast), + LLEventTimer(period) +{ +} + /*virtual*/ BOOL LLToastLifeTimer::tick() { @@ -45,6 +52,38 @@ BOOL LLToastLifeTimer::tick() return FALSE; } +void LLToastLifeTimer::stop() +{ + mEventTimer.stop(); +} + +void LLToastLifeTimer::start() +{ + mEventTimer.start(); +} + +void LLToastLifeTimer::restart() +{ + mEventTimer.reset(); +} + +BOOL LLToastLifeTimer::getStarted() +{ + return mEventTimer.getStarted(); +} + +void LLToastLifeTimer::setPeriod(F32 period) +{ + mPeriod = period; +} + +F32 LLToastLifeTimer::getRemainingTimeF32() +{ + F32 et = mEventTimer.getElapsedTimeF32(); + if (!getStarted() || et > mPeriod) return 0.0f; + return mPeriod - et; +} + //-------------------------------------------------------------------------- LLToast::Params::Params() : can_fade("can_fade", true), @@ -73,7 +112,8 @@ LLToast::LLToast(const LLToast::Params& p) mIsHidden(false), mHideBtnPressed(false), mIsTip(p.is_tip), - mWrapperPanel(NULL) + mWrapperPanel(NULL), + mIsFading(false) { mTimer.reset(new LLToastLifeTimer(this, p.lifetime_secs)); @@ -85,6 +125,9 @@ LLToast::LLToast(const LLToast::Params& p) mWrapperPanel->setMouseEnterCallback(boost::bind(&LLToast::onToastMouseEnter, this)); mWrapperPanel->setMouseLeaveCallback(boost::bind(&LLToast::onToastMouseLeave, this)); + setBackgroundOpaque(TRUE); // *TODO: obsolete + updateTransparency(); + if(mPanel) { insertPanel(mPanel); @@ -101,10 +144,6 @@ LLToast::LLToast(const LLToast::Params& p) // init callbacks if present if(!p.on_delete_toast().empty()) mOnDeleteToastSignal.connect(p.on_delete_toast()); - - // *TODO: This signal doesn't seem to be used at all. - if(!p.on_mouse_enter().empty()) - mOnMouseEnterSignal.connect(p.on_mouse_enter()); } void LLToast::reshape(S32 width, S32 height, BOOL called_from_parent) @@ -143,6 +182,7 @@ LLToast::~LLToast() void LLToast::hide() { setVisible(FALSE); + setFading(false); mTimer->stop(); mIsHidden = true; mOnFadeSignal(this); @@ -153,7 +193,7 @@ void LLToast::onFocusLost() if(mWrapperPanel && !isBackgroundVisible()) { // Lets make wrapper panel behave like a floater - setBackgroundOpaque(FALSE); + updateTransparency(); } } @@ -162,10 +202,20 @@ void LLToast::onFocusReceived() if(mWrapperPanel && !isBackgroundVisible()) { // Lets make wrapper panel behave like a floater - setBackgroundOpaque(TRUE); + updateTransparency(); } } +void LLToast::setLifetime(S32 seconds) +{ + mToastLifetime = seconds; +} + +void LLToast::setFadingTime(S32 seconds) +{ + mToastFadingTime = seconds; +} + S32 LLToast::getTopPad() { if(mWrapperPanel) @@ -195,13 +245,48 @@ void LLToast::setCanFade(bool can_fade) //-------------------------------------------------------------------------- void LLToast::expire() { - // if toast has fade property - hide it - if(mCanFade) + if (mCanFade) { - hide(); + if (mIsFading) + { + // Fade timer expired. Time to hide. + hide(); + } + else + { + // "Life" time has ended. Time to fade. + setFading(true); + mTimer->restart(); + } + } +} + +void LLToast::setFading(bool transparent) +{ + mIsFading = transparent; + updateTransparency(); + + if (transparent) + { + mTimer->setPeriod(mToastFadingTime); + } + else + { + mTimer->setPeriod(mToastLifetime); } } +F32 LLToast::getTimeLeftToLive() +{ + F32 time_to_live = mTimer->getRemainingTimeF32(); + + if (!mIsFading) + { + time_to_live += mToastFadingTime; + } + + return time_to_live; +} //-------------------------------------------------------------------------- void LLToast::reshapeToPanel() @@ -245,13 +330,6 @@ void LLToast::draw() drawChild(mHideBtn); } } - - // if timer started and remaining time <= fading time - if (mTimer->getStarted() && (mToastLifetime - - mTimer->getEventTimer().getElapsedTimeF32()) <= mToastFadingTime) - { - setBackgroundOpaque(FALSE); - } } //-------------------------------------------------------------------------- @@ -267,9 +345,13 @@ void LLToast::setVisible(BOOL show) return; } + if (show && getVisible()) + { + return; + } + if(show) { - setBackgroundOpaque(TRUE); if(!mTimer->getStarted() && mCanFade) { mTimer->start(); @@ -311,7 +393,7 @@ void LLToast::onToastMouseEnter() { mOnToastHoverSignal(this, MOUSE_ENTER); - setBackgroundOpaque(TRUE); + updateTransparency(); //toasts fading is management by Screen Channel @@ -320,7 +402,6 @@ void LLToast::onToastMouseEnter() { mHideBtn->setVisible(TRUE); } - mOnMouseEnterSignal(this); mToastMouseEnterSignal(this, getValue()); } } @@ -341,6 +422,8 @@ void LLToast::onToastMouseLeave() { mOnToastHoverSignal(this, MOUSE_LEAVE); + updateTransparency(); + //toasts fading is management by Screen Channel if(mHideBtn && mHideBtn->getEnabled()) @@ -368,19 +451,46 @@ void LLToast::setBackgroundOpaque(BOOL b) } } -void LLNotificationsUI::LLToast::stopFading() +void LLToast::updateTransparency() +{ + ETypeTransparency transparency_type; + + if (mCanFade) + { + // Notification toasts (including IM/chat toasts) change their transparency on hover. + if (isHovered()) + { + transparency_type = TT_ACTIVE; + } + else + { + transparency_type = mIsFading ? TT_FADING : TT_INACTIVE; + } + } + else + { + // Transparency of alert toasts depends on focus. + transparency_type = hasFocus() ? TT_ACTIVE : TT_INACTIVE; + } + + LLFloater::updateTransparency(transparency_type); +} + +void LLNotificationsUI::LLToast::stopTimer() { if(mCanFade) { - stopTimer(); + setFading(false); + mTimer->stop(); } } -void LLNotificationsUI::LLToast::startFading() +void LLNotificationsUI::LLToast::startTimer() { if(mCanFade) { - resetTimer(); + setFading(false); + mTimer->start(); } } diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h index 0a96c092a0..242f786bf2 100644 --- a/indra/newview/lltoast.h +++ b/indra/newview/lltoast.h @@ -49,14 +49,16 @@ class LLToast; class LLToastLifeTimer: public LLEventTimer { public: - LLToastLifeTimer(LLToast* toast, F32 period) : mToast(toast), LLEventTimer(period){} + LLToastLifeTimer(LLToast* toast, F32 period); /*virtual*/ BOOL tick(); - void stop() { mEventTimer.stop(); } - void start() { mEventTimer.start(); } - void restart() {mEventTimer.reset(); } - BOOL getStarted() { return mEventTimer.getStarted(); } + void stop(); + void start(); + void restart(); + BOOL getStarted(); + void setPeriod(F32 period); + F32 getRemainingTimeF32(); LLTimer& getEventTimer() { return mEventTimer;} private : @@ -80,10 +82,15 @@ public: Optional<LLUUID> notif_id, //notification ID session_id; //im session ID Optional<LLNotificationPtr> notification; - Optional<F32> lifetime_secs, - fading_time_secs; // Number of seconds while a toast is fading - Optional<toast_callback_t> on_delete_toast, - on_mouse_enter; + + //NOTE: Life time of a toast (i.e. period of time from the moment toast was shown + //till the moment when toast was hidden) is the sum of lifetime_secs and fading_time_secs. + + Optional<F32> lifetime_secs, // Number of seconds while a toast is non-transparent + fading_time_secs; // Number of seconds while a toast is transparent + + + Optional<toast_callback_t> on_delete_toast; Optional<bool> can_fade, can_be_stored, enable_hide_btn, @@ -107,11 +114,11 @@ public: //Fading - /** Stop fading timer */ - virtual void stopFading(); + /** Stop lifetime/fading timer */ + virtual void stopTimer(); - /** Start fading timer */ - virtual void startFading(); + /** Start lifetime/fading timer */ + virtual void startTimer(); bool isHovered(); @@ -125,10 +132,8 @@ public: LLPanel* getPanel() { return mPanel; } // enable/disable Toast's Hide button void setHideButtonEnabled(bool enabled); - // - void resetTimer() { mTimer->start(); } // - void stopTimer() { mTimer->stop(); } + F32 getTimeLeftToLive(); // LLToastLifeTimer* getTimer() { return mTimer.get();} // @@ -144,6 +149,10 @@ public: /*virtual*/ void onFocusReceived(); + void setLifetime(S32 seconds); + + void setFadingTime(S32 seconds); + /** * Returns padding between floater top and wrapper_panel top. * This padding should be taken into account when positioning or reshaping toasts @@ -172,7 +181,6 @@ public: // Registers signals/callbacks for events toast_signal_t mOnFadeSignal; - toast_signal_t mOnMouseEnterSignal; toast_signal_t mOnDeleteToastSignal; toast_signal_t mOnToastDestroyedSignal; boost::signals2::connection setOnFadeCallback(toast_callback_t cb) { return mOnFadeSignal.connect(cb); } @@ -190,13 +198,18 @@ public: LLHandle<LLToast> getHandle() { mHandle.bind(this); return mHandle; } +protected: + void updateTransparency(); + private: void onToastMouseEnter(); void onToastMouseLeave(); - void expire(); + void expire(); + + void setFading(bool fading); LLUUID mNotificationID; LLUUID mSessionID; @@ -222,6 +235,7 @@ private: bool mHideBtnPressed; bool mIsHidden; // this flag is TRUE when a toast has faded or was hidden with (x) button (EXT-1849) bool mIsTip; + bool mIsFading; commit_signal_t mToastMouseEnterSignal; commit_signal_t mToastMouseLeaveSignal; diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 371aad64bb..75178a6ef8 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -60,7 +60,7 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification LLGroupData groupData; if (!gAgent.getGroupData(payload["group_id"].asUUID(),groupData)) { - llwarns << "Group notice for unkown group: " << payload["group_id"].asUUID() << llendl; + llwarns << "Group notice for unknown group: " << payload["group_id"].asUUID() << llendl; } //group icon @@ -77,6 +77,7 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification from << from_name << "/" << groupData.mName; LLTextBox* pTitleText = getChild<LLTextBox>("title"); pTitleText->setValue(from.str()); + pTitleText->setToolTip(from.str()); //message subject const std::string& subject = payload["subject"].asString(); diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index 9017f5ec55..3f7dc24ade 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -33,6 +33,7 @@ // library includes #include "lldbstrings.h" +#include "lllslconstants.h" #include "llnotifications.h" #include "lluiconstants.h" #include "llrect.h" @@ -70,11 +71,11 @@ mCloseNotificationOnDestroy(true) mControlPanel = getChild<LLPanel>("control_panel"); BUTTON_WIDTH = gSavedSettings.getS32("ToastButtonWidth"); // customize panel's attributes - // is it intended for displaying a tip + // is it intended for displaying a tip? mIsTip = notification->getType() == "notifytip"; - // is it a script dialog + // is it a script dialog? mIsScriptDialog = (notification->getName() == "ScriptDialog" || notification->getName() == "ScriptDialogGroup"); - // is it a caution + // is it a caution? // // caution flag can be set explicitly by specifying it in the notification payload, or it can be set implicitly if the // notify xml template specifies that it is a caution @@ -139,6 +140,12 @@ mCloseNotificationOnDestroy(true) LLSD form_element = form->getElement(i); if (form_element["type"].asString() != "button") { + // not a button. + continue; + } + if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN) + { + // a textbox pretending to be a button. continue; } LLButton* new_button = createButton(form_element, TRUE); @@ -159,7 +166,7 @@ mCloseNotificationOnDestroy(true) if(h_pad < 2*HPAD) { /* - * Probably it is a scriptdialog toast + * Probably it is a scriptdialog toast * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons. * In last case set default h_pad to avoid heaping of buttons */ @@ -261,7 +268,7 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt } else if (mIsScriptDialog && is_ignore_btn) { - // this is ignore button,make it smaller + // this is ignore button, make it smaller p.rect.height = BTN_HEIGHT_SMALL; p.rect.width = 1; p.auto_resize = true; diff --git a/indra/newview/lltoastscripttextbox.cpp b/indra/newview/lltoastscripttextbox.cpp new file mode 100644 index 0000000000..2529ec865a --- /dev/null +++ b/indra/newview/lltoastscripttextbox.cpp @@ -0,0 +1,121 @@ +/** + * @file lltoastscripttextbox.cpp + * @brief Panel for script llTextBox dialogs + * + * $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 "lltoastscripttextbox.h" + +#include "llfocusmgr.h" + +#include "llbutton.h" +#include "llnotifications.h" +#include "llviewertexteditor.h" + +#include "llavatarnamecache.h" +#include "lluiconstants.h" +#include "llui.h" +#include "llviewercontrol.h" +#include "lltrans.h" +#include "llstyle.h" + +#include "llglheaders.h" +#include "llagent.h" + +const S32 LLToastScriptTextbox::DEFAULT_MESSAGE_MAX_LINE_COUNT= 7; + +LLToastScriptTextbox::LLToastScriptTextbox(const LLNotificationPtr& notification) +: LLToastPanel(notification) +{ + buildFromFile( "panel_notify_textbox.xml"); + + LLTextEditor* text_editorp = getChild<LLTextEditor>("text_editor_box"); + text_editorp->setValue(notification->getMessage()); + + getChild<LLButton>("ignore_btn")->setClickedCallback(boost::bind(&LLToastScriptTextbox::onClickIgnore, this)); + + const LLSD& payload = notification->getPayload(); + + //message body + const std::string& message = payload["message"].asString(); + + LLViewerTextEditor* pMessageText = getChild<LLViewerTextEditor>("message"); + pMessageText->clear(); + + LLStyle::Params style; + style.font = pMessageText->getDefaultFont(); + pMessageText->appendText(message, TRUE, style); + + //submit button + LLButton* pSubmitBtn = getChild<LLButton>("btn_submit"); + pSubmitBtn->setClickedCallback((boost::bind(&LLToastScriptTextbox::onClickSubmit, this))); + setDefaultBtn(pSubmitBtn); + + S32 maxLinesCount; + std::istringstream ss( getString("message_max_lines_count") ); + if (!(ss >> maxLinesCount)) + { + maxLinesCount = DEFAULT_MESSAGE_MAX_LINE_COUNT; + } + //snapToMessageHeight(pMessageText, maxLinesCount); +} + +// virtual +LLToastScriptTextbox::~LLToastScriptTextbox() +{ +} + +void LLToastScriptTextbox::close() +{ + die(); +} + +#include "lllslconstants.h" +void LLToastScriptTextbox::onClickSubmit() +{ + LLViewerTextEditor* pMessageText = getChild<LLViewerTextEditor>("message"); + + if (pMessageText) + { + LLSD response = mNotification->getResponseTemplate(); + response[TEXTBOX_MAGIC_TOKEN] = pMessageText->getText(); + if (response[TEXTBOX_MAGIC_TOKEN].asString().empty()) + { + // so we can distinguish between a successfully + // submitted blank textbox, and an ignored toast + response[TEXTBOX_MAGIC_TOKEN] = true; + } + mNotification->respond(response); + close(); + llwarns << response << llendl; + } +} + +void LLToastScriptTextbox::onClickIgnore() +{ + LLSD response = mNotification->getResponseTemplate(); + mNotification->respond(response); + close(); +} diff --git a/indra/newview/lltoastscripttextbox.h b/indra/newview/lltoastscripttextbox.h new file mode 100644 index 0000000000..8e69d8834d --- /dev/null +++ b/indra/newview/lltoastscripttextbox.h @@ -0,0 +1,59 @@ +/** + * @file lltoastscripttextbox.h + * @brief Panel for script llTextBox dialogs + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLTOASTSCRIPTTEXTBOX_H +#define LL_LLTOASTSCRIPTTEXTBOX_H + +#include "lltoastnotifypanel.h" +#include "llnotificationptr.h" + +/** + * Toast panel for scripted llTextbox notifications. + */ +class LLToastScriptTextbox +: public LLToastPanel +{ +public: + void close(); + + static bool onNewNotification(const LLSD& notification); + + // Non-transient messages. You can specify non-default button + // layouts (like one for script dialogs) by passing various + // numbers in for "layout". + LLToastScriptTextbox(const LLNotificationPtr& notification); + + /*virtual*/ ~LLToastScriptTextbox(); + +private: + + void onClickSubmit(); + void onClickIgnore(); + + static const S32 DEFAULT_MESSAGE_MAX_LINE_COUNT; +}; + +#endif diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp index 282d4e19c6..2d8ce95347 100644 --- a/indra/newview/lltool.cpp +++ b/indra/newview/lltool.cpp @@ -33,6 +33,7 @@ #include "llview.h" #include "llviewerwindow.h" +#include "llviewercontrol.h" #include "lltoolcomp.h" #include "lltoolfocus.h" #include "llfocusmgr.h" @@ -190,9 +191,12 @@ LLTool* LLTool::getOverrideTool(MASK mask) { return NULL; } - if (mask & MASK_ALT) + if (gSavedSettings.getBOOL("EnableAltZoom")) { - return LLToolCamera::getInstance(); + if (mask & MASK_ALT) + { + return LLToolCamera::getInstance(); + } } return NULL; } diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp index ca80a1db79..964b17d3a6 100644 --- a/indra/newview/lltoolmorph.cpp +++ b/indra/newview/lltoolmorph.cpp @@ -244,13 +244,13 @@ BOOL LLVisualParamHint::render() //----------------------------------------------------------------------------- // draw() //----------------------------------------------------------------------------- -void LLVisualParamHint::draw() +void LLVisualParamHint::draw(F32 alpha) { if (!mIsVisible) return; gGL.getTexUnit(0)->bind(this); - gGL.color4f(1.f, 1.f, 1.f, 1.f); + gGL.color4f(1.f, 1.f, 1.f, alpha); LLGLSUIDefault gls_ui; gGL.begin(LLRender::QUADS); diff --git a/indra/newview/lltoolmorph.h b/indra/newview/lltoolmorph.h index 59201233ae..a6889be151 100644 --- a/indra/newview/lltoolmorph.h +++ b/indra/newview/lltoolmorph.h @@ -68,7 +68,7 @@ public: BOOL render(); void requestUpdate( S32 delay_frames ) {mNeedsUpdate = TRUE; mDelayFrames = delay_frames; } void setUpdateDelayFrames( S32 delay_frames ) { mDelayFrames = delay_frames; } - void draw(); + void draw(F32 alpha); LLViewerVisualParam* getVisualParam() { return mVisualParam; } F32 getVisualParamWeight() { return mVisualParamWeight; } diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index d992d808c7..562b9219b4 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -81,9 +81,11 @@ LLToolPie::LLToolPie() : LLTool(std::string("Pie")), mGrabMouseButtonDown( FALSE ), mMouseOutsideSlop( FALSE ), - mClickAction(0) -{ } - + mClickAction(0), + mClickActionBuyEnabled( gSavedSettings.getBOOL("ClickActionBuyEnabled") ), + mClickActionPayEnabled( gSavedSettings.getBOOL("ClickActionPayEnabled") ) +{ +} BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) { @@ -211,12 +213,28 @@ BOOL LLToolPie::pickLeftMouseDownCallback() } // else nothing (fall through to touch) } case CLICK_ACTION_PAY: - if ((object && object->flagTakesMoney()) - || (parent && parent->flagTakesMoney())) + if ( mClickActionPayEnabled ) + { + if ((object && object->flagTakesMoney()) + || (parent && parent->flagTakesMoney())) + { + // pay event goes to object actually clicked on + mClickActionObject = object; + mLeftClickSelection = LLToolSelect::handleObjectSelection(mPick, FALSE, TRUE); + if (LLSelectMgr::getInstance()->selectGetAllValid()) + { + // call this right away, since we have all the info we need to continue the action + selectionPropertiesReceived(); + } + return TRUE; + } + } + break; + case CLICK_ACTION_BUY: + if ( mClickActionBuyEnabled ) { - // pay event goes to object actually clicked on - mClickActionObject = object; - mLeftClickSelection = LLToolSelect::handleObjectSelection(mPick, FALSE, TRUE); + mClickActionObject = parent; + mLeftClickSelection = LLToolSelect::handleObjectSelection(mPick, FALSE, TRUE, TRUE); if (LLSelectMgr::getInstance()->selectGetAllValid()) { // call this right away, since we have all the info we need to continue the action @@ -225,15 +243,6 @@ BOOL LLToolPie::pickLeftMouseDownCallback() return TRUE; } break; - case CLICK_ACTION_BUY: - mClickActionObject = parent; - mLeftClickSelection = LLToolSelect::handleObjectSelection(mPick, FALSE, TRUE, TRUE); - if (LLSelectMgr::getInstance()->selectGetAllValid()) - { - // call this right away, since we have all the info we need to continue the action - selectionPropertiesReceived(); - } - return TRUE; case CLICK_ACTION_OPEN: if (parent && parent->allowOpen()) { @@ -393,7 +402,7 @@ U8 final_click_action(LLViewerObject* obj) return click_action; } -ECursorType cursor_from_object(LLViewerObject* object) +ECursorType LLToolPie::cursorFromObject(LLViewerObject* object) { LLViewerObject* parent = NULL; if (object) @@ -413,7 +422,10 @@ ECursorType cursor_from_object(LLViewerObject* object) } break; case CLICK_ACTION_BUY: - cursor = UI_CURSOR_TOOLBUY; + if ( mClickActionBuyEnabled ) + { + cursor = UI_CURSOR_TOOLBUY; + } break; case CLICK_ACTION_OPEN: // Open always opens the parent. @@ -423,10 +435,13 @@ ECursorType cursor_from_object(LLViewerObject* object) } break; case CLICK_ACTION_PAY: - if ((object && object->flagTakesMoney()) - || (parent && parent->flagTakesMoney())) + if ( mClickActionPayEnabled ) { - cursor = UI_CURSOR_TOOLBUY; + if ((object && object->flagTakesMoney()) + || (parent && parent->flagTakesMoney())) + { + cursor = UI_CURSOR_TOOLBUY; + } } break; case CLICK_ACTION_ZOOM: @@ -474,10 +489,16 @@ void LLToolPie::selectionPropertiesReceived() switch (click_action) { case CLICK_ACTION_BUY: - handle_buy(); + if ( LLToolPie::getInstance()->mClickActionBuyEnabled ) + { + handle_buy(); + } break; case CLICK_ACTION_PAY: - handle_give_money_dialog(); + if ( LLToolPie::getInstance()->mClickActionPayEnabled ) + { + handle_give_money_dialog(); + } break; case CLICK_ACTION_OPEN: LLFloaterReg::showInstance("openobject"); @@ -518,7 +539,7 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) else if (click_action_object && useClickAction(mask, click_action_object, click_action_object->getRootEdit())) { show_highlight = true; - ECursorType cursor = cursor_from_object(click_action_object); + ECursorType cursor = cursorFromObject(click_action_object); gViewerWindow->setCursor(cursor); lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; } @@ -571,7 +592,9 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask) { switch(click_action) { + // NOTE: mClickActionBuyEnabled flag enables/disables BUY action but setting cursor to default is okay case CLICK_ACTION_BUY: + // NOTE: mClickActionPayEnabled flag enables/disables PAY action but setting cursor to default is okay case CLICK_ACTION_PAY: case CLICK_ACTION_OPEN: case CLICK_ACTION_ZOOM: @@ -1228,15 +1251,17 @@ void LLToolPie::handleDeselect() LLTool* LLToolPie::getOverrideTool(MASK mask) { - if (mask == MASK_CONTROL) + if (gSavedSettings.getBOOL("EnableGrab")) { - return LLToolGrab::getInstance(); - } - else if (mask == (MASK_CONTROL | MASK_SHIFT)) - { - return LLToolGrab::getInstance(); + if (mask == MASK_CONTROL) + { + return LLToolGrab::getInstance(); + } + else if (mask == (MASK_CONTROL | MASK_SHIFT)) + { + return LLToolGrab::getInstance(); + } } - return LLTool::getOverrideTool(mask); } diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index 65cb3e36a7..77200a1da4 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -80,6 +80,7 @@ private: BOOL useClickAction (MASK mask, LLViewerObject* object,LLViewerObject* parent); void showVisualContextMenuEffect(); + ECursorType cursorFromObject(LLViewerObject* object); bool handleMediaClick(const LLPickInfo& info); bool handleMediaHover(const LLPickInfo& info); @@ -96,8 +97,8 @@ private: LLPointer<LLViewerObject> mClickActionObject; U8 mClickAction; LLSafeHandle<LLObjectSelection> mLeftClickSelection; - + BOOL mClickActionBuyEnabled; + BOOL mClickActionPayEnabled; }; - #endif diff --git a/indra/newview/lltracker.cpp b/indra/newview/lltracker.cpp index c3dd17def9..983108391f 100644 --- a/indra/newview/lltracker.cpp +++ b/indra/newview/lltracker.cpp @@ -109,6 +109,8 @@ void LLTracker::stopTracking(void* userdata) // static virtual void LLTracker::drawHUDArrow() { + if (!gSavedSettings.getBOOL("RenderTrackerBeacon")) return; + static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white); /* tracking autopilot destination has been disabled @@ -155,7 +157,7 @@ void LLTracker::drawHUDArrow() // static void LLTracker::render3D() { - if (!gFloaterWorldMap) + if (!gFloaterWorldMap || !gSavedSettings.getBOOL("RenderTrackerBeacon")) { return; } diff --git a/indra/newview/lltransientfloatermgr.cpp b/indra/newview/lltransientfloatermgr.cpp index 78dd602f39..c648a6a28a 100644 --- a/indra/newview/lltransientfloatermgr.cpp +++ b/indra/newview/lltransientfloatermgr.cpp @@ -36,8 +36,11 @@ LLTransientFloaterMgr::LLTransientFloaterMgr() { - gViewerWindow->getRootView()->addMouseDownCallback(boost::bind( - &LLTransientFloaterMgr::leftMouseClickCallback, this, _1, _2, _3)); + if(gViewerWindow) + { + gViewerWindow->getRootView()->getChild<LLUICtrl>("popup_holder")->setMouseDownCallback(boost::bind( + &LLTransientFloaterMgr::leftMouseClickCallback, this, _2, _3, _4)); + } mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(GLOBAL, std::set<LLView*>())); mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(DOCKED, std::set<LLView*>())); diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 050e34ade9..8ccfdb071b 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -36,7 +36,7 @@ #include "llbufferstream.h" #include "llui.h" -#include "llversionviewer.h" +#include "llversioninfo.h" #include "llviewercontrol.h" #include "jsoncpp/reader.h" @@ -64,11 +64,11 @@ void LLTranslate::translateMessage(LLHTTPClient::ResponderPtr &result, const std getTranslateUrl(url, from_lang, to_lang, mesg); std::string user_agent = llformat("%s %d.%d.%d (%d)", - LL_CHANNEL, - LL_VERSION_MAJOR, - LL_VERSION_MINOR, - LL_VERSION_PATCH, - LL_VERSION_BUILD ); + LLVersionInfo::getChannel().c_str(), + LLVersionInfo::getMajor(), + LLVersionInfo::getMinor(), + LLVersionInfo::getPatch(), + LLVersionInfo::getBuild()); if (!m_Header.size()) { diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index 733d05834a..673d0c24cf 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -95,9 +95,42 @@ const std::string &LLVersionInfo::getShortVersion() return version; } +namespace +{ + /// Storage of the channel name the viewer is using. + // The channel name is set by hardcoded constant, + // or by calling LLVersionInfo::resetChannel() + std::string sWorkingChannelName(LL_CHANNEL); + + // Storage for the "version and channel" string. + // This will get reset too. + std::string sVersionChannel(""); +} + +//static +const std::string &LLVersionInfo::getChannelAndVersion() +{ + if (sVersionChannel.empty()) + { + // cache the version string + std::ostringstream stream; + stream << LLVersionInfo::getChannel() + << " " + << LLVersionInfo::getVersion(); + sVersionChannel = stream.str(); + } + + return sVersionChannel; +} + //static const std::string &LLVersionInfo::getChannel() { - static std::string name(LL_CHANNEL); - return name; + return sWorkingChannelName; +} + +void LLVersionInfo::resetChannel(const std::string& channel) +{ + sWorkingChannelName = channel; + sVersionChannel.clear(); // Reset version and channel string til next use. } diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h index e468b6ae4e..6f64544f3b 100644 --- a/indra/newview/llversioninfo.h +++ b/indra/newview/llversioninfo.h @@ -58,8 +58,15 @@ public: /// return the viewer version as a string like "2.0.0" static const std::string &getShortVersion(); + /// return the viewer version and channel as a string + /// like "Second Life Release 2.0.0.200030" + static const std::string &getChannelAndVersion(); + /// return the channel name, e.g. "Second Life" static const std::string &getChannel(); + + /// reset the channel name used by the viewer. + static void resetChannel(const std::string& channel); }; #endif diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp new file mode 100644 index 0000000000..5ad7725b3e --- /dev/null +++ b/indra/newview/llviewerassetstats.cpp @@ -0,0 +1,619 @@ +/** + * @file llviewerassetstats.cpp + * @brief + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewerassetstats.h" +#include "llregionhandle.h" + +#include "stdtypes.h" + +/* + * Classes and utility functions for per-thread and per-region + * asset and experiential metrics to be aggregated grid-wide. + * + * The basic metrics grouping is LLViewerAssetStats::PerRegionStats. + * This provides various counters and simple statistics for asset + * fetches binned into a few categories. One of these is maintained + * for each region encountered and the 'current' region is available + * as a simple reference. Each thread (presently two) interested + * in participating in these stats gets an instance of the + * LLViewerAssetStats class so that threads are completely + * independent. + * + * The idea of a current region is used for simplicity and speed + * of categorization. Each metrics event could have taken a + * region uuid argument resulting in a suitable lookup. Arguments + * against this design include: + * + * - Region uuid not trivially available to caller. + * - Cost (cpu, disruption in real work flow) too high. + * - Additional precision not really meaningful. + * + * By itself, the LLViewerAssetStats class is thread- and + * viewer-agnostic and can be used anywhere without assumptions + * of global pointers and other context. For the viewer, + * a set of free functions are provided in the namespace + * LLViewerAssetStatsFF which *do* implement viewer-native + * policies about per-thread globals and will do correct + * defensive tests of same. + * + * References + * + * Project: + * <TBD> + * + * Test Plan: + * <TBD> + * + * Jiras: + * <TBD> + * + * Unit Tests: + * indra/newview/tests/llviewerassetstats_test.cpp + * + */ + + +// ------------------------------------------------------ +// Global data definitions +// ------------------------------------------------------ +LLViewerAssetStats * gViewerAssetStatsMain(0); +LLViewerAssetStats * gViewerAssetStatsThread1(0); + + +// ------------------------------------------------------ +// Local declarations +// ------------------------------------------------------ +namespace +{ + +static LLViewerAssetStats::EViewerAssetCategories +asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp); + +} + +// ------------------------------------------------------ +// LLViewerAssetStats::PerRegionStats struct definition +// ------------------------------------------------------ +void +LLViewerAssetStats::PerRegionStats::reset() +{ + for (int i(0); i < LL_ARRAY_SIZE(mRequests); ++i) + { + mRequests[i].mEnqueued.reset(); + mRequests[i].mDequeued.reset(); + mRequests[i].mResponse.reset(); + } + mFPS.reset(); + + mTotalTime = 0; + mStartTimestamp = LLViewerAssetStatsFF::get_timestamp(); +} + + +void +LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionStats & src) +{ + // mRegionHandle, mTotalTime, mStartTimestamp are left alone. + + // mFPS + if (src.mFPS.getCount() && mFPS.getCount()) + { + mFPS.merge(src.mFPS); + } + + // Requests + for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i) + { + mRequests[i].mEnqueued.merge(src.mRequests[i].mEnqueued); + mRequests[i].mDequeued.merge(src.mRequests[i].mDequeued); + mRequests[i].mResponse.merge(src.mRequests[i].mResponse); + } +} + + +void +LLViewerAssetStats::PerRegionStats::accumulateTime(duration_t now) +{ + mTotalTime += (now - mStartTimestamp); + mStartTimestamp = now; +} + + +// ------------------------------------------------------ +// LLViewerAssetStats class definition +// ------------------------------------------------------ +LLViewerAssetStats::LLViewerAssetStats() + : mRegionHandle(U64(0)) +{ + reset(); +} + + +LLViewerAssetStats::LLViewerAssetStats(const LLViewerAssetStats & src) + : mRegionHandle(src.mRegionHandle), + mResetTimestamp(src.mResetTimestamp) +{ + const PerRegionContainer::const_iterator it_end(src.mRegionStats.end()); + for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it) + { + mRegionStats[it->first] = new PerRegionStats(*it->second); + } + mCurRegionStats = mRegionStats[mRegionHandle]; +} + + +void +LLViewerAssetStats::reset() +{ + // Empty the map of all region stats + mRegionStats.clear(); + + // If we have a current stats, reset it, otherwise, as at construction, + // create a new one as we must always have a current stats block. + if (mCurRegionStats) + { + mCurRegionStats->reset(); + } + else + { + mCurRegionStats = new PerRegionStats(mRegionHandle); + } + + // And add reference to map + mRegionStats[mRegionHandle] = mCurRegionStats; + + // Start timestamp consistent with per-region collector + mResetTimestamp = mCurRegionStats->mStartTimestamp; +} + + +void +LLViewerAssetStats::setRegion(region_handle_t region_handle) +{ + if (region_handle == mRegionHandle) + { + // Already active, ignore. + return; + } + + // Get duration for current set + const duration_t now = LLViewerAssetStatsFF::get_timestamp(); + mCurRegionStats->accumulateTime(now); + + // Prepare new set + PerRegionContainer::iterator new_stats = mRegionStats.find(region_handle); + if (mRegionStats.end() == new_stats) + { + // Haven't seen this region_id before, create a new block and make it current. + mCurRegionStats = new PerRegionStats(region_handle); + mRegionStats[region_handle] = mCurRegionStats; + } + else + { + mCurRegionStats = new_stats->second; + } + mCurRegionStats->mStartTimestamp = now; + mRegionHandle = region_handle; +} + + +void +LLViewerAssetStats::recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ + const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); + + ++(mCurRegionStats->mRequests[int(eac)].mEnqueued); +} + +void +LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ + const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); + + ++(mCurRegionStats->mRequests[int(eac)].mDequeued); +} + +void +LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, duration_t duration) +{ + const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); + + mCurRegionStats->mRequests[int(eac)].mResponse.record(duration); +} + +void +LLViewerAssetStats::recordFPS(F32 fps) +{ + mCurRegionStats->mFPS.record(fps); +} + +LLSD +LLViewerAssetStats::asLLSD(bool compact_output) +{ + // Top-level tags + static const LLSD::String tags[EVACCount] = + { + LLSD::String("get_texture_temp_http"), + LLSD::String("get_texture_temp_udp"), + LLSD::String("get_texture_non_temp_http"), + LLSD::String("get_texture_non_temp_udp"), + LLSD::String("get_wearable_udp"), + LLSD::String("get_sound_udp"), + LLSD::String("get_gesture_udp"), + LLSD::String("get_other") + }; + + // Stats Group Sub-tags. + static const LLSD::String enq_tag("enqueued"); + static const LLSD::String deq_tag("dequeued"); + static const LLSD::String rcnt_tag("resp_count"); + static const LLSD::String rmin_tag("resp_min"); + static const LLSD::String rmax_tag("resp_max"); + static const LLSD::String rmean_tag("resp_mean"); + + // MMM Group Sub-tags. + static const LLSD::String cnt_tag("count"); + static const LLSD::String min_tag("min"); + static const LLSD::String max_tag("max"); + static const LLSD::String mean_tag("mean"); + + const duration_t now = LLViewerAssetStatsFF::get_timestamp(); + mCurRegionStats->accumulateTime(now); + + LLSD regions = LLSD::emptyArray(); + for (PerRegionContainer::iterator it = mRegionStats.begin(); + mRegionStats.end() != it; + ++it) + { + if (0 == it->first) + { + // Never emit NULL UUID/handle in results. + continue; + } + + PerRegionStats & stats = *it->second; + + LLSD reg_stat = LLSD::emptyMap(); + + for (int i = 0; i < LL_ARRAY_SIZE(tags); ++i) + { + PerRegionStats::prs_group & group(stats.mRequests[i]); + + if ((! compact_output) || + group.mEnqueued.getCount() || + group.mDequeued.getCount() || + group.mResponse.getCount()) + { + LLSD & slot = reg_stat[tags[i]]; + slot = LLSD::emptyMap(); + slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount())); + slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount())); + slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount())); + slot[rmin_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMin() * 1.0e-6)); + slot[rmax_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMax() * 1.0e-6)); + slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean() * 1.0e-6)); + } + } + + if ((! compact_output) || stats.mFPS.getCount()) + { + LLSD & slot = reg_stat["fps"]; + slot = LLSD::emptyMap(); + slot[cnt_tag] = LLSD(S32(stats.mFPS.getCount())); + slot[min_tag] = LLSD(F64(stats.mFPS.getMin())); + slot[max_tag] = LLSD(F64(stats.mFPS.getMax())); + slot[mean_tag] = LLSD(F64(stats.mFPS.getMean())); + } + + U32 grid_x(0), grid_y(0); + grid_from_region_handle(it->first, &grid_x, &grid_y); + reg_stat["grid_x"] = LLSD::Integer(grid_x); + reg_stat["grid_y"] = LLSD::Integer(grid_y); + reg_stat["duration"] = LLSD::Real(stats.mTotalTime * 1.0e-6); + regions.append(reg_stat); + } + + LLSD ret = LLSD::emptyMap(); + ret["regions"] = regions; + ret["duration"] = LLSD::Real((now - mResetTimestamp) * 1.0e-6); + + return ret; +} + +void +LLViewerAssetStats::merge(const LLViewerAssetStats & src) +{ + // mRegionHandle, mCurRegionStats and mResetTimestamp are left untouched. + // Just merge the stats bodies + + const PerRegionContainer::const_iterator it_end(src.mRegionStats.end()); + for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it) + { + PerRegionContainer::iterator dst(mRegionStats.find(it->first)); + if (mRegionStats.end() == dst) + { + // Destination is missing data, just make a private copy + mRegionStats[it->first] = new PerRegionStats(*it->second); + } + else + { + dst->second->merge(*it->second); + } + } +} + + +// ------------------------------------------------------ +// Global free-function definitions (LLViewerAssetStatsFF namespace) +// ------------------------------------------------------ + +namespace LLViewerAssetStatsFF +{ + +// +// Target thread is elaborated in the function name. This could +// have been something 'templatey' like specializations iterated +// over a set of constants but with so few, this is clearer I think. +// +// As for the threads themselves... rather than do fine-grained +// locking as we gather statistics, this code creates a collector +// for each thread, allocated and run independently. Logging +// happens at relatively infrequent intervals and at that time +// the data is sent to a single thread to be aggregated into +// a single entity with locks, thread safety and other niceties. +// +// A particularly fussy implementation would distribute the +// per-thread pointers across separate cache lines. But that should +// be beyond current requirements. +// + +// 'main' thread - initial program thread + +void +set_region_main(LLViewerAssetStats::region_handle_t region_handle) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->setRegion(region_handle); +} + +void +record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->recordGetEnqueued(at, with_http, is_temp); +} + +void +record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->recordGetDequeued(at, with_http, is_temp); +} + +void +record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->recordGetServiced(at, with_http, is_temp, duration); +} + +void +record_fps_main(F32 fps) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->recordFPS(fps); +} + + +// 'thread1' - should be for TextureFetch thread + +void +set_region_thread1(LLViewerAssetStats::region_handle_t region_handle) +{ + if (! gViewerAssetStatsThread1) + return; + + gViewerAssetStatsThread1->setRegion(region_handle); +} + +void +record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ + if (! gViewerAssetStatsThread1) + return; + + gViewerAssetStatsThread1->recordGetEnqueued(at, with_http, is_temp); +} + +void +record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ + if (! gViewerAssetStatsThread1) + return; + + gViewerAssetStatsThread1->recordGetDequeued(at, with_http, is_temp); +} + +void +record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration) +{ + if (! gViewerAssetStatsThread1) + return; + + gViewerAssetStatsThread1->recordGetServiced(at, with_http, is_temp, duration); +} + + +void +init() +{ + if (! gViewerAssetStatsMain) + { + gViewerAssetStatsMain = new LLViewerAssetStats(); + } + if (! gViewerAssetStatsThread1) + { + gViewerAssetStatsThread1 = new LLViewerAssetStats(); + } +} + +void +cleanup() +{ + delete gViewerAssetStatsMain; + gViewerAssetStatsMain = 0; + + delete gViewerAssetStatsThread1; + gViewerAssetStatsThread1 = 0; +} + + +} // namespace LLViewerAssetStatsFF + + +// ------------------------------------------------------ +// Local function definitions +// ------------------------------------------------------ + +namespace +{ + +LLViewerAssetStats::EViewerAssetCategories +asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ + // For statistical purposes, we divide GETs into several + // populations of asset fetches: + // - textures which are de-prioritized in the asset system + // - wearables (clothing, bodyparts) which directly affect + // user experiences when they log in + // - sounds + // - gestures + // - everything else. + // + llassert_always(26 == 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 +#if 0 + // When LLViewerAssetType::AT_COUNT == 49 + LLViewerAssetStats::EVACOtherGet, // AT_FOLDER_ENSEMBLE_START + 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, // AT_FOLDER_ENSEMBLE_END + LLViewerAssetStats::EVACOtherGet, // AT_CURRENT_OUTFIT + LLViewerAssetStats::EVACOtherGet, // AT_OUTFIT + LLViewerAssetStats::EVACOtherGet // AT_MY_OUTFITS +#endif + }; + + if (at < 0 || at >= LLViewerAssetType::AT_COUNT) + { + return LLViewerAssetStats::EVACOtherGet; + } + 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/llviewerassetstats.h b/indra/newview/llviewerassetstats.h new file mode 100644 index 0000000000..905ceefad5 --- /dev/null +++ b/indra/newview/llviewerassetstats.h @@ -0,0 +1,334 @@ +/** + * @file llviewerassetstats.h + * @brief Client-side collection of asset request statistics + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLVIEWERASSETSTATUS_H +#define LL_LLVIEWERASSETSTATUS_H + + +#include "linden_common.h" + +#include "llpointer.h" +#include "llrefcount.h" +#include "llviewerassettype.h" +#include "llviewerassetstorage.h" +#include "llsimplestat.h" +#include "llsd.h" + +/** + * @class LLViewerAssetStats + * @brief Records performance aspects of asset access operations. + * + * This facility is derived from a very similar simulator-based + * one, LLSimAssetStats. It's function is to count asset access + * operations and characterize response times. Collected data + * are binned in several dimensions: + * + * - Asset types collapsed into a few aggregated categories + * - By simulator UUID + * - By transport mechanism (HTTP vs MessageSystem) + * - By persistence (temp vs non-temp) + * + * Statistics collected are fairly basic at this point: + * + * - Counts of enqueue and dequeue operations + * - Min/Max/Mean of asset transfer operations + * + * This collector differs from the simulator-based on in a + * number of ways: + * + * - The front-end/back-end distinction doesn't exist in viewer + * code + * - Multiple threads must be safely accomodated in the viewer + * + * Access to results is by conversion to an LLSD with some standardized + * key names. The intent of this structure is that it be emitted as + * standard syslog-based metrics formatting where it can be picked + * up by interested parties. + * + * For convenience, a set of free functions in namespace + * LLViewerAssetStatsFF is provided for conditional test-and-call + * operations. + */ +class LLViewerAssetStats +{ +public: + enum EViewerAssetCategories + { + EVACTextureTempHTTPGet, //< Texture GETs - temp/baked, HTTP + EVACTextureTempUDPGet, //< Texture GETs - temp/baked, UDP + EVACTextureNonTempHTTPGet, //< Texture GETs - perm, HTTP + EVACTextureNonTempUDPGet, //< Texture GETs - perm, UDP + EVACWearableUDPGet, //< Wearable GETs + EVACSoundUDPGet, //< Sound GETs + EVACGestureUDPGet, //< Gesture GETs + EVACOtherGet, //< Other GETs + + EVACCount // Must be last + }; + + /** + * Type for duration and other time values in the metrics. Selected + * for compatibility with the pre-existing timestamp on the texture + * fetcher class, LLTextureFetch. + */ + typedef U64 duration_t; + + /** + * Type for the region identifier used in stats. Currently uses + * the region handle's type (a U64) rather than the regions's LLUUID + * as the latter isn't available immediately. + */ + typedef U64 region_handle_t; + + /** + * @brief Collected data for a single region visited by the avatar. + * + * Fairly simple, for each asset bin enumerated above a count + * of enqueue and dequeue operations and simple stats on response + * times for completed requests. + */ + class PerRegionStats : public LLRefCount + { + public: + PerRegionStats(const region_handle_t region_handle) + : LLRefCount(), + mRegionHandle(region_handle) + { + reset(); + } + + PerRegionStats(const PerRegionStats & src) + : LLRefCount(), + mRegionHandle(src.mRegionHandle), + mTotalTime(src.mTotalTime), + mStartTimestamp(src.mStartTimestamp), + mFPS(src.mFPS) + { + for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i) + { + mRequests[i] = src.mRequests[i]; + } + } + + // Default assignment and destructor are correct. + + void reset(); + + void merge(const PerRegionStats & src); + + // Apply current running time to total and reset start point. + // Return current timestamp as a convenience. + void accumulateTime(duration_t now); + + public: + region_handle_t mRegionHandle; + duration_t mTotalTime; + duration_t mStartTimestamp; + LLSimpleStatMMM<> mFPS; + + struct prs_group + { + LLSimpleStatCounter mEnqueued; + LLSimpleStatCounter mDequeued; + LLSimpleStatMMM<duration_t> mResponse; + } + mRequests [EVACCount]; + }; + +public: + LLViewerAssetStats(); + LLViewerAssetStats(const LLViewerAssetStats &); + // Default destructor is correct. + LLViewerAssetStats & operator=(const LLViewerAssetStats &); // Not defined + + // Clear all metrics data. This leaves the currently-active region + // in place but with zero'd data for all metrics. All other regions + // are removed from the collection map. + void reset(); + + // Set hidden region argument and establish context for subsequent + // collection calls. + void setRegion(region_handle_t region_handle); + + // Asset GET Requests + void recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp); + void recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp); + void recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, duration_t duration); + + // Frames-Per-Second Samples + void recordFPS(F32 fps); + + // Merge a source instance into a destination instance. This is + // conceptually an 'operator+=()' method: + // - counts are added + // - minimums are min'd + // - maximums are max'd + // - other scalars are ignored ('this' wins) + // + void merge(const LLViewerAssetStats & src); + + // Retrieve current metrics for all visited regions (NULL region UUID/handle excluded) + // Returned LLSD is structured as follows: + // + // &stats_group = { + // enqueued : int, + // dequeued : int, + // resp_count : int, + // resp_min : float, + // resp_max : float, + // resp_mean : float + // } + // + // &mmm_group = { + // count : int, + // min : float, + // max : float, + // mean : float + // } + // + // { + // duration: int + // regions: { + // $: { // Keys are strings of the region's handle in hex + // duration: : int, + // fps: : &mmm_group, + // get_texture_temp_http : &stats_group, + // get_texture_temp_udp : &stats_group, + // get_texture_non_temp_http : &stats_group, + // get_texture_non_temp_udp : &stats_group, + // get_wearable_udp : &stats_group, + // get_sound_udp : &stats_group, + // get_gesture_udp : &stats_group, + // get_other : &stats_group + // } + // } + // } + // + // @param compact_output If true, omits from conversion any mmm_block + // or stats_block that would contain all zero data. + // Useful for transmission when the receiver knows + // what is expected and will assume zero for missing + // blocks. + LLSD asLLSD(bool compact_output); + +protected: + typedef std::map<region_handle_t, LLPointer<PerRegionStats> > PerRegionContainer; + + // Region of the currently-active region. Always valid but may + // be zero after construction or when explicitly set. Unchanged + // by a reset() call. + region_handle_t mRegionHandle; + + // Pointer to metrics collection for currently-active region. Always + // valid and unchanged after reset() though contents will be changed. + // Always points to a collection contained in mRegionStats. + LLPointer<PerRegionStats> mCurRegionStats; + + // Metrics data for all regions during one collection cycle + PerRegionContainer mRegionStats; + + // Time of last reset + duration_t mResetTimestamp; +}; + + +/** + * Global stats collectors one for each independent thread where + * assets and other statistics are gathered. The globals are + * expected to be created at startup time and then picked up by + * their respective threads afterwards. A set of free functions + * are provided to access methods behind the globals while both + * minimally disrupting visual flow and supplying a description + * of intent. + * + * Expected thread assignments: + * + * - Main: main() program execution thread + * - Thread1: TextureFetch worker thread + */ +extern LLViewerAssetStats * gViewerAssetStatsMain; + +extern LLViewerAssetStats * gViewerAssetStatsThread1; + +namespace LLViewerAssetStatsFF +{ +/** + * @brief Allocation and deallocation of globals. + * + * init() should be called before threads are started that will access it though + * you'll likely get away with calling it afterwards. cleanup() should only be + * called after threads are shutdown to prevent races on the global pointers. + */ +void init(); + +void cleanup(); + +/** + * We have many timers, clocks etc. in the runtime. This is the + * canonical timestamp for these metrics which is compatible with + * the pre-existing timestamping in the texture fetcher. + */ +inline LLViewerAssetStats::duration_t get_timestamp() +{ + return LLTimer::getTotalTime(); +} + +/** + * Region context, event and duration loggers for the Main thread. + */ +void set_region_main(LLViewerAssetStats::region_handle_t region_handle); + +void record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp); + +void record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp); + +void record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, + LLViewerAssetStats::duration_t duration); + +void record_fps_main(F32 fps); + + +/** + * Region context, event and duration loggers for Thread 1. + */ +void set_region_thread1(LLViewerAssetStats::region_handle_t region_handle); + +void record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp); + +void record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp); + +void record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, + LLViewerAssetStats::duration_t duration); + +} // namespace LLViewerAssetStatsFF + +#endif // LL_LLVIEWERASSETSTATUS_H diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 2e7ef0fec3..36c8b42a52 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -33,6 +33,61 @@ #include "message.h" #include "llagent.h" +#include "lltransfersourceasset.h" +#include "lltransfertargetvfile.h" +#include "llviewerassetstats.h" + +///---------------------------------------------------------------------------- +/// LLViewerAssetRequest +///---------------------------------------------------------------------------- + +/** + * @brief Local class to encapsulate asset fetch requests with a timestamp. + * + * Derived from the common LLAssetRequest class, this is currently used + * only for fetch/get operations and its only function is to wrap remote + * asset fetch requests so that they can be timed. + */ +class LLViewerAssetRequest : public LLAssetRequest +{ +public: + LLViewerAssetRequest(const LLUUID &uuid, const LLAssetType::EType type) + : LLAssetRequest(uuid, type), + mMetricsStartTime(0) + { + } + + LLViewerAssetRequest & operator=(const LLViewerAssetRequest &); // Not defined + // Default assignment operator valid + + // virtual + ~LLViewerAssetRequest() + { + recordMetrics(); + } + +protected: + void recordMetrics() + { + if (mMetricsStartTime) + { + // Okay, it appears this request was used for useful things. Record + // the expected dequeue and duration of request processing. + LLViewerAssetStatsFF::record_dequeue_main(mType, false, false); + LLViewerAssetStatsFF::record_response_main(mType, false, false, + (LLViewerAssetStatsFF::get_timestamp() + - mMetricsStartTime)); + mMetricsStartTime = 0; + } + } + +public: + LLViewerAssetStats::duration_t mMetricsStartTime; +}; + +///---------------------------------------------------------------------------- +/// LLViewerAssetStorage +///---------------------------------------------------------------------------- LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs, @@ -258,3 +313,77 @@ void LLViewerAssetStorage::storeAssetData( } } } + + +/** + * @brief Allocate and queue an asset fetch request for the viewer + * + * This is a nearly-verbatim copy of the base class's implementation + * with the following changes: + * - Use a locally-derived request class + * - Start timing for metrics when request is queued + * + * This is an unfortunate implementation choice but it's forced by + * current conditions. A refactoring that might clean up the layers + * of responsibility or introduce factories or more virtualization + * of methods would enable a more attractive solution. + * + * If LLAssetStorage::_queueDataRequest changes, this must change + * as well. + */ + +// virtual +void LLViewerAssetStorage::_queueDataRequest( + const LLUUID& uuid, + LLAssetType::EType atype, + LLGetAssetCallback callback, + void *user_data, + BOOL duplicate, + BOOL is_priority) +{ + if (mUpstreamHost.isOk()) + { + // stash the callback info so we can find it after we get the response message + LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype); + req->mDownCallback = callback; + req->mUserData = user_data; + req->mIsPriority = is_priority; + if (!duplicate) + { + // Only collect metrics for non-duplicate requests. Others + // are piggy-backing and will artificially lower averages. + req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + + mPendingDownloads.push_back(req); + + 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); + + llinfos << "Starting transfer for " << uuid << llendl; + LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); + ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); + + LLViewerAssetStatsFF::record_enqueue_main(atype, false, false); + } + } + else + { + // uh-oh, we shouldn't have gotten here + llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl; + if (callback) + { + callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); + } + } +} + diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 6346b79f03..ca9b9943fa 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -63,6 +63,17 @@ public: bool is_priority = false, bool user_waiting=FALSE, F64 timeout=LL_ASSET_STORAGE_TIMEOUT); + +protected: + using LLAssetStorage::_queueDataRequest; + + // virtual + void _queueDataRequest(const LLUUID& uuid, + LLAssetType::EType type, + void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), + void *user_data, + BOOL duplicate, + BOOL is_priority); }; #endif diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index fbec2a7b9e..8c5a52c187 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -70,6 +70,7 @@ #include "llpaneloutfitsinventory.h" #include "llpanellogin.h" #include "llpaneltopinfobar.h" +#include "llupdaterservice.h" #ifdef TOGGLE_HACKED_GODLIKE_VIEWER BOOL gHackGodmode = FALSE; @@ -82,7 +83,6 @@ LLControlGroup gCrashSettings("CrashSettings"); // saved at end of session LLControlGroup gWarningSettings("Warnings"); // persists ignored dialogs/warnings std::string gLastRunVersion; -std::string gCurrentVersion; extern BOOL gResizeScreenTexture; extern BOOL gDebugGL; @@ -117,10 +117,23 @@ static bool handleSetShaderChanged(const LLSD& newvalue) gBumpImageList.destroyGL(); gBumpImageList.restoreGL(); + // Changing shader also changes the terrain detail to high, reflect that change here + if (newvalue.asBoolean()) + { + // shaders enabled, set terrain detail to high + gSavedSettings.setS32("RenderTerrainDetail", 1); + } + // else, leave terrain detail as is LLViewerShaderMgr::instance()->setShaders(); return true; } +bool handleRenderTransparentWaterChanged(const LLSD& newvalue) +{ + LLWorld::getInstance()->updateWaterObjects(); + return true; +} + static bool handleReleaseGLBufferChanged(const LLSD& newvalue) { if (gPipeline.isInit()) @@ -489,6 +502,19 @@ bool toggle_show_object_render_cost(const LLSD& newvalue) return true; } +void toggle_updater_service_active(LLControlVariable* control, const LLSD& new_value) +{ + if(new_value.asInteger()) + { + LLUpdaterService update_service; + if(!update_service.isChecking()) update_service.startChecking(); + } + else + { + LLUpdaterService().stopChecking(); + } +} + //////////////////////////////////////////////////////////////////////////// void settings_setup_listeners() @@ -636,7 +662,9 @@ void settings_setup_listeners() gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2)); gSavedSettings.getControl("ShowMiniLocationPanel")->getSignal()->connect(boost::bind(&toggle_show_mini_location_panel, _2)); gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2)); + gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(&toggle_updater_service_active); gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2)); + gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2)); } #if TEST_CACHED_CONTROL diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h index 22b48f8906..d7191f5c8d 100644 --- a/indra/newview/llviewercontrol.h +++ b/indra/newview/llviewercontrol.h @@ -57,7 +57,5 @@ extern LLControlGroup gCrashSettings; // Set after settings loaded extern std::string gLastRunVersion; -extern std::string gCurrentVersion; - #endif // LL_LLVIEWERCONTROL_H diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 40583f05bf..ddb11829df 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -95,6 +95,7 @@ BOOL gForceRenderLandFence = FALSE; BOOL gDisplaySwapBuffers = FALSE; BOOL gDepthDirty = FALSE; BOOL gResizeScreenTexture = FALSE; +BOOL gWindowResized = FALSE; BOOL gSnapshot = FALSE; U32 gRecentFrameCount = 0; // number of 'recent' frames @@ -218,22 +219,15 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) LLMemType mt_render(LLMemType::MTYPE_RENDER); LLFastTimer t(FTM_RENDER); - if (gResizeScreenTexture) - { //skip render on frames where screen texture is resizing + if (gWindowResized) + { //skip render on frames where window has been resized gGL.flush(); - if (!for_snapshot) - { - glClear(GL_COLOR_BUFFER_BIT); - gViewerWindow->mWindow->swapBuffers(); - } - - gResizeScreenTexture = FALSE; + glClear(GL_COLOR_BUFFER_BIT); + gViewerWindow->mWindow->swapBuffers(); gPipeline.resizeScreenTexture(); - - if (!for_snapshot) - { - return; - } + gResizeScreenTexture = FALSE; + gWindowResized = FALSE; + return; } if (LLPipeline::sRenderDeferred) @@ -597,7 +591,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) S32 water_clip = 0; if ((LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_ENVIRONMENT) > 1) && - gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER)) + (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER) || + gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_VOIDWATER))) { if (LLViewerCamera::getInstance()->cameraUnderWater()) { @@ -665,11 +660,11 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) LLVertexBuffer::clientCopy(0.016); } - //if (gResizeScreenTexture) - //{ - // gResizeScreenTexture = FALSE; - // gPipeline.resizeScreenTexture(); - //} + if (gResizeScreenTexture) + { + gResizeScreenTexture = FALSE; + gPipeline.resizeScreenTexture(); + } gGL.setColorMask(true, true); glClearColor(0,0,0,0); @@ -730,7 +725,6 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) // // Doing this here gives hardware occlusion queries extra time to complete LLAppViewer::instance()->pingMainloopTimeout("Display:UpdateImages"); - LLError::LLCallStacks::clear() ; { LLMemType mt_iu(LLMemType::MTYPE_DISPLAY_IMAGE_UPDATE); diff --git a/indra/newview/llviewerdisplay.h b/indra/newview/llviewerdisplay.h index c6e86751e8..f6467d7f93 100644 --- a/indra/newview/llviewerdisplay.h +++ b/indra/newview/llviewerdisplay.h @@ -40,5 +40,6 @@ extern BOOL gTeleportDisplay; extern LLFrameTimer gTeleportDisplayTimer; extern BOOL gForceRenderLandFence; extern BOOL gResizeScreenTexture; +extern BOOL gWindowResized; #endif // LL_LLVIEWERDISPLAY_H diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index b3f14b441d..dca1e33e60 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -60,6 +60,7 @@ #include "llfloaterhardwaresettings.h" #include "llfloaterhelpbrowser.h" #include "llfloatermediabrowser.h" +#include "llfloaterwebcontent.h" #include "llfloatermediasettings.h" #include "llfloaterhud.h" #include "llfloaterimagepreview.h" @@ -81,6 +82,7 @@ #include "llfloaterpostprocess.h" #include "llfloaterpreference.h" #include "llfloaterproperties.h" +#include "llfloaterregiondebugconsole.h" #include "llfloaterregioninfo.h" #include "llfloaterreporter.h" #include "llfloaterscriptdebug.h" @@ -227,6 +229,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("reporter", "floater_report_abuse.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterReporter>); LLFloaterReg::add("reset_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterResetQueue>); + LLFloaterReg::add("region_debug_console", "floater_region_debug_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionDebugConsole>); LLFloaterReg::add("region_info", "floater_region_info.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionInfo>); LLFloaterReg::add("script_debug", "floater_script_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebug>); @@ -250,6 +253,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("voice_controls", "floater_voice_controls.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLCallFloater>); LLFloaterReg::add("voice_effect", "floater_voice_effect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterVoiceEffect>); + LLFloaterReg::add("web_content", "floater_web_content.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWebContent>); LLFloaterReg::add("whitelist_entry", "floater_whitelist_entry.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWhiteListEntry>); LLFloaterWindowSizeUtil::registerFloater(); LLFloaterReg::add("world_map", "floater_world_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWorldMap>); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 7dbaa4cf92..b3642a2c1e 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -48,6 +48,7 @@ #include "llinventorybridge.h" #include "llinventorypanel.h" #include "llfloaterinventory.h" +#include "lllandmarkactions.h" #include "llviewerassettype.h" #include "llviewerregion.h" @@ -59,6 +60,9 @@ #include "llcommandhandler.h" #include "llviewermessage.h" #include "llsidepanelappearance.h" +#include "llavatarnamecache.h" +#include "llavataractions.h" +#include "lllogininstance.h" ///---------------------------------------------------------------------------- /// Helper class to store special inventory item names and their localized values. @@ -211,6 +215,7 @@ public: }; LLInventoryHandler gInventoryHandler; + ///---------------------------------------------------------------------------- /// Class LLViewerInventoryItem ///---------------------------------------------------------------------------- @@ -1414,6 +1419,8 @@ public: S32 getSortIndex(const LLUUID& inv_item_id); void removeSortIndex(const LLUUID& inv_item_id); + void getSLURL(const LLUUID& asset_id); + /** * Implementation of LLDestroyClass. Calls cleanup() instance method. * @@ -1440,9 +1447,17 @@ private: void load(); void save(); + void saveFavoritesSLURLs(); + + void onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark); + void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); + typedef std::map<LLUUID, S32> sort_index_map_t; sort_index_map_t mSortIndexes; + typedef std::map<LLUUID, std::string> slurls_map_t; + slurls_map_t mSLURLs; + bool mIsDirty; struct IsNotInFavorites @@ -1497,10 +1512,27 @@ void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& 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) + { + onLandmarkLoaded(asset_id, lm); + } +} + // static void LLFavoritesOrderStorage::destroyClass() { LLFavoritesOrderStorage::instance().cleanup(); + if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) + { + LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); + } } void LLFavoritesOrderStorage::load() @@ -1523,6 +1555,76 @@ void LLFavoritesOrderStorage::load() } } +void LLFavoritesOrderStorage::saveFavoritesSLURLs() +{ + // Do not change the file if we are not logged in yet. + if (!LLLoginInstance::getInstance()->authSuccess()) return; + + std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); + if (user_dir.empty()) return; + + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + llifstream in_file; + in_file.open(filename); + LLSD fav_llsd; + if (in_file.is_open()) + { + LLSDSerialize::fromXML(fav_llsd, in_file); + } + + 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::iterator it = items.begin(); it != items.end(); it++) + { + LLSD value; + value["name"] = (*it)->getName(); + value["asset_id"] = (*it)->getAssetUUID(); + + slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); + if (slurl_iter != mSLURLs.end()) + { + value["slurl"] = slurl_iter->second; + user_llsd[(*it)->getSortField()] = value; + } + } + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + fav_llsd[av_name.getLegacyName()] = user_llsd; + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, file); +} + +void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) +{ + if (!landmark) return; + + 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()) + { + LLLandmarkActions::getSLURLfromPosGlobal(pos_global, + boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); + } +} + +void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) +{ + mSLURLs[asset_id] = slurl; +} + void LLFavoritesOrderStorage::save() { // nothing to save if clean @@ -1579,6 +1681,12 @@ S32 LLViewerInventoryItem::getSortField() const void LLViewerInventoryItem::setSortField(S32 sortField) { LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField); + getSLURL(); +} + +void LLViewerInventoryItem::getSLURL() +{ + LLFavoritesOrderStorage::instance().getSLURL(mAssetUUID); } const LLPermissions& LLViewerInventoryItem::getPermissions() const diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 1af06a1be8..41542a4e0f 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -62,6 +62,7 @@ public: 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; diff --git a/indra/newview/llviewerjoint.cpp b/indra/newview/llviewerjoint.cpp index 0cf5fe0ada..baf85d6884 100644 --- a/indra/newview/llviewerjoint.cpp +++ b/indra/newview/llviewerjoint.cpp @@ -257,7 +257,7 @@ U32 LLViewerJoint::render( F32 pixelArea, BOOL first_pass, BOOL is_dummy ) // if object is transparent, defer it, otherwise // give the joint subclass a chance to draw itself //---------------------------------------------------------------- - if ( gRenderForSelect || is_dummy ) + if ( is_dummy ) { triangle_count += drawShape( pixelArea, first_pass, is_dummy ); } diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp index ae2aa41b3a..e59e685f53 100644 --- a/indra/newview/llviewerjointmesh.cpp +++ b/indra/newview/llviewerjointmesh.cpp @@ -61,7 +61,6 @@ extern PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB; extern PFNGLWEIGHTFVARBPROC glWeightfvARB; extern PFNGLVERTEXBLENDARBPROC glVertexBlendARB; #endif -extern BOOL gRenderForSelect; static LLPointer<LLVertexBuffer> sRenderBuffer = NULL; static const U32 sRenderMask = LLVertexBuffer::MAP_VERTEX | @@ -515,17 +514,14 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy) //---------------------------------------------------------------- // setup current color //---------------------------------------------------------------- - if (!gRenderForSelect) - { - if (is_dummy) - glColor4fv(LLVOAvatar::getDummyColor().mV); - else - glColor4fv(mColor.mV); - } + if (is_dummy) + glColor4fv(LLVOAvatar::getDummyColor().mV); + else + glColor4fv(mColor.mV); stop_glerror(); - LLGLSSpecular specular(LLColor4(1.f,1.f,1.f,1.f), gRenderForSelect ? 0.0f : mShiny && !(mFace->getPool()->getVertexShaderLevel() > 0)); + LLGLSSpecular specular(LLColor4(1.f,1.f,1.f,1.f), mShiny && !(mFace->getPool()->getVertexShaderLevel() > 0)); //---------------------------------------------------------------- // setup current texture @@ -580,19 +576,6 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy) gGL.getTexUnit(0)->bind(LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT)); } - if (gRenderForSelect) - { - if (isTransparent()) - { - gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR); - gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_TEX_ALPHA, LLTexUnit::TBS_CONST_ALPHA); - } - else - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - } - mFace->mVertexBuffer->setBuffer(sRenderMask); U32 start = mMesh->mFaceVertexOffset; diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index d7e15e7d6c..1aa9fd8a45 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -40,6 +40,7 @@ #include "llviewerwindow.h" #include "llvoavatarself.h" #include "llfloatercamera.h" +#include "llinitparam.h" // // Constants @@ -53,6 +54,11 @@ const S32 NUDGE_FRAMES = 2; const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed const F32 YAW_NUDGE_RATE = 0.05f; // fraction of normal speed +struct LLKeyboardActionRegistry +: public LLRegistrySingleton<std::string, boost::function<void (EKeystate keystate)>, LLKeyboardActionRegistry> +{ +}; + LLViewerKeyboard gViewerKeyboard; void agent_jump( EKeystate s ) @@ -550,52 +556,50 @@ void start_gesture( EKeystate s ) } } -void bind_keyboard_functions() -{ - gViewerKeyboard.bindNamedFunction("jump", agent_jump); - gViewerKeyboard.bindNamedFunction("push_down", agent_push_down); - gViewerKeyboard.bindNamedFunction("push_forward", agent_push_forward); - gViewerKeyboard.bindNamedFunction("push_backward", agent_push_backward); - gViewerKeyboard.bindNamedFunction("look_up", agent_look_up); - gViewerKeyboard.bindNamedFunction("look_down", agent_look_down); - gViewerKeyboard.bindNamedFunction("toggle_fly", agent_toggle_fly); - gViewerKeyboard.bindNamedFunction("turn_left", agent_turn_left); - gViewerKeyboard.bindNamedFunction("turn_right", agent_turn_right); - gViewerKeyboard.bindNamedFunction("slide_left", agent_slide_left); - gViewerKeyboard.bindNamedFunction("slide_right", agent_slide_right); - gViewerKeyboard.bindNamedFunction("spin_around_ccw", camera_spin_around_ccw); - gViewerKeyboard.bindNamedFunction("spin_around_cw", camera_spin_around_cw); - gViewerKeyboard.bindNamedFunction("spin_around_ccw_sitting", camera_spin_around_ccw_sitting); - gViewerKeyboard.bindNamedFunction("spin_around_cw_sitting", camera_spin_around_cw_sitting); - gViewerKeyboard.bindNamedFunction("spin_over", camera_spin_over); - gViewerKeyboard.bindNamedFunction("spin_under", camera_spin_under); - gViewerKeyboard.bindNamedFunction("spin_over_sitting", camera_spin_over_sitting); - gViewerKeyboard.bindNamedFunction("spin_under_sitting", camera_spin_under_sitting); - gViewerKeyboard.bindNamedFunction("move_forward", camera_move_forward); - gViewerKeyboard.bindNamedFunction("move_backward", camera_move_backward); - gViewerKeyboard.bindNamedFunction("move_forward_sitting", camera_move_forward_sitting); - gViewerKeyboard.bindNamedFunction("move_backward_sitting", camera_move_backward_sitting); - gViewerKeyboard.bindNamedFunction("pan_up", camera_pan_up); - gViewerKeyboard.bindNamedFunction("pan_down", camera_pan_down); - gViewerKeyboard.bindNamedFunction("pan_left", camera_pan_left); - gViewerKeyboard.bindNamedFunction("pan_right", camera_pan_right); - gViewerKeyboard.bindNamedFunction("pan_in", camera_pan_in); - gViewerKeyboard.bindNamedFunction("pan_out", camera_pan_out); - gViewerKeyboard.bindNamedFunction("move_forward_fast", camera_move_forward_fast); - gViewerKeyboard.bindNamedFunction("move_backward_fast", camera_move_backward_fast); - gViewerKeyboard.bindNamedFunction("edit_avatar_spin_ccw", edit_avatar_spin_ccw); - gViewerKeyboard.bindNamedFunction("edit_avatar_spin_cw", edit_avatar_spin_cw); - gViewerKeyboard.bindNamedFunction("edit_avatar_spin_over", edit_avatar_spin_over); - gViewerKeyboard.bindNamedFunction("edit_avatar_spin_under", edit_avatar_spin_under); - gViewerKeyboard.bindNamedFunction("edit_avatar_move_forward", edit_avatar_move_forward); - gViewerKeyboard.bindNamedFunction("edit_avatar_move_backward", edit_avatar_move_backward); - gViewerKeyboard.bindNamedFunction("stop_moving", stop_moving); - gViewerKeyboard.bindNamedFunction("start_chat", start_chat); - gViewerKeyboard.bindNamedFunction("start_gesture", start_gesture); -} - -LLViewerKeyboard::LLViewerKeyboard() : - mNamedFunctionCount(0) +#define REGISTER_KEYBOARD_ACTION(KEY, ACTION) LLREGISTER_STATIC(LLKeyboardActionRegistry, KEY, ACTION); +REGISTER_KEYBOARD_ACTION("jump", agent_jump); +REGISTER_KEYBOARD_ACTION("push_down", agent_push_down); +REGISTER_KEYBOARD_ACTION("push_forward", agent_push_forward); +REGISTER_KEYBOARD_ACTION("push_backward", agent_push_backward); +REGISTER_KEYBOARD_ACTION("look_up", agent_look_up); +REGISTER_KEYBOARD_ACTION("look_down", agent_look_down); +REGISTER_KEYBOARD_ACTION("toggle_fly", agent_toggle_fly); +REGISTER_KEYBOARD_ACTION("turn_left", agent_turn_left); +REGISTER_KEYBOARD_ACTION("turn_right", agent_turn_right); +REGISTER_KEYBOARD_ACTION("slide_left", agent_slide_left); +REGISTER_KEYBOARD_ACTION("slide_right", agent_slide_right); +REGISTER_KEYBOARD_ACTION("spin_around_ccw", camera_spin_around_ccw); +REGISTER_KEYBOARD_ACTION("spin_around_cw", camera_spin_around_cw); +REGISTER_KEYBOARD_ACTION("spin_around_ccw_sitting", camera_spin_around_ccw_sitting); +REGISTER_KEYBOARD_ACTION("spin_around_cw_sitting", camera_spin_around_cw_sitting); +REGISTER_KEYBOARD_ACTION("spin_over", camera_spin_over); +REGISTER_KEYBOARD_ACTION("spin_under", camera_spin_under); +REGISTER_KEYBOARD_ACTION("spin_over_sitting", camera_spin_over_sitting); +REGISTER_KEYBOARD_ACTION("spin_under_sitting", camera_spin_under_sitting); +REGISTER_KEYBOARD_ACTION("move_forward", camera_move_forward); +REGISTER_KEYBOARD_ACTION("move_backward", camera_move_backward); +REGISTER_KEYBOARD_ACTION("move_forward_sitting", camera_move_forward_sitting); +REGISTER_KEYBOARD_ACTION("move_backward_sitting", camera_move_backward_sitting); +REGISTER_KEYBOARD_ACTION("pan_up", camera_pan_up); +REGISTER_KEYBOARD_ACTION("pan_down", camera_pan_down); +REGISTER_KEYBOARD_ACTION("pan_left", camera_pan_left); +REGISTER_KEYBOARD_ACTION("pan_right", camera_pan_right); +REGISTER_KEYBOARD_ACTION("pan_in", camera_pan_in); +REGISTER_KEYBOARD_ACTION("pan_out", camera_pan_out); +REGISTER_KEYBOARD_ACTION("move_forward_fast", camera_move_forward_fast); +REGISTER_KEYBOARD_ACTION("move_backward_fast", camera_move_backward_fast); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_ccw", edit_avatar_spin_ccw); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_cw", edit_avatar_spin_cw); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_over", edit_avatar_spin_over); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_under", edit_avatar_spin_under); +REGISTER_KEYBOARD_ACTION("edit_avatar_move_forward", edit_avatar_move_forward); +REGISTER_KEYBOARD_ACTION("edit_avatar_move_backward", edit_avatar_move_backward); +REGISTER_KEYBOARD_ACTION("stop_moving", stop_moving); +REGISTER_KEYBOARD_ACTION("start_chat", start_chat); +REGISTER_KEYBOARD_ACTION("start_gesture", start_gesture); +#undef REGISTER_KEYBOARD_ACTION + +LLViewerKeyboard::LLViewerKeyboard() { for (S32 i = 0; i < MODE_COUNT; i++) { @@ -613,16 +617,6 @@ LLViewerKeyboard::LLViewerKeyboard() : } } - -void LLViewerKeyboard::bindNamedFunction(const std::string& name, LLKeyFunc func) -{ - S32 i = mNamedFunctionCount; - mNamedFunctions[i].mName = name; - mNamedFunctions[i].mFunction = func; - mNamedFunctionCount++; -} - - BOOL LLViewerKeyboard::modeFromString(const std::string& string, S32 *mode) { if (string == "FIRST_PERSON") @@ -695,8 +689,9 @@ BOOL LLViewerKeyboard::handleKey(KEY translated_key, MASK translated_mask, BOOL BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name) { - S32 i,index; - void (*function)(EKeystate keystate) = NULL; + S32 index; + typedef boost::function<void(EKeystate)> function_t; + function_t function = NULL; std::string name; // Allow remapping of F2-F12 @@ -719,13 +714,11 @@ BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, c } // Not remapped, look for a function - for (i = 0; i < mNamedFunctionCount; i++) + + function_t* result = LLKeyboardActionRegistry::getValue(function_name); + if (result) { - if (function_name == mNamedFunctions[i].mName) - { - function = mNamedFunctions[i].mFunction; - name = mNamedFunctions[i].mName; - } + function = *result; } if (!function) @@ -755,7 +748,6 @@ BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, c mBindings[mode][index].mKey = key; mBindings[mode][index].mMask = mask; -// mBindings[mode][index].mName = name; mBindings[mode][index].mFunction = function; if (index == mBindingCount[mode]) @@ -764,6 +756,61 @@ BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, c return TRUE; } +LLViewerKeyboard::KeyBinding::KeyBinding() +: key("key"), + mask("mask"), + command("command") +{} + +LLViewerKeyboard::KeyMode::KeyMode(EKeyboardMode _mode) +: bindings("binding"), + mode(_mode) +{} + +LLViewerKeyboard::Keys::Keys() +: first_person("first_person", KeyMode(MODE_FIRST_PERSON)), + third_person("third_person", KeyMode(MODE_THIRD_PERSON)), + edit("edit", KeyMode(MODE_EDIT)), + sitting("sitting", KeyMode(MODE_SITTING)), + edit_avatar("edit_avatar", KeyMode(MODE_EDIT_AVATAR)) +{} + +S32 LLViewerKeyboard::loadBindingsXML(const std::string& filename) +{ + S32 binding_count = 0; + Keys keys; + LLSimpleXUIParser parser; + + if (parser.readXUI(filename, keys) + && keys.validateBlock()) + { + binding_count += loadBindingMode(keys.first_person); + binding_count += loadBindingMode(keys.third_person); + binding_count += loadBindingMode(keys.edit); + binding_count += loadBindingMode(keys.sitting); + binding_count += loadBindingMode(keys.edit_avatar); + } + return binding_count; +} + +S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode) +{ + S32 binding_count = 0; + for (LLInitParam::ParamIterator<KeyBinding>::const_iterator it = keymode.bindings.begin(), + end_it = keymode.bindings.end(); + it != end_it; + ++it) + { + KEY key; + MASK mask; + LLKeyboard::keyFromString(it->key, &key); + LLKeyboard::maskFromString(it->mask, &mask); + bindKey(keymode.mode, key, mask, it->command); + binding_count++; + } + + return binding_count; +} S32 LLViewerKeyboard::loadBindings(const std::string& filename) { @@ -912,18 +959,18 @@ void LLViewerKeyboard::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_lev if (key_down && !repeat) { // ...key went down this frame, call function - (*binding[i].mFunction)( KEYSTATE_DOWN ); + binding[i].mFunction( KEYSTATE_DOWN ); } else if (key_up) { // ...key went down this frame, call function - (*binding[i].mFunction)( KEYSTATE_UP ); + binding[i].mFunction( KEYSTATE_UP ); } else if (key_level) { // ...key held down from previous frame // Not windows, just call the function. - (*binding[i].mFunction)( KEYSTATE_LEVEL ); + binding[i].mFunction( KEYSTATE_LEVEL ); }//if }//if }//for diff --git a/indra/newview/llviewerkeyboard.h b/indra/newview/llviewerkeyboard.h index 2fa5d5dfa6..925244e89b 100644 --- a/indra/newview/llviewerkeyboard.h +++ b/indra/newview/llviewerkeyboard.h @@ -55,26 +55,51 @@ typedef enum e_keyboard_mode void bind_keyboard_functions(); - class LLViewerKeyboard { public: + struct KeyBinding : public LLInitParam::Block<KeyBinding> + { + Mandatory<std::string> key, + mask, + command; + + KeyBinding(); + }; + + struct KeyMode : public LLInitParam::Block<KeyMode> + { + Multiple<KeyBinding> bindings; + EKeyboardMode mode; + KeyMode(EKeyboardMode mode); + }; + + struct Keys : public LLInitParam::Block<Keys> + { + Optional<KeyMode> first_person, + third_person, + edit, + sitting, + edit_avatar; + + Keys(); + }; + LLViewerKeyboard(); BOOL handleKey(KEY key, MASK mask, BOOL repeated); - void bindNamedFunction(const std::string& name, LLKeyFunc func); - S32 loadBindings(const std::string& filename); // returns number bound, 0 on error + S32 loadBindingsXML(const std::string& filename); // returns number bound, 0 on error EKeyboardMode getMode(); BOOL modeFromString(const std::string& string, S32 *mode); // False on failure void scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level); -protected: + +private: + S32 loadBindingMode(const LLViewerKeyboard::KeyMode& keymode); BOOL bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name); - S32 mNamedFunctionCount; - LLNamedFunction mNamedFunctions[MAX_NAMED_FUNCTIONS]; // Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here S32 mBindingCount[MODE_COUNT]; diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 48ab122edf..d3b6dcd86f 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -52,6 +52,7 @@ #include "llviewerregion.h" #include "llwebsharing.h" // For LLWebSharing::setOpenIDCookie(), *TODO: find a better way to do this! #include "llfilepicker.h" +#include "llnotifications.h" #include "llevent.h" // LLSimpleListener #include "llnotificationsutil.h" @@ -62,6 +63,7 @@ #include "llwindow.h" #include "llfloatermediabrowser.h" // for handling window close requests and geometry change requests in media browser windows. +#include "llfloaterwebcontent.h" // for handling window close requests and geometry change requests in media browser windows. #include <boost/bind.hpp> // for SkinFolder listener #include <boost/signals2.hpp> @@ -293,6 +295,7 @@ public: LLPluginCookieStore *LLViewerMedia::sCookieStore = NULL; LLURL LLViewerMedia::sOpenIDURL; std::string LLViewerMedia::sOpenIDCookie; +LLPluginClassMedia* LLViewerMedia::sSpareBrowserMediaSource = NULL; static LLViewerMedia::impl_list sViewerMediaImplList; static LLViewerMedia::impl_id_map sViewerMediaTextureIDMap; static LLTimer sMediaCreateTimer; @@ -492,7 +495,7 @@ std::string LLViewerMedia::getCurrentUserAgent() // Just in case we need to check browser differences in A/B test // builds. - std::string channel = gSavedSettings.getString("VersionChannelName"); + std::string channel = LLVersionInfo::getChannel(); // append our magic version number string to the browser user agent id // See the HTTP 1.0 and 1.1 specifications for allowed formats: @@ -742,6 +745,9 @@ void LLViewerMedia::updateMedia(void *dummy_arg) // Enable/disable the plugin read thread LLPluginProcessParent::setUseReadThread(gSavedSettings.getBOOL("PluginUseReadThread")); + // HACK: we always try to keep a spare running webkit plugin around to improve launch times. + createSpareBrowserMediaSource(); + sAnyMediaShowing = false; sUpdatedCookies = getCookieStore()->getChangedCookies(); if(!sUpdatedCookies.empty()) @@ -759,6 +765,12 @@ void LLViewerMedia::updateMedia(void *dummy_arg) pimpl->update(); pimpl->calculateInterest(); } + + // Let the spare media source actually launch + if(sSpareBrowserMediaSource) + { + sSpareBrowserMediaSource->idle(); + } // Sort the static instance list using our interest criteria sViewerMediaImplList.sort(priorityComparitor); @@ -1034,6 +1046,26 @@ bool LLViewerMedia::isParcelAudioPlaying() return (LLViewerMedia::hasParcelAudio() && gAudiop && LLAudioEngine::AUDIO_PLAYING == gAudiop->isInternetStreamPlaying()); } +void LLViewerMedia::onAuthSubmit(const LLSD& notification, const LLSD& response) +{ + LLViewerMediaImpl *impl = LLViewerMedia::getMediaImplFromTextureID(notification["payload"]["media_id"]); + if(impl) + { + LLPluginClassMedia* media = impl->getMediaPlugin(); + if(media) + { + if (response["ok"]) + { + media->sendAuthResponse(true, response["username"], response["password"]); + } + else + { + media->sendAuthResponse(false, "", ""); + } + } + } +} + ///////////////////////////////////////////////////////////////////////////////////////// // static void LLViewerMedia::clearAllCookies() @@ -1083,7 +1115,7 @@ void LLViewerMedia::clearAllCookies() } // the hard part: iterate over all user directories and delete the cookie file from each one - while(gDirUtilp->getNextFileInDir(base_dir, "*_*", filename, false)) + while(gDirUtilp->getNextFileInDir(base_dir, "*_*", filename)) { target = base_dir; target += filename; @@ -1400,6 +1432,29 @@ void LLViewerMedia::proxyWindowClosed(const std::string &uuid) } } +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::createSpareBrowserMediaSource() +{ + if(!sSpareBrowserMediaSource) + { + // If we don't have a spare browser media source, create one. + // The null owner will keep the browser plugin from fully initializing + // (specifically, it keeps LLPluginClassMedia from negotiating a size change, + // which keeps MediaPluginWebkit::initBrowserWindow from doing anything until we have some necessary data, like the background color) + sSpareBrowserMediaSource = LLViewerMediaImpl::newSourceFromMediaType("text/html", NULL, 0, 0); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource() +{ + LLPluginClassMedia* result = sSpareBrowserMediaSource; + sSpareBrowserMediaSource = NULL; + return result; +}; + bool LLViewerMedia::hasInWorldMedia() { if (sInWorldMediaDisabled) return false; @@ -1636,6 +1691,21 @@ void LLViewerMediaImpl::setMediaType(const std::string& media_type) LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target) { std::string plugin_basename = LLMIMETypes::implType(media_type); + LLPluginClassMedia* media_source = NULL; + + // HACK: we always try to keep a spare running webkit plugin around to improve launch times. + if(plugin_basename == "media_plugin_webkit") + { + media_source = LLViewerMedia::getSpareBrowserMediaSource(); + if(media_source) + { + media_source->setOwner(owner); + media_source->setTarget(target); + media_source->setSize(default_width, default_height); + + return media_source; + } + } if(plugin_basename.empty()) { @@ -1673,7 +1743,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ } else { - LLPluginClassMedia* media_source = new LLPluginClassMedia(owner); + media_source = new LLPluginClassMedia(owner); media_source->setSize(default_width, default_height); media_source->setUserDataPath(user_data_path); media_source->setLanguageCode(LLUI::getLanguage()); @@ -1753,6 +1823,22 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) media_source->focus(mHasFocus); media_source->setBackgroundColor(mBackgroundColor); + if(gSavedSettings.getBOOL("BrowserIgnoreSSLCertErrors")) + { + media_source->ignore_ssl_cert_errors(true); + } + + // start by assuming the default CA file will be used + std::string ca_path = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "lindenlab.pem" ); + + // default turned off so pick up the user specified path + if( ! gSavedSettings.getBOOL("BrowserUseDefaultCAFile")) + { + ca_path = gSavedSettings.getString("BrowserCAFilePath"); + } + // set the path to the CA.pem file + media_source->addCertificateFilePath( ca_path ); + media_source->proxy_setup(gSavedSettings.getBOOL("BrowserProxyEnabled"), gSavedSettings.getString("BrowserProxyAddress"), gSavedSettings.getS32("BrowserProxyPort")); if(mClearCache) @@ -1849,6 +1935,18 @@ void LLViewerMediaImpl::setSize(int width, int height) } ////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::showNotification(LLNotificationPtr notify) +{ + mNotification = notify; +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::hideNotification() +{ + mNotification.reset(); +} + +////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::play() { // If the media source isn't there, try to initialize it and load an URL. @@ -2850,7 +2948,6 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla LL_DEBUGS("Media") << "MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is: " << plugin->getClickURL() << LL_ENDL; std::string url = plugin->getClickURL(); LLURLDispatcher::dispatch(url, NULL, mTrustedBrowser); - } break; case MEDIA_EVENT_CLICK_LINK_HREF: @@ -2913,6 +3010,7 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_BEGIN: { LL_DEBUGS("Media") << "MEDIA_EVENT_NAVIGATE_BEGIN, uri is: " << plugin->getNavigateURI() << LL_ENDL; + hideNotification(); if(getNavState() == MEDIANAVSTATE_SERVER_SENT) { @@ -3003,7 +3101,26 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla plugin->sendPickFileResponse(response); } break; - + + + case LLViewerMediaObserver::MEDIA_EVENT_AUTH_REQUEST: + { + LLNotification::Params auth_request_params; + auth_request_params.name = "AuthRequest"; + + // pass in host name and realm for site (may be zero length but will always exist) + LLSD args; + LLURL raw_url( plugin->getAuthURL().c_str() ); + args["HOST_NAME"] = raw_url.getAuthority(); + args["REALM"] = plugin->getAuthRealm(); + auth_request_params.substitutions = args; + + auth_request_params.payload = LLSD().with("media_id", mTextureId); + auth_request_params.functor.function = boost::bind(&LLViewerMedia::onAuthSubmit, _1, _2); + LLNotifications::instance().add(auth_request_params); + }; + break; + case LLViewerMediaObserver::MEDIA_EVENT_CLOSE_REQUEST: { std::string uuid = plugin->getClickUUID(); @@ -3019,6 +3136,7 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla // This close request is directed at another instance pass_through = false; LLFloaterMediaBrowser::closeRequest(uuid); + LLFloaterWebContent::closeRequest(uuid); } } break; @@ -3038,6 +3156,7 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla // This request is directed at another instance pass_through = false; LLFloaterMediaBrowser::geometryChanged(uuid, plugin->getGeometryX(), plugin->getGeometryY(), plugin->getGeometryWidth(), plugin->getGeometryHeight()); + LLFloaterWebContent::geometryChanged(uuid, plugin->getGeometryX(), plugin->getGeometryY(), plugin->getGeometryWidth(), plugin->getGeometryHeight()); } } break; @@ -3521,6 +3640,11 @@ bool LLViewerMediaImpl::isInAgentParcel() const return result; } +LLNotificationPtr LLViewerMediaImpl::getCurrentNotification() const +{ + return mNotification; +} + ////////////////////////////////////////////////////////////////////////////////////////// // // static diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 4025a4484f..e2e342cc45 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -37,6 +37,7 @@ #include "llpluginclassmedia.h" #include "v4color.h" +#include "llnotificationptr.h" #include "llurl.h" @@ -130,6 +131,8 @@ public: static bool isParcelMediaPlaying(); static bool isParcelAudioPlaying(); + static void onAuthSubmit(const LLSD& notification, const LLSD& response); + // Clear all cookies for all plugins static void clearAllCookies(); @@ -155,6 +158,9 @@ public: static void proxyWindowOpened(const std::string &target, const std::string &uuid); static void proxyWindowClosed(const std::string &uuid); + static void createSpareBrowserMediaSource(); + static LLPluginClassMedia* getSpareBrowserMediaSource(); + private: static void setOpenIDCookie(); static void onTeleportFinished(); @@ -162,6 +168,7 @@ private: static LLPluginCookieStore *sCookieStore; static LLURL sOpenIDURL; static std::string sOpenIDCookie; + static LLPluginClassMedia* sSpareBrowserMediaSource; }; // Implementation functions not exported into header file @@ -195,6 +202,9 @@ public: LLPluginClassMedia* getMediaPlugin() { return mMediaSource; } void setSize(int width, int height); + void showNotification(LLNotificationPtr notify); + void hideNotification(); + void play(); void stop(); void pause(); @@ -387,6 +397,9 @@ public: // Is this media in the agent's parcel? bool isInAgentParcel() const; + // get currently active notification associated with this media instance + LLNotificationPtr getCurrentNotification() const; + private: bool isAutoPlayable() const; bool shouldShowBasedOnClass() const; @@ -444,7 +457,8 @@ private: bool mNavigateSuspendedDeferred; bool mTrustedBrowser; std::string mTarget; - + LLNotificationPtr mNotification; + private: BOOL mIsUpdated ; std::list< LLVOVolume* > mObjectList ; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index ccf3df827d..9e16bf2fbb 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -45,7 +45,7 @@ #include "llconsole.h" #include "lldebugview.h" #include "llfilepicker.h" -//#include "llfirstuse.h" +#include "llfirstuse.h" #include "llfloaterbuy.h" #include "llfloaterbuycontents.h" #include "llbuycurrencyhtml.h" @@ -191,6 +191,8 @@ BOOL is_selection_buy_not_take(); S32 selection_price(); BOOL enable_take(); void handle_take(); +void handle_object_show_inspector(); +void handle_avatar_show_inspector(); bool confirm_take(const LLSD& notification, const LLSD& response); void handle_buy_object(LLSaleInfo sale_info); @@ -221,8 +223,6 @@ BOOL check_show_xui_names(void *); // Debug UI void handle_buy_currency_test(void*); -void handle_save_to_xml(void*); -void handle_load_from_xml(void*); void handle_god_mode(void*); @@ -843,6 +843,35 @@ class LLAdvancedCheckFeature : public view_listener_t } }; +void LLDestinationAndAvatarShow(const LLSD& value) +{ + S32 panel_idx = value.isDefined() ? value.asInteger() : -1; + LLView* container = gViewerWindow->getRootView()->getChildView("avatar_picker_and_destination_guide_container"); + LLMediaCtrl* destinations = container->findChild<LLMediaCtrl>("destination_guide_contents"); + LLMediaCtrl* avatar_picker = container->findChild<LLMediaCtrl>("avatar_picker_contents"); + + switch(panel_idx) + { + case 0: + container->setVisible(true); + destinations->setVisible(true); + avatar_picker->setVisible(false); + LLFirstUse::notUsingDestinationGuide(false); + break; + case 1: + container->setVisible(true); + destinations->setVisible(false); + avatar_picker->setVisible(true); + LLFirstUse::notUsingAvatarPicker(false); + break; + default: + container->setVisible(false); + destinations->setVisible(false); + avatar_picker->setVisible(false); + break; + } +}; + ////////////////// // INFO DISPLAY // @@ -1384,37 +1413,6 @@ class LLAdvancedCheckDebugWindowProc : public view_listener_t // ------------------------------XUI MENU --------------------------- -////////////////////// -// LOAD UI FROM XML // -////////////////////// - - -class LLAdvancedLoadUIFromXML : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_load_from_xml(NULL); - return true; -} -}; - - - -//////////////////// -// SAVE UI TO XML // -//////////////////// - - -class LLAdvancedSaveUIToXML : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_save_to_xml(NULL); - return true; -} -}; - - class LLAdvancedSendTestIms : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -1994,6 +1992,16 @@ class LLAdvancedShowDebugSettings : public view_listener_t // VIEW ADMIN OPTIONS // //////////////////////// +class LLAdvancedEnableViewAdminOptions : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // Don't enable in god mode since the admin menu is shown anyway. + // Only enable if the user has set the appropriate debug setting. + bool new_value = !gAgent.getAgentAccess().isGodlikeWithoutAdminMenuFakery() && gSavedSettings.getBOOL("AdminMenu"); + return new_value; + } +}; class LLAdvancedToggleViewAdminOptions : public view_listener_t { @@ -2008,7 +2016,7 @@ class LLAdvancedCheckViewAdminOptions : public view_listener_t { bool handleEvent(const LLSD& userdata) { - bool new_value = check_admin_override(NULL); + bool new_value = check_admin_override(NULL) || gAgent.isGodlike(); return new_value; } }; @@ -4160,6 +4168,11 @@ class LLObjectEnableReturn : public view_listener_t { bool handleEvent(const LLSD& userdata) { + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) + { + // Do not enable if nothing selected + return false; + } #ifdef HACKED_GODLIKE_VIEWER bool new_value = true; #else @@ -4312,6 +4325,33 @@ void handle_take() } } +void handle_object_show_inspector() +{ + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + LLViewerObject* objectp = selection->getFirstRootObject(TRUE); + if (!objectp) + { + return; + } + + LLSD params; + params["object_id"] = objectp->getID(); + LLFloaterReg::showInstance("inspect_object", params); +} + +void handle_avatar_show_inspector() +{ + LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); + if(avatar) + { + LLSD params; + params["avatar_id"] = avatar->getID(); + LLFloaterReg::showInstance("inspect_avatar", params); + } +} + + + bool confirm_take(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -6518,16 +6558,6 @@ class LLToggleControl : public view_listener_t std::string control_name = userdata.asString(); BOOL checked = gSavedSettings.getBOOL( control_name ); gSavedSettings.setBOOL( control_name, !checked ); - - // Doubleclick actions - there can be only one - if ((control_name == "DoubleClickAutoPilot") && !checked) - { - gSavedSettings.setBOOL( "DoubleClickTeleport", FALSE ); - } - else if ((control_name == "DoubleClickTeleport") && !checked) - { - gSavedSettings.setBOOL( "DoubleClickAutoPilot", FALSE ); - } return true; } }; @@ -7183,44 +7213,6 @@ const LLRect LLViewerMenuHolderGL::getMenuRect() const return LLRect(0, getRect().getHeight() - MENU_BAR_HEIGHT, getRect().getWidth(), STATUS_BAR_HEIGHT); } -void handle_save_to_xml(void*) -{ - LLFloater* frontmost = gFloaterView->getFrontmost(); - if (!frontmost) - { - LLNotificationsUtil::add("NoFrontmostFloater"); - return; - } - - std::string default_name = "floater_"; - default_name += frontmost->getTitle(); - default_name += ".xml"; - - LLStringUtil::toLower(default_name); - LLStringUtil::replaceChar(default_name, ' ', '_'); - LLStringUtil::replaceChar(default_name, '/', '_'); - LLStringUtil::replaceChar(default_name, ':', '_'); - LLStringUtil::replaceChar(default_name, '"', '_'); - - LLFilePicker& picker = LLFilePicker::instance(); - if (picker.getSaveFile(LLFilePicker::FFSAVE_XML, default_name)) - { - std::string filename = picker.getFirstFile(); - LLUICtrlFactory::getInstance()->saveToXML(frontmost, filename); - } -} - -void handle_load_from_xml(void*) -{ - LLFilePicker& picker = LLFilePicker::instance(); - if (picker.getOpenFile(LLFilePicker::FFLOAD_XML)) - { - std::string filename = picker.getFirstFile(); - LLFloater* floater = new LLFloater(LLSD()); - floater->buildFromFile(filename); - } -} - void handle_web_browser_test(const LLSD& param) { std::string url = param.asString(); @@ -7231,6 +7223,12 @@ void handle_web_browser_test(const LLSD& param) LLWeb::loadURLInternal(url); } +void handle_web_content_test(const LLSD& param) +{ + std::string url = param.asString(); + LLWeb::loadWebURLInternal(url); +} + void handle_buy_currency_test(void*) { std::string url = @@ -7861,6 +7859,9 @@ void initialize_menus() view_listener_t::addMenu(new LLViewCheckRenderType(), "View.CheckRenderType"); view_listener_t::addMenu(new LLViewCheckHUDAttachments(), "View.CheckHUDAttachments"); + // Me > Movement + view_listener_t::addMenu(new LLAdvancedAgentFlyingInfo(), "Agent.getFlying"); + // World menu commit.add("World.Chat", boost::bind(&handle_chat, (void*)NULL)); view_listener_t::addMenu(new LLWorldAlwaysRun(), "World.AlwaysRun"); @@ -7934,9 +7935,6 @@ void initialize_menus() // Advanced Other Settings view_listener_t::addMenu(new LLAdvancedClearGroupCache(), "Advanced.ClearGroupCache"); - - // Advanced > Shortcuts - view_listener_t::addMenu(new LLAdvancedAgentFlyingInfo(), "Agent.getFlying"); // Advanced > Render > Types view_listener_t::addMenu(new LLAdvancedToggleRenderType(), "Advanced.ToggleRenderType"); @@ -7981,7 +7979,8 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedDumpRegionObjectCache(), "Advanced.DumpRegionObjectCache"); // Advanced > UI - commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test, _2)); + commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test, _2)); // sigh! this one opens the MEDIA browser + commit.add("Advanced.WebContentTest", boost::bind(&handle_web_content_test, _2)); // this one opens the Web Content floater view_listener_t::addMenu(new LLAdvancedBuyCurrencyTest(), "Advanced.BuyCurrencyTest"); view_listener_t::addMenu(new LLAdvancedDumpSelectMgr(), "Advanced.DumpSelectMgr"); view_listener_t::addMenu(new LLAdvancedDumpInventory(), "Advanced.DumpInventory"); @@ -8006,8 +8005,6 @@ void initialize_menus() // Advanced > XUI commit.add("Advanced.ReloadColorSettings", boost::bind(&LLUIColorTable::loadFromSettings, LLUIColorTable::getInstance())); - view_listener_t::addMenu(new LLAdvancedLoadUIFromXML(), "Advanced.LoadUIFromXML"); - view_listener_t::addMenu(new LLAdvancedSaveUIToXML(), "Advanced.SaveUIToXML"); view_listener_t::addMenu(new LLAdvancedToggleXUINames(), "Advanced.ToggleXUINames"); view_listener_t::addMenu(new LLAdvancedCheckXUINames(), "Advanced.CheckXUINames"); view_listener_t::addMenu(new LLAdvancedSendTestIms(), "Advanced.SendTestIMs"); @@ -8068,6 +8065,7 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedCheckShowObjectUpdates(), "Advanced.CheckShowObjectUpdates"); view_listener_t::addMenu(new LLAdvancedCompressImage(), "Advanced.CompressImage"); view_listener_t::addMenu(new LLAdvancedShowDebugSettings(), "Advanced.ShowDebugSettings"); + view_listener_t::addMenu(new LLAdvancedEnableViewAdminOptions(), "Advanced.EnableViewAdminOptions"); view_listener_t::addMenu(new LLAdvancedToggleViewAdminOptions(), "Advanced.ToggleViewAdminOptions"); view_listener_t::addMenu(new LLAdvancedCheckViewAdminOptions(), "Advanced.CheckViewAdminOptions"); view_listener_t::addMenu(new LLAdvancedRequestAdminStatus(), "Advanced.RequestAdminStatus"); @@ -8113,6 +8111,7 @@ void initialize_menus() view_listener_t::addMenu(new LLAvatarVisibleDebug(), "Avatar.VisibleDebug"); view_listener_t::addMenu(new LLAvatarInviteToGroup(), "Avatar.InviteToGroup"); commit.add("Avatar.Eject", boost::bind(&handle_avatar_eject, LLSD())); + commit.add("Avatar.ShowInspector", boost::bind(&handle_avatar_show_inspector)); view_listener_t::addMenu(new LLAvatarSendIM(), "Avatar.SendIM"); view_listener_t::addMenu(new LLAvatarCall(), "Avatar.Call"); enable.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall)); @@ -8140,6 +8139,7 @@ void initialize_menus() commit.add("Object.Inspect", boost::bind(&handle_object_inspect)); commit.add("Object.Open", boost::bind(&handle_object_open)); commit.add("Object.Take", boost::bind(&handle_take)); + commit.add("Object.ShowInspector", boost::bind(&handle_object_show_inspector)); enable.add("Object.EnableOpen", boost::bind(&enable_object_open)); enable.add("Object.EnableTouch", boost::bind(&enable_object_touch, _1)); enable.add("Object.EnableDelete", boost::bind(&enable_object_delete)); @@ -8199,4 +8199,6 @@ void initialize_menus() view_listener_t::addMenu(new LLEditableSelectedMono(), "EditableSelectedMono"); view_listener_t::addMenu(new LLToggleUIHints(), "ToggleUIHints"); + + commit.add("DestinationAndAvatar.show", boost::bind(&LLDestinationAndAvatarShow, _2)); } diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 237aa39e6e..fda291f3c1 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -507,7 +507,7 @@ void upload_new_resource(const std::string& src_filename, std::string name, "No file extension for the file: '%s'\nPlease make sure the file has a correct file extension", short_name.c_str()); args["FILE"] = short_name; - upload_error(error_message, "NofileExtension", filename, args); + upload_error(error_message, "NoFileExtension", filename, args); return; } else if( exten == "bmp") diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 9b1f2e67c6..7dc5d96689 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -39,6 +39,7 @@ #include "llfloaterreg.h" #include "llfollowcamparams.h" #include "llinventorydefines.h" +#include "lllslconstants.h" #include "llregionhandle.h" #include "llsdserialize.h" #include "llteleportflags.h" @@ -170,6 +171,31 @@ const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] = FALSE // ControlYourCamera }; +// Extract channel and version from a string like "SL Web Viewer Beta 10.11.29.215604". +// (channel: "SL Web Viewer Beta", version: "10.11.29.215604") +static bool parse_version_info(const std::string& version_info, std::string& channel, std::string& ver) +{ + size_t last_space = version_info.rfind(" "); + channel = version_info; + + if (last_space != std::string::npos) + { + try + { + ver = version_info.substr(last_space + 1); + channel.replace(last_space, ver.length() + 1, ""); // strip version + } + catch (std::out_of_range) + { + return false; + } + + return true; + } + + return false; +} + bool friendship_offer_callback(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -638,7 +664,7 @@ bool join_group_response(const LLSD& notification, const LLSD& response) if(option == 0 && !group_id.isNull()) { // check for promotion or demotion. - S32 max_groups = MAX_AGENT_GROUPS; + S32 max_groups = gMaxAgentGroups; if(gAgent.isInGroup(group_id)) ++max_groups; if(gAgent.mGroups.count() < max_groups) @@ -1199,7 +1225,6 @@ void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_nam const BOOL auto_open = gSavedSettings.getBOOL("ShowInInventory") && // don't open if showininventory is false !(asset_type == LLAssetType::AT_CALLINGCARD) && // don't open if it's a calling card - !(item && (item->getInventoryType() == LLInventoryType::IT_ATTACHMENT)) && // don't open if it's an item that's an attachment !from_name.empty(); // don't open if it's not from anyone. LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open); if(active_panel) @@ -1511,7 +1536,12 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& // MUTE falls through to decline case IOR_DECLINE: { - log_message = LLTrans::getString("InvOfferYouDecline") + " " + mDesc + " " + LLTrans::getString("InvOfferFrom") + " " + mFromName +"."; + { + LLStringUtil::format_map_t log_message_args; + log_message_args["DESC"] = mDesc; + log_message_args["NAME"] = mFromName; + log_message = LLTrans::getString("InvOfferDecline", log_message_args); + } chat.mText = log_message; if( LLMuteList::getInstance()->isMuted(mFromID ) && ! LLMuteList::getInstance()->isLinden(mFromName) ) // muting for SL-42269 { @@ -1710,8 +1740,12 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE); // send the message msg->sendReliable(mHost); - - log_message = LLTrans::getString("InvOfferYouDecline") + " " + mDesc + " " + LLTrans::getString("InvOfferFrom") + " " + mFromName +"."; + { + LLStringUtil::format_map_t log_message_args; + log_message_args["DESC"] = mDesc; + log_message_args["NAME"] = mFromName; + log_message = LLTrans::getString("InvOfferDecline", log_message_args); + } LLSD args; args["MESSAGE"] = log_message; LLNotificationsUtil::add("SystemMessage", args); @@ -2501,15 +2535,6 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) invite_bucket = (struct invite_bucket_t*) &binary_bucket[0]; S32 membership_fee = ntohl(invite_bucket->membership_fee); - // IDEVO Clean up legacy name "Resident" in message constructed in - // lldatagroups.cpp - U32 pos = message.find(" has invited you to join a group.\n"); - if (pos != std::string::npos) - { - // use cleaned-up name from above - message = name + message.substr(pos); - } - LLSD payload; payload["transaction_id"] = session_id; payload["group_id"] = from_id; @@ -3032,6 +3057,7 @@ void process_offer_callingcard(LLMessageSystem* msg, void**) } else { + args["NAME"] = source_name; LLNotificationsUtil::add("OfferCallingCard", args, payload); } } @@ -3346,6 +3372,8 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) // then this info is news to us. void process_teleport_start(LLMessageSystem *msg, void**) { + // on teleport, don't tell them about destination guide anymore + LLFirstUse::notUsingDestinationGuide(false); U32 teleport_flags = 0x0; msg->getU32("Info", "TeleportFlags", teleport_flags); @@ -3822,28 +3850,22 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) if (!gLastVersionChannel.empty()) { - // work out the URL for this server's Release Notes - std::string url ="http://wiki.secondlife.com/wiki/Release_Notes/"; - std::string server_version = version_channel; - std::vector<std::string> s_vect; - boost::algorithm::split(s_vect, server_version, isspace); - for(U32 i = 0; i < s_vect.size(); i++) + std::string url = regionp->getCapability("ServerReleaseNotes"); + if (url.empty()) { - if (i != (s_vect.size() - 1)) + // The capability hasn't arrived yet or is not supported, + // fall back to parsing server version channel. + std::string channel, ver; + if (!parse_version_info(version_channel, channel, ver)) { - if(i != (s_vect.size() - 2)) - { - url += s_vect[i] + "_"; - } - else - { - url += s_vect[i] + "/"; - } - } - else - { - url += s_vect[i].substr(0,4); + llwarns << "Failed to parse server version channel (" << version_channel << ")" << llendl; } + + url = gSavedSettings.getString("ReleaseNotesURL"); + LLSD args; + args["CHANNEL"] = LLWeb::escapeURL(channel); + args["VERSION"] = LLWeb::escapeURL(ver); + LLStringUtil::format(url, args); } LLSD args; @@ -3867,6 +3889,7 @@ void process_crossed_region(LLMessageSystem* msg, void**) return; } LL_INFOS("Messaging") << "process_crossed_region()" << LL_ENDL; + gAgentAvatarp->resetRegionCrossingTimer(); U32 sim_ip; msg->getIPAddrFast(_PREHASH_RegionData, _PREHASH_SimIP, sim_ip); @@ -6302,6 +6325,9 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response) payload["from_id"] = target_id; payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("TeleportOfferSent", args, payload); + + // Add the recepient to the recent people list. + LLRecentPeople::instance().add(target_id); } } gAgent.sendReliableMessage(); @@ -6435,8 +6461,22 @@ const char* SCRIPT_DIALOG_HEADER = "Script Dialog:\n"; bool callback_script_dialog(const LLSD& notification, const LLSD& response) { LLNotificationForm form(notification["form"]); - std::string button = LLNotification::getSelectedOptionName(response); - S32 button_idx = LLNotification::getSelectedOption(notification, response); + + std::string rtn_text; + S32 button_idx; + button_idx = LLNotification::getSelectedOption(notification, response); + if (response[TEXTBOX_MAGIC_TOKEN].isDefined()) + { + if (response[TEXTBOX_MAGIC_TOKEN].isString()) + rtn_text = response[TEXTBOX_MAGIC_TOKEN].asString(); + else + rtn_text.clear(); // bool marks empty string + } + else + { + rtn_text = LLNotification::getSelectedOptionName(response); + } + // Didn't click "Ignore" if (button_idx != -1) { @@ -6449,7 +6489,7 @@ bool callback_script_dialog(const LLSD& notification, const LLSD& response) msg->addUUID("ObjectID", notification["payload"]["object_id"].asUUID()); msg->addS32("ChatChannel", notification["payload"]["chat_channel"].asInteger()); msg->addS32("ButtonIndex", button_idx); - msg->addString("ButtonLabel", button); + msg->addString("ButtonLabel", rtn_text); msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); } @@ -6670,6 +6710,8 @@ void process_initiate_download(LLMessageSystem* msg, void**) void process_script_teleport_request(LLMessageSystem* msg, void**) { + if (!gSavedSettings.getBOOL("ScriptsCanShowUI")) return; + std::string object_name; std::string sim_name; LLVector3 pos; diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index 6ff893f543..b4a9b8e677 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -117,7 +117,6 @@ void process_alert_core(const std::string& message, BOOL modal); typedef std::list<LLMeanCollisionData*> mean_collision_list_t; extern mean_collision_list_t gMeanCollisionList; -void handle_show_mean_events(void *); void process_mean_collision_alert_message(LLMessageSystem* msg, void**); void process_frozen_message(LLMessageSystem* msg, void**); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index a58b0d68de..1804fac1b3 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -103,8 +103,8 @@ //#define DEBUG_UPDATE_TYPE -BOOL gVelocityInterpolate = TRUE; -BOOL gPingInterpolate = TRUE; +BOOL LLViewerObject::sVelocityInterpolate = TRUE; +BOOL LLViewerObject::sPingInterpolate = TRUE; U32 LLViewerObject::sNumZombieObjects = 0; S32 LLViewerObject::sNumObjects = 0; @@ -115,6 +115,11 @@ S32 LLViewerObject::sAxisArrowLength(50); BOOL LLViewerObject::sPulseEnabled(FALSE); BOOL LLViewerObject::sUseSharedDrawables(FALSE); // TRUE +// sMaxUpdateInterpolationTime must be greater than sPhaseOutUpdateInterpolationTime +F64 LLViewerObject::sMaxUpdateInterpolationTime = 3.0; // For motion interpolation: after X seconds with no updates, don't predict object motion +F64 LLViewerObject::sPhaseOutUpdateInterpolationTime = 2.0; // For motion interpolation: after Y seconds with no updates, taper off motion prediction + + static LLFastTimer::DeclareTimer FTM_CREATE_OBJECT("Create Object"); // static @@ -168,8 +173,10 @@ LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pco res = new LLVOSurfacePatch(id, pcode, regionp); break; case LL_VO_SKY: res = new LLVOSky(id, pcode, regionp); break; + case LL_VO_VOID_WATER: + res = new LLVOVoidWater(id, pcode, regionp); break; case LL_VO_WATER: - res = new LLVOWater(id, pcode, regionp); break; + res = new LLVOWater(id, pcode, regionp); break; case LL_VO_GROUND: res = new LLVOGround(id, pcode, regionp); break; case LL_VO_PART_GROUP: @@ -203,6 +210,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mLastInterpUpdateSecs(0.f), mLastMessageUpdateSecs(0.f), mLatestRecvPacketID(0), + mCircuitPacketCount(0), mData(NULL), mAudioSourcep(NULL), mAudioGain(1.f), @@ -1839,7 +1847,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, new_rot.normQuat(); - if (gPingInterpolate) + if (sPingInterpolate) { LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mesgsys->getSender()); if (cdp) @@ -1860,6 +1868,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // // + // WTF? If we're going to skip this message, why are we + // doing all the parenting, etc above? U32 packet_id = mesgsys->getCurrentRecvPacketID(); if (packet_id < mLatestRecvPacketID && mLatestRecvPacketID - packet_id < 65536) @@ -1869,6 +1879,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, } mLatestRecvPacketID = packet_id; + mCircuitPacketCount = 0; // Set the change flags for scale if (new_scale != getScale()) @@ -2000,7 +2011,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // U32 ping_delay = mesgsys->mCircuitInfo.getPingDelay(); mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds(); - mLastMessageUpdateSecs = LLFrameTimer::getElapsedSeconds(); + mLastMessageUpdateSecs = mLastInterpUpdateSecs; if (mDrawable.notNull()) { // Don't clear invisibility flag on update if still orphaned! @@ -2027,6 +2038,8 @@ BOOL LLViewerObject::isActive() const return TRUE; } + + BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { static LLFastTimer::DeclareTimer ftm("Viewer Object"); @@ -2040,7 +2053,7 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) // CRO - don't velocity interp linked objects! // Leviathan - but DO velocity interp joints - if (!mStatic && gVelocityInterpolate && !isSelected()) + if (!mStatic && sVelocityInterpolate && !isSelected()) { // calculate dt from last update F32 dt_raw = (F32)(time - mLastInterpUpdateSecs); @@ -2130,33 +2143,8 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) return TRUE; } else - { - // linear motion - // PHYSICS_TIMESTEP is used below to correct for the fact that the velocity in object - // updates represents the average velocity of the last timestep, rather than the final velocity. - // the time dilation above should guarantee that dt is never less than PHYSICS_TIMESTEP, theoretically - // - // There is a problem here if dt is negative. . . - - // *TODO: should also wrap linear accel/velocity in check - // to see if object is selected, instead of explicitly - // zeroing it out - LLVector3 accel = getAcceleration(); - LLVector3 vel = getVelocity(); - - if (!(accel.isExactlyZero() && vel.isExactlyZero())) - { - LLVector3 pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt; - - // region local - setPositionRegion(pos + getPositionRegion()); - setVelocity(vel + accel*dt); - - // for objects that are spinning but not translating, make sure to flag them as having moved - setChanged(MOVED | SILHOUETTE); - } - - mLastInterpUpdateSecs = time; + { // Move object based on it's velocity and rotation + interpolateLinearMotion(time, dt); } } @@ -2172,6 +2160,158 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } +// Move an object due to idle-time viewer side updates by iterpolating motion +void LLViewerObject::interpolateLinearMotion(const F64 & time, const F32 & dt) +{ + // linear motion + // PHYSICS_TIMESTEP is used below to correct for the fact that the velocity in object + // updates represents the average velocity of the last timestep, rather than the final velocity. + // the time dilation above should guarantee that dt is never less than PHYSICS_TIMESTEP, theoretically + // + // *TODO: should also wrap linear accel/velocity in check + // to see if object is selected, instead of explicitly + // zeroing it out + + F64 time_since_last_update = time - mLastMessageUpdateSecs; + if (time_since_last_update <= 0.0 || dt <= 0.f) + { + return; + } + + LLVector3 accel = getAcceleration(); + LLVector3 vel = getVelocity(); + + if (sMaxUpdateInterpolationTime <= 0.0) + { // Old code path ... unbounded, simple interpolation + if (!(accel.isExactlyZero() && vel.isExactlyZero())) + { + LLVector3 pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt; + + // region local + setPositionRegion(pos + getPositionRegion()); + setVelocity(vel + accel*dt); + + // for objects that are spinning but not translating, make sure to flag them as having moved + setChanged(MOVED | SILHOUETTE); + } + } + else if (!accel.isExactlyZero() || !vel.isExactlyZero()) // object is moving + { // Object is moving, and hasn't been too long since we got an update from the server + + // Calculate predicted position and velocity + LLVector3 new_pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt; + LLVector3 new_v = accel * dt; + + if (time_since_last_update > sPhaseOutUpdateInterpolationTime) + { // Haven't seen a viewer update in a while, check to see if the ciruit is still active + if (mRegionp) + { // The simulator will NOT send updates if the object continues normally on the path + // predicted by the velocity and the acceleration (often gravity) sent to the viewer + // So check to see if the circuit is blocked, which means the sim is likely in a long lag + LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit( mRegionp->getHost() ); + if (cdp) + { + if (!cdp->isAlive() || // Circuit is dead or blocked + cdp->isBlocked() || // or doesn't seem to be getting any packets + (mCircuitPacketCount > 0 && mCircuitPacketCount == cdp->getPacketsIn())) + { + // Start to reduce motion interpolation since we haven't seen a server update in a while + F64 time_since_last_interpolation = time - mLastInterpUpdateSecs; + F64 phase_out = 1.0; + if (time_since_last_update > sMaxUpdateInterpolationTime) + { // Past the time limit, so stop the object + phase_out = 0.0; + //llinfos << "Motion phase out to zero" << llendl; + + // Kill angular motion as well. Note - not adding this due to paranoia + // about stopping rotation for llTargetOmega objects and not having it restart + // setAngularVelocity(LLVector3::zero); + } + else if (mLastInterpUpdateSecs - mLastMessageUpdateSecs > sPhaseOutUpdateInterpolationTime) + { // Last update was already phased out a bit + phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) / + (sMaxUpdateInterpolationTime - time_since_last_interpolation); + //llinfos << "Continuing motion phase out of " << (F32) phase_out << llendl; + } + else + { // Phase out from full value + phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) / + (sMaxUpdateInterpolationTime - sPhaseOutUpdateInterpolationTime); + //llinfos << "Starting motion phase out of " << (F32) phase_out << llendl; + } + phase_out = llclamp(phase_out, 0.0, 1.0); + + new_pos = new_pos * ((F32) phase_out); + new_v = new_v * ((F32) phase_out); + } + + // Save current circuit packet count to see if it changes + mCircuitPacketCount = cdp->getPacketsIn(); + } + } + } + + new_pos = new_pos + getPositionRegion(); + new_v = new_v + vel; + + + // Clamp interpolated position to minimum underground and maximum region height + LLVector3d new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos); + F32 min_height; + if (isAvatar()) + { // Make a better guess about AVs not going underground + min_height = LLWorld::getInstance()->resolveLandHeightGlobal(new_pos_global); + min_height += (0.5f * getScale().mV[VZ]); + } + else + { // This will put the object underground, but we can't tell if it will stop + // at ground level or not + min_height = LLWorld::getInstance()->getMinAllowedZ(this, new_pos_global); + } + + new_pos.mV[VZ] = llmax(min_height, new_pos.mV[VZ]); + new_pos.mV[VZ] = llmin(LLWorld::getInstance()->getRegionMaxHeight(), new_pos.mV[VZ]); + + // Check to see if it's going off the region + LLVector3 temp(new_pos); + if (temp.clamp(0.f, mRegionp->getWidth())) + { // Going off this region, so see if we might end up on another region + LLVector3d old_pos_global = mRegionp->getPosGlobalFromRegion(getPositionRegion()); + new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos); // Re-fetch in case it got clipped above + + // Clip the positions to known regions + LLVector3d clip_pos_global = LLWorld::getInstance()->clipToVisibleRegions(old_pos_global, new_pos_global); + if (clip_pos_global != new_pos_global) + { // Was clipped, so this means we hit a edge where there is no region to enter + + //llinfos << "Hit empty region edge, clipped predicted position to " << mRegionp->getPosRegionFromGlobal(clip_pos_global) + // << " from " << new_pos << llendl; + new_pos = mRegionp->getPosRegionFromGlobal(clip_pos_global); + + // Stop motion and get server update for bouncing on the edge + new_v.clear(); + setAcceleration(LLVector3::zero); + } + else + { // Let predicted movement cross into another region + //llinfos << "Predicting region crossing to " << new_pos << llendl; + } + } + + // Set new position and velocity + setPositionRegion(new_pos); + setVelocity(new_v); + + // for objects that are spinning but not translating, make sure to flag them as having moved + setChanged(MOVED | SILHOUETTE); + } + + // Update the last time we did anything + mLastInterpUpdateSecs = time; +} + + + BOOL LLViewerObject::setData(const U8 *datap, const U32 data_size) { LLMemType mt(LLMemType::MTYPE_OBJECT); @@ -4960,6 +5100,7 @@ void LLViewerObject::setRegion(LLViewerRegion *regionp) } mLatestRecvPacketID = 0; + mCircuitPacketCount = 0; mRegionp = regionp; for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) @@ -4972,6 +5113,20 @@ void LLViewerObject::setRegion(LLViewerRegion *regionp) updateDrawable(FALSE); } +// virtual +void LLViewerObject::updateRegion(LLViewerRegion *regionp) +{ +// if (regionp) +// { +// F64 now = LLFrameTimer::getElapsedSeconds(); +// llinfos << "Updating to region " << regionp->getName() +// << ", ms since last update message: " << (F32)((now - mLastMessageUpdateSecs) * 1000.0) +// << ", ms since last interpolation: " << (F32)((now - mLastInterpUpdateSecs) * 1000.0) +// << llendl; +// } +} + + bool LLViewerObject::specialHoverCursor() const { return (mFlags & FLAGS_USE_PHYSICS) diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 7d697a4a5f..fe670f8827 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -131,7 +131,7 @@ public: typedef const child_list_t const_child_list_t; - LLViewerObject(const LLUUID &id, const LLPCode type, LLViewerRegion *regionp, BOOL is_global = FALSE); + LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp, BOOL is_global = FALSE); MEM_TYPE_NEW(LLMemType::MTYPE_OBJECT); virtual void markDead(); // Mark this object as dead, and clean up its references @@ -464,7 +464,7 @@ public: bool specialHoverCursor() const; // does it have a special hover cursor? void setRegion(LLViewerRegion *regionp); - virtual void updateRegion(LLViewerRegion *regionp) {} + virtual void updateRegion(LLViewerRegion *regionp); void updateFlags(); BOOL setFlags(U32 flag, BOOL state); @@ -510,6 +510,9 @@ private: // and the update wasn't due to this agent's last action. U32 checkMediaURL(const std::string &media_url); + // Motion prediction between updates + void interpolateLinearMotion(const F64 & time, const F32 & dt); + public: // // Viewer-side only types - use the LL_PCODE_APP mask. @@ -518,14 +521,14 @@ public: { LL_VO_CLOUDS = LL_PCODE_APP | 0x20, LL_VO_SURFACE_PATCH = LL_PCODE_APP | 0x30, - //LL_VO_STARS = LL_PCODE_APP | 0x40, + LL_VO_WL_SKY = LL_PCODE_APP | 0x40, LL_VO_SQUARE_TORUS = LL_PCODE_APP | 0x50, LL_VO_SKY = LL_PCODE_APP | 0x60, - LL_VO_WATER = LL_PCODE_APP | 0x70, - LL_VO_GROUND = LL_PCODE_APP | 0x80, - LL_VO_PART_GROUP = LL_PCODE_APP | 0x90, - LL_VO_TRIANGLE_TORUS = LL_PCODE_APP | 0xa0, - LL_VO_WL_SKY = LL_PCODE_APP | 0xb0, // should this be moved to 0x40? + LL_VO_VOID_WATER = LL_PCODE_APP | 0x70, + LL_VO_WATER = LL_PCODE_APP | 0x80, + LL_VO_GROUND = LL_PCODE_APP | 0x90, + LL_VO_PART_GROUP = LL_PCODE_APP | 0xa0, + LL_VO_TRIANGLE_TORUS = LL_PCODE_APP | 0xb0, LL_VO_HUD_PART_GROUP = LL_PCODE_APP | 0xc0, } EVOType; @@ -612,6 +615,8 @@ protected: F64 mLastInterpUpdateSecs; // Last update for purposes of interpolation F64 mLastMessageUpdateSecs; // Last update from a message from the simulator TPACKETID mLatestRecvPacketID; // Latest time stamp on message from simulator + U32 mCircuitPacketCount; // Packet tracking for early detection of a stopped simulator circuit + // extra data sent from the sim...currently only used for tree species info U8* mData; @@ -669,9 +674,21 @@ protected: mutable LLVector3 mPositionRegion; mutable LLVector3 mPositionAgent; + static void setPhaseOutUpdateInterpolationTime(F32 value) { sPhaseOutUpdateInterpolationTime = (F64) value; } + static void setMaxUpdateInterpolationTime(F32 value) { sMaxUpdateInterpolationTime = (F64) value; } + + static void setVelocityInterpolate(BOOL value) { sVelocityInterpolate = value; } + static void setPingInterpolate(BOOL value) { sPingInterpolate = value; } + private: static S32 sNumObjects; + static F64 sPhaseOutUpdateInterpolationTime; // For motion interpolation + static F64 sMaxUpdateInterpolationTime; // For motion interpolation + + static BOOL sVelocityInterpolate; + static BOOL sPingInterpolate; + //-------------------------------------------------------------------- // For objects that are attachments //-------------------------------------------------------------------- @@ -717,8 +734,8 @@ public: class LLAlphaObject : public LLViewerObject { public: - LLAlphaObject(const LLUUID &id, const LLPCode type, LLViewerRegion *regionp) - : LLViewerObject(id,type,regionp) + LLAlphaObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) + : LLViewerObject(id,pcode,regionp) { mDepth = 0.f; } virtual F32 getPartSize(S32 idx); @@ -735,14 +752,12 @@ public: class LLStaticViewerObject : public LLViewerObject { public: - LLStaticViewerObject(const LLUUID& id, const LLPCode type, LLViewerRegion* regionp, BOOL is_global = FALSE) - : LLViewerObject(id,type,regionp, is_global) + LLStaticViewerObject(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp, BOOL is_global = FALSE) + : LLViewerObject(id,pcode,regionp, is_global) { } virtual void updateDrawable(BOOL force_damped); }; -extern BOOL gVelocityInterpolate; -extern BOOL gPingInterpolate; #endif diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index b597e6148e..f5a32438cf 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -165,6 +165,11 @@ BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object) { U32 local_id = object.mLocalID; LLHost region_host = object.getRegion()->getHost(); + if(!region_host.isOk()) + { + return FALSE ; + } + U32 ip = region_host.getAddress(); U32 port = region_host.getPort(); U64 ipport = (((U64)ip) << 32) | (U64)port; @@ -654,9 +659,24 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent) void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) { LLMemType mt(LLMemType::MTYPE_OBJECT); + // Update globals - gVelocityInterpolate = gSavedSettings.getBOOL("VelocityInterpolate"); - gPingInterpolate = gSavedSettings.getBOOL("PingInterpolate"); + LLViewerObject::setVelocityInterpolate( gSavedSettings.getBOOL("VelocityInterpolate") ); + LLViewerObject::setPingInterpolate( gSavedSettings.getBOOL("PingInterpolate") ); + + F32 interp_time = gSavedSettings.getF32("InterpolationTime"); + F32 phase_out_time = gSavedSettings.getF32("InterpolationPhaseOut"); + if (interp_time < 0.0 || + phase_out_time < 0.0 || + phase_out_time > interp_time) + { + llwarns << "Invalid values for InterpolationTime or InterpolationPhaseOut, resetting to defaults" << llendl; + interp_time = 3.0f; + phase_out_time = 1.0f; + } + LLViewerObject::setPhaseOutUpdateInterpolationTime( interp_time ); + LLViewerObject::setMaxUpdateInterpolationTime( phase_out_time ); + gAnimateTextures = gSavedSettings.getBOOL("AnimateTextures"); // update global timer @@ -1269,34 +1289,6 @@ void LLViewerObjectList::generatePickList(LLCamera &camera) } } -void LLViewerObjectList::renderPickList(const LLRect& screen_rect, BOOL pick_parcel_wall, BOOL render_transparent) -{ - gRenderForSelect = TRUE; - - gPipeline.renderForSelect(mSelectPickList, render_transparent, screen_rect); - - // - // Render pass for selected objects - // - gGL.color4f(1,1,1,1); - gViewerWindow->renderSelections( TRUE, pick_parcel_wall, FALSE ); - - //fix for DEV-19335. Don't pick hud objects when customizing avatar (camera mode doesn't play nice with nametags). - if (!gAgentCamera.cameraCustomizeAvatar()) - { - // render pickable ui elements, like names, etc. - LLHUDObject::renderAllForSelect(); - } - - gGL.flush(); - LLVertexBuffer::unbind(); - - gRenderForSelect = FALSE; - - //llinfos << "Rendered " << count << " for select" << llendl; - //llinfos << "Took " << pick_timer.getElapsedTimeF32()*1000.f << "ms to pick" << llendl; -} - LLViewerObject *LLViewerObjectList::getSelectedObject(const U32 object_id) { std::set<LLViewerObject*>::iterator pick_it; diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index eba5584b57..605bac8e89 100644 --- a/indra/newview/llviewerobjectlist.h +++ b/indra/newview/llviewerobjectlist.h @@ -104,7 +104,6 @@ public: // Selection related stuff void generatePickList(LLCamera &camera); - void renderPickList(const LLRect& screen_rect, BOOL pick_parcel_wall, BOOL render_transparent); LLViewerObject *getSelectedObject(const U32 object_id); diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 99e869dafc..40f0b43313 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -586,6 +586,18 @@ void LLViewerParcelMedia::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_GEOMETRY_CHANGE, uuid is " << self->getClickUUID() << LL_ENDL; } break; + + case MEDIA_EVENT_AUTH_REQUEST: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_AUTH_REQUEST, url " << self->getAuthURL() << ", realm " << self->getAuthRealm() << LL_ENDL; + } + break; + + case MEDIA_EVENT_LINK_HOVERED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << LL_ENDL; + }; + break; }; } diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 11de377410..fccd1156d3 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -850,7 +850,7 @@ LLParcel* LLViewerParcelMgr::getCollisionParcel() const void LLViewerParcelMgr::render() { - if (mSelected && mRenderSelection) + if (mSelected && mRenderSelection && gSavedSettings.getBOOL("RenderParcelSelection")) { // Rendering is done in agent-coordinates, so need to supply // an appropriate offset to the render code. @@ -1784,8 +1784,7 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use void optionally_start_music(const std::string& music_url) { - if (gSavedSettings.getBOOL("AudioStreamingMusic") && - gSavedSettings.getBOOL("AudioStreamingMedia")) + if (gSavedSettings.getBOOL("AudioStreamingMusic")) { // only play music when you enter a new parcel if the UI control for this // was not *explicitly* stopped by the user. (part of SL-4878) diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 7fb259e012..551ba18dd5 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -266,6 +266,7 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, //MUST MATCH declaration of eObjectPartitions mObjectPartition.push_back(new LLHUDPartition()); //PARTITION_HUD mObjectPartition.push_back(new LLTerrainPartition()); //PARTITION_TERRAIN + mObjectPartition.push_back(new LLVoidWaterPartition()); //PARTITION_VOIDWATER mObjectPartition.push_back(new LLWaterPartition()); //PARTITION_WATER mObjectPartition.push_back(new LLTreePartition()); //PARTITION_TREE mObjectPartition.push_back(new LLParticlePartition()); //PARTITION_PARTICLE @@ -1399,6 +1400,8 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("SendUserReportWithScreenshot"); capabilityNames.append("ServerReleaseNotes"); capabilityNames.append("SetDisplayName"); + capabilityNames.append("SimConsole"); + capabilityNames.append("SimConsoleAsync"); capabilityNames.append("StartGroupProposal"); capabilityNames.append("TextureStats"); capabilityNames.append("UntrustedSimulatorMessage"); @@ -1411,6 +1414,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("UpdateNotecardTaskInventory"); capabilityNames.append("UpdateScriptTask"); capabilityNames.append("UploadBakedTexture"); + capabilityNames.append("ViewerMetrics"); capabilityNames.append("ViewerStartAuction"); capabilityNames.append("ViewerStats"); capabilityNames.append("WebFetchInventoryDescendents"); diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 1ba025312b..8b71998f60 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -73,6 +73,7 @@ public: { PARTITION_HUD=0, PARTITION_TERRAIN, + PARTITION_VOIDWATER, PARTITION_WATER, PARTITION_TREE, PARTITION_PARTICLE, diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index d078c15316..c1abead36e 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -335,8 +335,8 @@ void LLViewerShaderMgr::setShaders() } else { - LLPipeline::sRenderGlow = - LLPipeline::sWaterReflections = FALSE; + LLPipeline::sRenderGlow = FALSE; + LLPipeline::sWaterReflections = FALSE; } //hack to reset buffers that change behavior with shaders diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 42266ad233..546ee9a334 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -48,6 +48,7 @@ #include "llagent.h" #include "llagentcamera.h" #include "llviewercontrol.h" +#include "llversioninfo.h" #include "llfloatertools.h" #include "lldebugview.h" #include "llfasttimerview.h" @@ -558,7 +559,7 @@ F32 gWorstLandCompression = 0.f, gWorstWaterCompression = 0.f; U32 gTotalWorldBytes = 0, gTotalObjectBytes = 0, gTotalTextureBytes = 0, gSimPingCount = 0; U32 gObjectBits = 0; F32 gAvgSimPing = 0.f; - +U32 gTotalTextureBytesPerBoostLevel[LLViewerTexture::MAX_GL_IMAGE_CATEGORY] = {0}; extern U32 gVisCompared; extern U32 gVisTested; @@ -749,7 +750,7 @@ void send_stats() // send fps only for time app spends in foreground agent["fps"] = (F32)gForegroundFrameCount / gForegroundTime.getElapsedTimeF32(); - agent["version"] = gCurrentVersion; + agent["version"] = LLVersionInfo::getChannelAndVersion(); std::string language = LLUI::getLanguage(); agent["language"] = language; diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index ca977d4599..3f9cfb9d9b 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -264,4 +264,6 @@ void send_stats(); extern std::map<S32,LLFrameTimer> gDebugTimers; extern std::map<S32,std::string> gDebugTimerLabel; extern U32 gTotalTextureBytes; +extern U32 gTotalObjectBytes; +extern U32 gTotalTextureBytesPerBoostLevel[] ; #endif // LL_LLVIEWERSTATS_H diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 5c262838ae..0c05a301e6 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -72,6 +72,7 @@ LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sDefaultImagep = NULL; LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sSmokeImagep = NULL; LLViewerMediaTexture::media_map_t LLViewerMediaTexture::sMediaMap ; LLTexturePipelineTester* LLViewerTextureManager::sTesterp = NULL ; +const std::string sTesterName("TextureTester"); S32 LLViewerTexture::sImageCount = 0; S32 LLViewerTexture::sRawCount = 0; @@ -341,9 +342,14 @@ void LLViewerTextureManager::init() LLViewerTexture::initClass() ; - if(LLFastTimer::sMetricLog) + if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName)) { - LLViewerTextureManager::sTesterp = new LLTexturePipelineTester() ; + sTesterp = new LLTexturePipelineTester() ; + if (!sTesterp->isValid()) + { + delete sTesterp; + sTesterp = NULL; + } } } @@ -408,9 +414,10 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity { sCurrentTime = gFrameTimeSeconds ; - if(LLViewerTextureManager::sTesterp) + LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) { - LLViewerTextureManager::sTesterp->update() ; + tester->update() ; } LLViewerMediaTexture::updateClass() ; @@ -603,9 +610,10 @@ bool LLViewerTexture::bindDefaultImage(S32 stage) //check if there is cached raw image and switch to it if possible switchToCachedImage() ; - if(LLViewerTextureManager::sTesterp) + LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) { - LLViewerTextureManager::sTesterp->updateGrayTextureBinding() ; + tester->updateGrayTextureBinding() ; } return res; } @@ -1066,9 +1074,10 @@ BOOL LLViewerTexture::isLargeImage() //virtual void LLViewerTexture::updateBindStatsForTester() { - if(LLViewerTextureManager::sTesterp) + LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) { - LLViewerTextureManager::sTesterp->updateTextureBindingStats(this) ; + tester->updateTextureBindingStats(this) ; } } @@ -1130,7 +1139,7 @@ void LLViewerFetchedTexture::init(bool firstinit) // does not contain this image. mIsMissingAsset = FALSE; - mLoadedCallbackDesiredDiscardLevel = 0; + mLoadedCallbackDesiredDiscardLevel = S8_MAX; mPauseLoadedCallBacks = TRUE ; mNeedsCreateTexture = FALSE; @@ -1155,9 +1164,11 @@ void LLViewerFetchedTexture::init(bool firstinit) mSavedRawImage = NULL ; mForceToSaveRawImage = FALSE ; + mSaveRawImage = FALSE ; mSavedRawDiscardLevel = -1 ; mDesiredSavedRawDiscardLevel = -1 ; mLastReferencedSavedRawImageTime = 0.0f ; + mLastCallBackActiveTime = 0.f; } LLViewerFetchedTexture::~LLViewerFetchedTexture() @@ -1503,7 +1514,7 @@ void LLViewerFetchedTexture::processTextureStats() } else if(!mFullWidth || !mFullHeight) { - mDesiredDiscardLevel = getMaxDiscardLevel() ; + mDesiredDiscardLevel = llmin(getMaxDiscardLevel(), (S32)mLoadedCallbackDesiredDiscardLevel) ; } else { @@ -1846,10 +1857,11 @@ bool LLViewerFetchedTexture::updateFetch() // We may have data ready regardless of whether or not we are finished (e.g. waiting on write) if (mRawImage.notNull()) { - if(LLViewerTextureManager::sTesterp) + LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) { mIsFetched = TRUE ; - LLViewerTextureManager::sTesterp->updateTextureLoadingStats(this, mRawImage, LLAppViewer::getTextureFetch()->isFromLocalCache(mID)) ; + tester->updateTextureLoadingStats(this, mRawImage, LLAppViewer::getTextureFetch()->isFromLocalCache(mID)) ; } mRawDiscardLevel = fetch_discard; if ((mRawImage->getDataSize() > 0 && mRawDiscardLevel >= 0) && @@ -2074,13 +2086,14 @@ void LLViewerFetchedTexture::setLoadedCallback( loaded_callback_func loaded_call mNeedsAux |= needs_aux; if(keep_imageraw) { - forceToSaveRawImage(discard_level, true) ; + mSaveRawImage = TRUE ; } if (mNeedsAux && mAuxRawImage.isNull() && getDiscardLevel() >= 0) { // We need aux data, but we've already loaded the image, and it didn't have any llwarns << "No aux data available for callback for image:" << getID() << llendl; } + mLastCallBackActiveTime = sCurrentTime ; } void LLViewerFetchedTexture::clearCallbackEntryList() @@ -2103,9 +2116,8 @@ void LLViewerFetchedTexture::clearCallbackEntryList() } gTextureList.mCallbackList.erase(this); - mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; mLoadedCallbackDesiredDiscardLevel = S8_MAX ; - if(mForceToSaveRawImage) + if(needsToSaveRawImage()) { destroySavedRawImage() ; } @@ -2151,14 +2163,13 @@ void LLViewerFetchedTexture::deleteCallbackEntry(const LLLoadedCallbackEntry::so { // If we have no callbacks, take us off of the image callback list. gTextureList.mCallbackList.erase(this); - mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; - - if(mForceToSaveRawImage) + + if(needsToSaveRawImage()) { destroySavedRawImage() ; } } - else if(mForceToSaveRawImage && mBoostLevel != LLViewerTexture::BOOST_PREVIEW) + else if(needsToSaveRawImage() && mBoostLevel != LLViewerTexture::BOOST_PREVIEW) { if(desired_raw_discard != INVALID_DISCARD_LEVEL) { @@ -2196,7 +2207,7 @@ void LLViewerFetchedTexture::unpauseLoadedCallbacks(const LLLoadedCallbackEntry: mPauseLoadedCallBacks = FALSE ; if(need_raw) { - mForceToSaveRawImage = TRUE ; + mSaveRawImage = TRUE ; } } @@ -2227,16 +2238,23 @@ void LLViewerFetchedTexture::pauseLoadedCallbacks(const LLLoadedCallbackEntry::s { mPauseLoadedCallBacks = TRUE ;//when set, loaded callback is paused. resetTextureStats(); - mForceToSaveRawImage = FALSE ; + mSaveRawImage = FALSE ; } } bool LLViewerFetchedTexture::doLoadedCallbacks() { + static const F32 MAX_INACTIVE_TIME = 120.f ; //seconds + if (mNeedsCreateTexture) { return false; } + if(sCurrentTime - mLastCallBackActiveTime > MAX_INACTIVE_TIME) + { + clearCallbackEntryList() ; //remove all callbacks. + return false ; + } bool res = false; @@ -2302,13 +2320,11 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() bool run_raw_callbacks = false; bool need_readback = false; - mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); iter != mLoadedCallbackList.end(); ) { LLLoadedCallbackEntry *entryp = *iter++; - mMinDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, (S8)entryp->mDesiredDiscard) ; - + if (entryp->mNeedsImageRaw) { if (mNeedsAux) @@ -2382,7 +2398,8 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() // to satisfy the interested party, then this is the last time that // we're going to call them. - llassert_always(mRawImage.notNull()); + mLastCallBackActiveTime = sCurrentTime ; + //llassert_always(mRawImage.notNull()); if(mNeedsAux && mAuxRawImage.isNull()) { llwarns << "Raw Image with no Aux Data for callback" << llendl; @@ -2417,6 +2434,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() LLLoadedCallbackEntry *entryp = *curiter; if (!entryp->mNeedsImageRaw && (entryp->mLastUsedDiscard > gl_discard)) { + mLastCallBackActiveTime = sCurrentTime ; BOOL final = gl_discard <= entryp->mDesiredDiscard ? TRUE : FALSE; entryp->mLastUsedDiscard = gl_discard; entryp->mCallback(TRUE, this, NULL, NULL, gl_discard, final, entryp->mUserData); @@ -2436,7 +2454,6 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() if (mLoadedCallbackList.empty()) { gTextureList.mCallbackList.erase(this); - mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; } // Done with any raw image data at this point (will be re-created if we still have callbacks) @@ -2516,6 +2533,11 @@ LLImageRaw* LLViewerFetchedTexture::reloadRawImage(S8 discard_level) return mRawImage; } +bool LLViewerFetchedTexture::needsToSaveRawImage() +{ + return mForceToSaveRawImage || mSaveRawImage ; +} + void LLViewerFetchedTexture::destroyRawImage() { if (mAuxRawImage.notNull()) sAuxCount--; @@ -2526,7 +2548,7 @@ void LLViewerFetchedTexture::destroyRawImage() if(mIsRawImageValid) { - if(mForceToSaveRawImage) + if(needsToSaveRawImage()) { saveRawImage() ; } @@ -2658,7 +2680,7 @@ void LLViewerFetchedTexture::saveRawImage() mSavedRawDiscardLevel = mRawDiscardLevel ; mSavedRawImage = new LLImageRaw(mRawImage->getData(), mRawImage->getWidth(), mRawImage->getHeight(), mRawImage->getComponents()) ; - if(mSavedRawDiscardLevel <= mDesiredSavedRawDiscardLevel) + if(mForceToSaveRawImage && mSavedRawDiscardLevel <= mDesiredSavedRawDiscardLevel) { mForceToSaveRawImage = FALSE ; } @@ -2691,13 +2713,10 @@ void LLViewerFetchedTexture::forceToSaveRawImage(S32 desired_discard, bool from_ void LLViewerFetchedTexture::destroySavedRawImage() { clearCallbackEntryList() ; - //if(mForceToSaveRawImage && mDesiredSavedRawDiscardLevel >= 0 && mDesiredSavedRawDiscardLevel < getDiscardLevel()) - //{ - // return ; //can not destroy the saved raw image before it is fully fetched, otherwise causing callbacks hanging there. - //} - + mSavedRawImage = NULL ; mForceToSaveRawImage = FALSE ; + mSaveRawImage = FALSE ; mSavedRawDiscardLevel = -1 ; mDesiredSavedRawDiscardLevel = -1 ; mLastReferencedSavedRawImageTime = 0.0f ; @@ -3066,9 +3085,10 @@ void LLViewerLODTexture::scaleDown() { switchToCachedImage() ; - if(LLViewerTextureManager::sTesterp) + LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) { - LLViewerTextureManager::sTesterp->setStablizingTime() ; + tester->setStablizingTime() ; } } } @@ -3578,23 +3598,22 @@ F32 LLViewerMediaTexture::getMaxVirtualSize() //---------------------------------------------------------------------------------------------- //start of LLTexturePipelineTester //---------------------------------------------------------------------------------------------- -LLTexturePipelineTester::LLTexturePipelineTester() : - LLMetricPerformanceTester("TextureTester", FALSE) -{ - addMetricString("TotalBytesLoaded") ; - addMetricString("TotalBytesLoadedFromCache") ; - addMetricString("TotalBytesLoadedForLargeImage") ; - addMetricString("TotalBytesLoadedForSculpties") ; - addMetricString("StartFetchingTime") ; - addMetricString("TotalGrayTime") ; - addMetricString("TotalStablizingTime") ; - addMetricString("StartTimeLoadingSculpties") ; - addMetricString("EndTimeLoadingSculpties") ; - - addMetricString("Time") ; - addMetricString("TotalBytesBound") ; - addMetricString("TotalBytesBoundForLargeImage") ; - addMetricString("PercentageBytesBound") ; +LLTexturePipelineTester::LLTexturePipelineTester() : LLMetricPerformanceTesterWithSession(sTesterName) +{ + addMetric("TotalBytesLoaded") ; + addMetric("TotalBytesLoadedFromCache") ; + addMetric("TotalBytesLoadedForLargeImage") ; + addMetric("TotalBytesLoadedForSculpties") ; + addMetric("StartFetchingTime") ; + addMetric("TotalGrayTime") ; + addMetric("TotalStablizingTime") ; + addMetric("StartTimeLoadingSculpties") ; + addMetric("EndTimeLoadingSculpties") ; + + addMetric("Time") ; + addMetric("TotalBytesBound") ; + addMetric("TotalBytesBoundForLargeImage") ; + addMetric("PercentageBytesBound") ; mTotalBytesLoaded = 0 ; mTotalBytesLoadedFromCache = 0 ; @@ -3606,7 +3625,7 @@ LLTexturePipelineTester::LLTexturePipelineTester() : LLTexturePipelineTester::~LLTexturePipelineTester() { - LLViewerTextureManager::sTesterp = NULL ; + LLViewerTextureManager::sTesterp = NULL; } void LLTexturePipelineTester::update() @@ -3672,22 +3691,23 @@ void LLTexturePipelineTester::reset() //virtual void LLTexturePipelineTester::outputTestRecord(LLSD *sd) { - (*sd)[mCurLabel]["TotalBytesLoaded"] = (LLSD::Integer)mTotalBytesLoaded ; - (*sd)[mCurLabel]["TotalBytesLoadedFromCache"] = (LLSD::Integer)mTotalBytesLoadedFromCache ; - (*sd)[mCurLabel]["TotalBytesLoadedForLargeImage"] = (LLSD::Integer)mTotalBytesLoadedForLargeImage ; - (*sd)[mCurLabel]["TotalBytesLoadedForSculpties"] = (LLSD::Integer)mTotalBytesLoadedForSculpties ; + std::string currentLabel = getCurrentLabelName(); + (*sd)[currentLabel]["TotalBytesLoaded"] = (LLSD::Integer)mTotalBytesLoaded ; + (*sd)[currentLabel]["TotalBytesLoadedFromCache"] = (LLSD::Integer)mTotalBytesLoadedFromCache ; + (*sd)[currentLabel]["TotalBytesLoadedForLargeImage"] = (LLSD::Integer)mTotalBytesLoadedForLargeImage ; + (*sd)[currentLabel]["TotalBytesLoadedForSculpties"] = (LLSD::Integer)mTotalBytesLoadedForSculpties ; - (*sd)[mCurLabel]["StartFetchingTime"] = (LLSD::Real)mStartFetchingTime ; - (*sd)[mCurLabel]["TotalGrayTime"] = (LLSD::Real)mTotalGrayTime ; - (*sd)[mCurLabel]["TotalStablizingTime"] = (LLSD::Real)mTotalStablizingTime ; + (*sd)[currentLabel]["StartFetchingTime"] = (LLSD::Real)mStartFetchingTime ; + (*sd)[currentLabel]["TotalGrayTime"] = (LLSD::Real)mTotalGrayTime ; + (*sd)[currentLabel]["TotalStablizingTime"] = (LLSD::Real)mTotalStablizingTime ; - (*sd)[mCurLabel]["StartTimeLoadingSculpties"] = (LLSD::Real)mStartTimeLoadingSculpties ; - (*sd)[mCurLabel]["EndTimeLoadingSculpties"] = (LLSD::Real)mEndTimeLoadingSculpties ; + (*sd)[currentLabel]["StartTimeLoadingSculpties"] = (LLSD::Real)mStartTimeLoadingSculpties ; + (*sd)[currentLabel]["EndTimeLoadingSculpties"] = (LLSD::Real)mEndTimeLoadingSculpties ; - (*sd)[mCurLabel]["Time"] = LLImageGL::sLastFrameTime ; - (*sd)[mCurLabel]["TotalBytesBound"] = (LLSD::Integer)mLastTotalBytesUsed ; - (*sd)[mCurLabel]["TotalBytesBoundForLargeImage"] = (LLSD::Integer)mLastTotalBytesUsedForLargeImage ; - (*sd)[mCurLabel]["PercentageBytesBound"] = (LLSD::Real)(100.f * mLastTotalBytesUsed / mTotalBytesLoaded) ; + (*sd)[currentLabel]["Time"] = LLImageGL::sLastFrameTime ; + (*sd)[currentLabel]["TotalBytesBound"] = (LLSD::Integer)mLastTotalBytesUsed ; + (*sd)[currentLabel]["TotalBytesBoundForLargeImage"] = (LLSD::Integer)mLastTotalBytesUsedForLargeImage ; + (*sd)[currentLabel]["PercentageBytesBound"] = (LLSD::Real)(100.f * mLastTotalBytesUsed / mTotalBytesLoaded) ; } void LLTexturePipelineTester::updateTextureBindingStats(const LLViewerTexture* imagep) @@ -3776,7 +3796,7 @@ void LLTexturePipelineTester::compareTestSessions(std::ofstream* os) } //compare and output the comparison - *os << llformat("%s\n", mName.c_str()) ; + *os << llformat("%s\n", getTesterName().c_str()) ; *os << llformat("AggregateResults\n") ; compareTestResults(os, "TotalFetchingTime", base_sessionp->mTotalFetchingTime, current_sessionp->mTotalFetchingTime) ; @@ -3831,7 +3851,7 @@ void LLTexturePipelineTester::compareTestSessions(std::ofstream* os) } //virtual -LLMetricPerformanceTester::LLTestSession* LLTexturePipelineTester::loadTestSession(LLSD* log) +LLMetricPerformanceTesterWithSession::LLTestSession* LLTexturePipelineTester::loadTestSession(LLSD* log) { LLTexturePipelineTester::LLTextureTestSession* sessionp = new LLTexturePipelineTester::LLTextureTestSession() ; if(!sessionp) @@ -3858,12 +3878,11 @@ LLMetricPerformanceTester::LLTestSession* LLTexturePipelineTester::loadTestSessi sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mTime = 0.f ; //load a session - BOOL in_log = (*log).has(mCurLabel) ; - while(in_log) + std::string currentLabel = getCurrentLabelName(); + BOOL in_log = (*log).has(currentLabel) ; + while (in_log) { - LLSD::String label = mCurLabel ; - incLabel() ; - in_log = (*log).has(mCurLabel) ; + LLSD::String label = currentLabel ; if(sessionp->mInstantPerformanceListCounter >= (S32)sessionp->mInstantPerformanceList.size()) { @@ -3929,7 +3948,11 @@ LLMetricPerformanceTester::LLTestSession* LLTexturePipelineTester::loadTestSessi sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAverageBytesUsedForLargeImagePerSecond = 0 ; sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAveragePercentageBytesUsedPerSecond = 0.f ; sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mTime = 0.f ; - } + } + // Next label + incrementCurrentCount() ; + currentLabel = getCurrentLabelName(); + in_log = (*log).has(currentLabel) ; } sessionp->mTotalFetchingTime += total_fetching_time ; diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 7cb8bea4c8..b5636bbdc7 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -441,6 +441,7 @@ public: LLImageRaw* reloadRawImage(S8 discard_level) ; void destroyRawImage(); + bool needsToSaveRawImage(); const std::string& getUrl() const {return mUrl;} //--------------- @@ -532,6 +533,7 @@ protected: S8 mLoadedCallbackDesiredDiscardLevel; BOOL mPauseLoadedCallBacks; callback_list_t mLoadedCallbackList; + F32 mLastCallBackActiveTime; LLPointer<LLImageRaw> mRawImage; S32 mRawDiscardLevel; @@ -543,6 +545,7 @@ protected: //keep a copy of mRawImage for some special purposes //when mForceToSaveRawImage is set. BOOL mForceToSaveRawImage ; + BOOL mSaveRawImage; LLPointer<LLImageRaw> mSavedRawImage; S32 mSavedRawDiscardLevel; S32 mDesiredSavedRawDiscardLevel; @@ -732,7 +735,7 @@ public: //it tracks the activities of the texture pipeline //records them, and outputs them to log files // -class LLTexturePipelineTester : public LLMetricPerformanceTester +class LLTexturePipelineTester : public LLMetricPerformanceTesterWithSession { enum { @@ -748,8 +751,6 @@ public: void updateGrayTextureBinding() ; void setStablizingTime() ; - /*virtual*/ void analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current) ; - private: void reset() ; void updateStablizingTime() ; @@ -820,7 +821,7 @@ private: S32 mInstantPerformanceListCounter ; }; - /*virtual*/ LLMetricPerformanceTester::LLTestSession* loadTestSession(LLSD* log) ; + /*virtual*/ LLMetricPerformanceTesterWithSession::LLTestSession* loadTestSession(LLSD* log) ; /*virtual*/ void compareTestSessions(std::ofstream* os) ; }; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index bbf7c8e60e..10126219f8 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1161,6 +1161,8 @@ void LLViewerTextureList::updateMaxResidentTexMem(S32 mem) // static void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_data) { + static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; + LLFastTimer t(FTM_PROCESS_IMAGES); // Receive image header, copy into image object and decompresses @@ -1171,14 +1173,16 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d char ip_string[256]; u32_to_ip_string(msg->getSenderIP(),ip_string); + U32 received_size ; if (msg->getReceiveCompressedSize()) { - gTextureList.sTextureBits += msg->getReceiveCompressedSize() * 8; + received_size = msg->getReceiveCompressedSize() ; } else { - gTextureList.sTextureBits += msg->getReceiveSize() * 8; + received_size = msg->getReceiveSize() ; } + gTextureList.sTextureBits += received_size * 8; gTextureList.sTexturePackets++; U8 codec; @@ -1213,6 +1217,11 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d delete [] data; return; } + if(log_texture_traffic) + { + gTotalTextureBytesPerBoostLevel[image->getBoostLevel()] += received_size ; + } + //image->getLastPacketTimer()->reset(); bool res = LLAppViewer::getTextureFetch()->receiveImageHeader(msg->getSender(), id, codec, packets, totalbytes, data_size, data); if (!res) @@ -1224,6 +1233,8 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d // static void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_data) { + static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; + LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE); LLFastTimer t(FTM_PROCESS_IMAGES); @@ -1236,14 +1247,16 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d char ip_string[256]; u32_to_ip_string(msg->getSenderIP(),ip_string); + U32 received_size ; if (msg->getReceiveCompressedSize()) { - gTextureList.sTextureBits += msg->getReceiveCompressedSize() * 8; + received_size = msg->getReceiveCompressedSize() ; } else { - gTextureList.sTextureBits += msg->getReceiveSize() * 8; + received_size = msg->getReceiveSize() ; } + gTextureList.sTextureBits += received_size * 8; gTextureList.sTexturePackets++; //llprintline("Start decode, image header..."); @@ -1277,6 +1290,11 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d delete [] data; return; } + if(log_texture_traffic) + { + gTotalTextureBytesPerBoostLevel[image->getBoostLevel()] += received_size ; + } + //image->getLastPacketTimer()->reset(); bool res = LLAppViewer::getTextureFetch()->receiveImagePacket(msg->getSender(), id, packet_num, data_size, data); if (!res) @@ -1513,42 +1531,45 @@ bool LLUIImageList::initFromFile() return false; } - std::vector<std::string> paths; - // path to current selected skin - paths.push_back(gDirUtilp->getSkinDir() - + gDirUtilp->getDirDelimiter() - + "textures" - + gDirUtilp->getDirDelimiter() - + "textures.xml"); - // path to user overrides on current skin - paths.push_back(gDirUtilp->getUserSkinDir() - + gDirUtilp->getDirDelimiter() - + "textures" - + gDirUtilp->getDirDelimiter() - + "textures.xml"); - - // apply skinned xml files incrementally - for(std::vector<std::string>::iterator path_it = paths.begin(); - path_it != paths.end(); - ++path_it) - { - // don't reapply base file to itself - if (!path_it->empty() && (*path_it) != base_file_path) - { - LLXMLNodePtr update_root; - if (LLXMLNode::parseFile(*path_it, update_root, NULL)) - { - LLXMLNode::updateNode(root, update_root); - } - } - } - UIImageDeclarations images; LLXUIParser parser; parser.readXUI(root, images, base_file_path); + // add components defined in current skin + std::string skin_update_path = gDirUtilp->getSkinDir() + + gDirUtilp->getDirDelimiter() + + "textures" + + gDirUtilp->getDirDelimiter() + + "textures.xml"; + LLXMLNodePtr update_root; + if (skin_update_path != base_file_path + && LLXMLNode::parseFile(skin_update_path, update_root, NULL)) + { + parser.readXUI(update_root, images, skin_update_path); + } + + // add components defined in user override of current skin + skin_update_path = gDirUtilp->getUserSkinDir() + + gDirUtilp->getDirDelimiter() + + "textures" + + gDirUtilp->getDirDelimiter() + + "textures.xml"; + if (skin_update_path != base_file_path + && LLXMLNode::parseFile(skin_update_path, update_root, NULL)) + { + parser.readXUI(update_root, images, skin_update_path); + } + if (!images.validateBlock()) return false; + std::map<std::string, UIImageDeclaration> merged_declarations; + for (LLInitParam::ParamIterator<UIImageDeclaration>::const_iterator image_it = images.textures.begin(); + image_it != images.textures.end(); + ++image_it) + { + merged_declarations[image_it->name].overwriteFrom(*image_it); + } + enum e_decode_pass { PASS_DECODE_NOW, @@ -1558,19 +1579,20 @@ bool LLUIImageList::initFromFile() for (S32 cur_pass = PASS_DECODE_NOW; cur_pass < NUM_PASSES; cur_pass++) { - for (LLInitParam::ParamIterator<UIImageDeclaration>::const_iterator image_it = images.textures.begin(); - image_it != images.textures.end(); + for (std::map<std::string, UIImageDeclaration>::const_iterator image_it = merged_declarations.begin(); + image_it != merged_declarations.end(); ++image_it) { - std::string file_name = image_it->file_name.isProvided() ? image_it->file_name() : image_it->name(); + const UIImageDeclaration& image = image_it->second; + std::string file_name = image.file_name.isProvided() ? image.file_name() : image.name(); // load high priority textures on first pass (to kick off decode) - enum e_decode_pass decode_pass = image_it->preload ? PASS_DECODE_NOW : PASS_DECODE_LATER; + enum e_decode_pass decode_pass = image.preload ? PASS_DECODE_NOW : PASS_DECODE_LATER; if (decode_pass != cur_pass) { continue; } - preloadUIImage(image_it->name, file_name, image_it->use_mips, image_it->scale); + preloadUIImage(image.name, file_name, image.use_mips, image.scale); } if (cur_pass == PASS_DECODE_NOW && !gSavedSettings.getBOOL("NoPreload")) diff --git a/indra/newview/llviewerthrottle.cpp b/indra/newview/llviewerthrottle.cpp index b614ccdbc2..5147272122 100644 --- a/indra/newview/llviewerthrottle.cpp +++ b/indra/newview/llviewerthrottle.cpp @@ -46,7 +46,7 @@ const F32 MAX_FRACTIONAL = 1.5f; const F32 MIN_FRACTIONAL = 0.2f; const F32 MIN_BANDWIDTH = 50.f; -const F32 MAX_BANDWIDTH = 1500.f; +const F32 MAX_BANDWIDTH = 3000.f; const F32 STEP_FRACTIONAL = 0.1f; const F32 TIGHTEN_THROTTLE_THRESHOLD = 3.0f; // packet loss % per s const F32 EASE_THROTTLE_THRESHOLD = 0.5f; // packet loss % per s diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index ebcb6e3738..c812fcf2da 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -306,6 +306,8 @@ public: void update() { + static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; + std::string wind_vel_text; std::string wind_vector_text; std::string rwind_vel_text; @@ -582,6 +584,23 @@ public: ypos += y_inc; } } + if(log_texture_traffic) + { + U32 old_y = ypos ; + for(S32 i = LLViewerTexture::BOOST_NONE; i < LLViewerTexture::MAX_GL_IMAGE_CATEGORY; i++) + { + if(gTotalTextureBytesPerBoostLevel[i] > 0) + { + addText(xpos, ypos, llformat("Boost_Level %d: %.3f MB", i, (F32)gTotalTextureBytesPerBoostLevel[i] / (1024 * 1024))); + ypos += y_inc; + } + } + if(ypos != old_y) + { + addText(xpos, ypos, "Network traffic for textures:"); + ypos += y_inc; + } + } } void draw() @@ -1348,6 +1367,15 @@ LLViewerWindow::LLViewerWindow( LL_WARNS("Window") << " Someone took over my signal/exception handler (post createWindow)!" << LL_ENDL; } + LLCoordScreen scr; + mWindow->getSize(&scr); + + if(fullscreen && ( scr.mX!=width || scr.mY!=height)) + { + llwarns << "Fullscreen has forced us in to a different resolution now using "<<scr.mX<<" x "<<scr.mY<<llendl; + gSavedSettings.setS32("FullScreenWidth",scr.mX); + gSavedSettings.setS32("FullScreenHeight",scr.mY); + } if (NULL == mWindow) { @@ -1359,7 +1387,7 @@ LLViewerWindow::LLViewerWindow( LL_WARNS("Window") << "Unable to create window, be sure screen is set at 32-bit color in Control Panels->Display->Settings" << LL_ENDL; #endif - LLAppViewer::instance()->forceExit(1); + LLAppViewer::instance()->fastQuit(1); } // Get the real window rect the window was created with (since there are various OS-dependent reasons why @@ -1404,6 +1432,11 @@ LLViewerWindow::LLViewerWindow( gSavedSettings.setBOOL("ProbeHardwareOnStartup", FALSE); } + if (!gGLManager.mHasDepthClamp) + { + LL_INFOS("RenderInit") << "Missing feature GL_ARB_depth_clamp. Void water might disappear in rare cases." << LL_ENDL; + } + // If we crashed while initializng GL stuff last time, disable certain features if (gSavedSettings.getBOOL("RenderInitError")) { @@ -1521,8 +1554,9 @@ void LLViewerWindow::initBase() mWorldViewPlaceholder = main_view->getChildView("world_view_rect")->getHandle(); mNonSideTrayView = main_view->getChildView("non_side_tray_view")->getHandle(); mFloaterViewHolder = main_view->getChildView("floater_view_holder")->getHandle(); - mPopupView = main_view->findChild<LLPopupView>("popup_holder"); + mPopupView = main_view->getChild<LLPopupView>("popup_holder"); mHintHolder = main_view->getChild<LLView>("hint_holder")->getHandle(); + mLoginPanelHolder = main_view->getChild<LLView>("login_panel_holder")->getHandle(); // Constrain floaters to inside the menu and status bar regions. gFloaterView = main_view->getChild<LLFloaterView>("Floater View"); @@ -1656,7 +1690,7 @@ void LLViewerWindow::initWorldUI() { LLRect hud_rect = full_window; hud_rect.mBottom += 50; - if (gMenuBarView) + if (gMenuBarView && gMenuBarView->isInVisibleChain()) { hud_rect.mTop -= gMenuBarView->getRect().getHeight(); } @@ -1685,6 +1719,20 @@ void LLViewerWindow::initWorldUI() buttons_panel->setShape(buttons_panel_container->getLocalRect()); buttons_panel->setFollowsAll(); buttons_panel_container->addChild(buttons_panel); + + LLView* avatar_picker_destination_guide_container = gViewerWindow->getRootView()->getChild<LLView>("avatar_picker_and_destination_guide_container"); + LLMediaCtrl* destinations = avatar_picker_destination_guide_container->findChild<LLMediaCtrl>("destination_guide_contents"); + LLMediaCtrl* avatar_picker = avatar_picker_destination_guide_container->findChild<LLMediaCtrl>("avatar_picker_contents"); + if (destinations) + { + destinations->navigateTo(gSavedSettings.getString("DestinationGuideURL"), "text/html"); + } + + if (avatar_picker) + { + avatar_picker->navigateTo(gSavedSettings.getString("AvatarPickerURL"), "text/html"); + } + } // Destroy the UI @@ -1839,6 +1887,8 @@ void LLViewerWindow::reshape(S32 width, S32 height) return; } + gWindowResized = TRUE; + // update our window rectangle mWindowRectRaw.mRight = mWindowRectRaw.mLeft + width; mWindowRectRaw.mTop = mWindowRectRaw.mBottom + height; @@ -2240,6 +2290,20 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) return TRUE; } + // If "Pressing letter keys starts local chat" option is selected, we are not in mouselook, + // no view has keyboard focus, this is a printable character key (and no modifier key is + // pressed except shift), then give focus to nearby chat (STORM-560) + if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && + !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) + { + LLLineEditor* chat_editor = LLBottomTray::instanceExists() ? LLBottomTray::getInstance()->getNearbyChatBar()->getChatBox() : NULL; + if (chat_editor) + { + // passing NULL here, character will be added later when it is handled by character handler. + LLBottomTray::getInstance()->getNearbyChatBar()->startChat(NULL); + return TRUE; + } + } // give menus a chance to handle unmodified accelerator keys if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) @@ -2440,6 +2504,10 @@ void LLViewerWindow::updateUI() { LLFirstUse::notUsingDestinationGuide(); } + if (gLoggedInTime.getElapsedTimeF32() > gSavedSettings.getF32("AvatarPickerHintTimeout")) + { + LLFirstUse::notUsingAvatarPicker(); + } if (gLoggedInTime.getElapsedTimeF32() > gSavedSettings.getF32("SidePanelHintTimeout")) { LLFirstUse::notUsingSidePanel(); @@ -2935,18 +3003,20 @@ void LLViewerWindow::updateKeyboardFocus() LLUICtrl* parent = cur_focus->getParentUICtrl(); const LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); + bool new_focus_found = false; while(parent) { - if (parent->isCtrl() && - (parent->hasTabStop() || parent == focus_root) && - !parent->getIsChrome() && - parent->isInVisibleChain() && - parent->isInEnabledChain()) + if (parent->isCtrl() + && (parent->hasTabStop() || parent == focus_root) + && !parent->getIsChrome() + && parent->isInVisibleChain() + && parent->isInEnabledChain()) { if (!parent->focusFirstItem()) { parent->setFocus(TRUE); } + new_focus_found = true; break; } parent = parent->getParentUICtrl(); @@ -2955,7 +3025,7 @@ void LLViewerWindow::updateKeyboardFocus() // if we didn't find a better place to put focus, just release it // hasFocus() will return true if and only if we didn't touch focus since we // are only moving focus higher in the hierarchy - if (cur_focus->hasFocus()) + if (!new_focus_found) { cur_focus->setFocus(FALSE); } @@ -3985,30 +4055,19 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei { gDisplaySwapBuffers = FALSE; gDepthDirty = TRUE; - if (type == SNAPSHOT_TYPE_OBJECT_ID) - { - glClearColor(0.f, 0.f, 0.f, 0.f); - glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - LLViewerCamera::getInstance()->setZoomParameters(scale_factor, subimage_x+(subimage_y*llceil(scale_factor))); - setup3DRender(); - gObjectList.renderPickList(gViewerWindow->getWindowRectScaled(), FALSE, FALSE); + const U32 subfield = subimage_x+(subimage_y*llceil(scale_factor)); + + if (LLPipeline::sRenderDeferred) + { + display(do_rebuild, scale_factor, subfield, TRUE); } else { - const U32 subfield = subimage_x+(subimage_y*llceil(scale_factor)); - - if (LLPipeline::sRenderDeferred) - { - display(do_rebuild, scale_factor, subfield, TRUE); - } - else - { - display(do_rebuild, scale_factor, subfield, TRUE); + display(do_rebuild, scale_factor, subfield, TRUE); // Required for showing the GUI in snapshots and performing bloom composite overlay // Call even if show_ui is FALSE - render_ui(scale_factor, subfield); - } + render_ui(scale_factor, subfield); } S32 subimage_x_offset = llclamp(buffer_x_offset - (subimage_x * window_width), 0, window_width); @@ -4031,7 +4090,7 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei LLAppViewer::instance()->pingMainloopTimeout("LLViewerWindow::rawSnapshot"); } - if (type == SNAPSHOT_TYPE_OBJECT_ID || type == SNAPSHOT_TYPE_COLOR) + if (type == SNAPSHOT_TYPE_COLOR) { glReadPixels( subimage_x_offset, out_y + subimage_y_offset, @@ -4252,17 +4311,8 @@ void LLViewerWindow::setup3DRender() void LLViewerWindow::setup3DViewport(S32 x_offset, S32 y_offset) { - if (LLRenderTarget::getCurrentBoundTarget() != NULL) - { - // don't use translation component of mWorldViewRectRaw, as we are already in a properly sized render target - gGLViewport[0] = x_offset; - gGLViewport[1] = y_offset; - } - else - { - gGLViewport[0] = mWorldViewRectRaw.mLeft + x_offset; - gGLViewport[1] = mWorldViewRectRaw.mBottom + y_offset; - } + gGLViewport[0] = mWorldViewRectRaw.mLeft + x_offset; + gGLViewport[1] = mWorldViewRectRaw.mBottom + y_offset; gGLViewport[2] = mWorldViewRectRaw.getWidth(); gGLViewport[3] = mWorldViewRectRaw.getHeight(); glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); @@ -4415,6 +4465,7 @@ void LLViewerWindow::restoreGL(const std::string& progress_message) LLVOAvatar::restoreGL(); gResizeScreenTexture = TRUE; + gWindowResized = TRUE; if (isAgentAvatarValid() && !gAgentAvatarp->isUsingBakedTextures()) { diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 633c3a41d2..5eeb02b080 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -186,11 +186,6 @@ public: /*virtual*/ std::string translateString(const char* tag, const std::map<std::string, std::string>& args); - // signal on bottom tray width changed - typedef boost::function<void (void)> bottom_tray_callback_t; - typedef boost::signals2::signal<void (void)> bottom_tray_signal_t; - bottom_tray_signal_t mOnBottomTrayWidthChanged; - boost::signals2::connection setOnBottomTrayWidthChanged(bottom_tray_callback_t cb) { return mOnBottomTrayWidthChanged.connect(cb); } // signal on update of WorldView rect typedef boost::function<void (LLRect old_world_rect, LLRect new_world_rect)> world_rect_callback_t; typedef boost::signals2::signal<void (LLRect old_world_rect, LLRect new_world_rect)> world_rect_signal_t; @@ -288,6 +283,7 @@ public: LLView* getNonSideTrayView() { return mNonSideTrayView.get(); } LLView* getFloaterViewHolder() { return mFloaterViewHolder.get(); } LLView* getHintHolder() { return mHintHolder.get(); } + LLView* getLoginPanelHolder() { return mLoginPanelHolder.get(); } BOOL handleKey(KEY key, MASK mask); void handleScrollWheel (S32 clicks); @@ -316,8 +312,7 @@ public: typedef enum { SNAPSHOT_TYPE_COLOR, - SNAPSHOT_TYPE_DEPTH, - SNAPSHOT_TYPE_OBJECT_ID + SNAPSHOT_TYPE_DEPTH } ESnapshotType; BOOL saveSnapshot(const std::string& filename, S32 image_width, S32 image_height, BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR); BOOL rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, BOOL keep_window_aspect = TRUE, BOOL is_texture = FALSE, @@ -448,6 +443,7 @@ protected: LLHandle<LLView> mNonSideTrayView; // parent of world view + bottom bar, etc...everything but the side tray LLHandle<LLView> mFloaterViewHolder; // container for floater_view LLHandle<LLView> mHintHolder; // container for hints + LLHandle<LLView> mLoginPanelHolder; // container for login panel LLPopupView* mPopupView; // container for transient popups class LLDebugText* mDebugText; // Internal class for debug text diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp index 4473b5820d..0b52948680 100644 --- a/indra/newview/llviewerwindowlistener.cpp +++ b/indra/newview/llviewerwindowlistener.cpp @@ -54,7 +54,7 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): // saveSnapshotArgs["type"] = LLSD::String(); add("saveSnapshot", "Save screenshot: [\"filename\"], [\"width\"], [\"height\"], [\"showui\"], [\"rebuild\"], [\"type\"]\n" - "type: \"COLOR\", \"DEPTH\", \"OBJECT_ID\"\n" + "type: \"COLOR\", \"DEPTH\"\n" "Post on [\"reply\"] an event containing [\"ok\"]", &LLViewerWindowListener::saveSnapshot, saveSnapshotArgs); @@ -71,7 +71,6 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const #define tp(name) types[#name] = LLViewerWindow::SNAPSHOT_TYPE_##name tp(COLOR); tp(DEPTH); - tp(OBJECT_ID); #undef tp // Our add() call should ensure that the incoming LLSD does in fact // contain our required arguments. Deal with the optional ones. diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 8738ad7687..bb4c5b1804 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2333,8 +2333,19 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) { - // disable voice visualizer when in mouselook - mVoiceVisualizer->setVoiceEnabled( voice_enabled && !(isSelf() && gAgentCamera.cameraMouselook()) ); + bool render_visualizer = voice_enabled; + + // Don't render the user's own voice visualizer when in mouselook, or when opening the mic is disabled. + if(isSelf()) + { + if(gAgentCamera.cameraMouselook() || gSavedSettings.getBOOL("VoiceDisableMic")) + { + render_visualizer = false; + } + } + + mVoiceVisualizer->setVoiceEnabled(render_visualizer); + if ( voice_enabled ) { //---------------------------------------------------------------- @@ -3024,7 +3035,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) std::deque<LLChat>::iterator chat_iter = mChats.begin(); mNameText->clearString(); - LLColor4 new_chat = LLUIColorTable::instance().getColor( "NameTagChat" ); + LLColor4 new_chat = LLUIColorTable::instance().getColor( isSelf() ? "UserChatColor" : "AgentChatColor" ); LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f); LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f); if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES) @@ -3992,7 +4003,7 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) // *NOTE: this is disabled (there is no UI for enabling sShowFootPlane) due // to DEV-14477. the code is left here to aid in tracking down the cause // of the crash in the future. -brad - if (!gRenderForSelect && sShowFootPlane && mDrawable.notNull()) + if (sShowFootPlane && mDrawable.notNull()) { LLVector3 slaved_pos = mDrawable->getPositionAgent(); LLVector3 foot_plane_normal(mFootPlane.mV[VX], mFootPlane.mV[VY], mFootPlane.mV[VZ]); @@ -7864,6 +7875,7 @@ BOOL LLVOAvatar::LLVOAvatarXmlInfo::parseXmlMorphNodes(LLXmlTreeNode* root) //virtual void LLVOAvatar::updateRegion(LLViewerRegion *regionp) { + LLViewerObject::updateRegion(regionp); } std::string LLVOAvatar::getFullname() const diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index e5cbf65682..5f9e343907 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -783,11 +783,19 @@ void LLVOAvatarSelf::removeMissingBakedTextures() for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { const S32 te = mBakedTextureDatas[i].mTextureIndex; - LLViewerTexture* tex = getTEImage(te) ; + const LLViewerTexture* tex = getTEImage(te); + + // Replace with default if we can't find the asset, assuming the + // default is actually valid (which it should be unless something + // is seriously wrong). if (!tex || tex->isMissingAsset()) { - setTEImage(te, LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT_AVATAR)); - removed = TRUE; + LLViewerTexture *imagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT_AVATAR); + if (imagep) + { + setTEImage(te, imagep); + removed = TRUE; + } } } @@ -806,7 +814,23 @@ void LLVOAvatarSelf::removeMissingBakedTextures() //virtual void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp) { + // Save the global position + LLVector3d global_pos_from_old_region = getPositionGlobal(); + + // Change the region setRegion(regionp); + + if (regionp) + { // Set correct region-relative position from global coordinates + setPositionGlobal(global_pos_from_old_region); + + // Diagnostic info + //LLVector3d pos_from_new_region = getPositionGlobal(); + //llinfos << "pos_from_old_region is " << global_pos_from_old_region + // << " while pos_from_new_region is " << pos_from_new_region + // << llendl; + } + if (!regionp || (regionp->getHandle() != mLastRegionHandle)) { if (mLastRegionHandle != 0) @@ -820,6 +844,9 @@ void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp) F64 max = (mRegionCrossingCount == 1) ? 0 : LLViewerStats::getInstance()->getStat(LLViewerStats::ST_CROSSING_MAX); max = llmax(delta, max); LLViewerStats::getInstance()->setStat(LLViewerStats::ST_CROSSING_MAX, max); + + // Diagnostics + llinfos << "Region crossing took " << (F32)(delta * 1000.0) << " ms " << llendl; } if (regionp) { @@ -827,6 +854,7 @@ void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp) } } mRegionCrossingTimer.reset(); + LLViewerObject::updateRegion(regionp); } //-------------------------------------------------------------------- diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 23a799ea3a..d13cf5ba38 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -123,6 +123,8 @@ public: //-------------------------------------------------------------------- // Region state //-------------------------------------------------------------------- + void resetRegionCrossingTimer() { mRegionCrossingTimer.reset(); } + private: U64 mLastRegionHandle; LLFrameTimer mRegionCrossingTimer; diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index b1c6ea26cc..145ee31260 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -28,6 +28,7 @@ #include "llvocache.h" #include "llerror.h" #include "llregionhandle.h" +#include "llviewercontrol.h" BOOL check_read(LLAPRFile* apr_file, void* src, S32 n_bytes) { @@ -231,11 +232,11 @@ LLVOCache* LLVOCache::sInstance = NULL; //static LLVOCache* LLVOCache::getInstance() -{ +{ if(!sInstance) { sInstance = new LLVOCache() ; -} + } return sInstance ; } @@ -261,13 +262,17 @@ LLVOCache::LLVOCache(): mNumEntries(0), mCacheSize(1) { + mEnabled = gSavedSettings.getBOOL("ObjectCacheEnabled"); mLocalAPRFilePoolp = new LLVolatileAPRPool() ; } LLVOCache::~LLVOCache() { - writeCacheHeader(); - clearCacheInMemory(); + if(mEnabled) + { + writeCacheHeader(); + clearCacheInMemory(); + } delete mLocalAPRFilePoolp; } @@ -281,7 +286,7 @@ void LLVOCache::setDirNames(ELLPath location) void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version) { - if(mInitialized) + if(mInitialized || !mEnabled) { return ; } @@ -408,6 +413,11 @@ BOOL LLVOCache::checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes) void LLVOCache::readCacheHeader() { + if(!mEnabled) + { + return ; + } + //clear stale info. clearCacheInMemory(); @@ -452,7 +462,7 @@ void LLVOCache::readCacheHeader() void LLVOCache::writeCacheHeader() { - if(mReadOnly) + if(mReadOnly || !mEnabled) { return ; } @@ -503,6 +513,10 @@ BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry) void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map) { + if(!mEnabled) + { + return ; + } llassert_always(mInitialized); handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ; @@ -572,6 +586,10 @@ void LLVOCache::purgeEntries() void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache) { + if(!mEnabled) + { + return ; + } llassert_always(mInitialized); if(mReadOnly) diff --git a/indra/newview/llvocache.h b/indra/newview/llvocache.h index ccdff5e96c..ed2bc8bafe 100644 --- a/indra/newview/llvocache.h +++ b/indra/newview/llvocache.h @@ -129,6 +129,7 @@ private: BOOL checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes) ; private: + BOOL mEnabled; BOOL mInitialized ; BOOL mReadOnly ; HeaderMetaInfo mMetaInfo; @@ -143,7 +144,7 @@ private: static LLVOCache* sInstance ; public: static LLVOCache* getInstance() ; - static BOOL hasInstance() ; + static BOOL hasInstance() ; static void destroyClass() ; }; diff --git a/indra/newview/llvoicecallhandler.cpp b/indra/newview/llvoicecallhandler.cpp new file mode 100644 index 0000000000..274bd75208 --- /dev/null +++ b/indra/newview/llvoicecallhandler.cpp @@ -0,0 +1,61 @@ + /** + * @file llvoicecallhandler.cpp + * @brief slapp to handle avatar to avatar voice call. + * + * $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 "llcommandhandler.h" +#include "llavataractions.h" + +class LLVoiceCallAvatarHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLVoiceCallAvatarHandler() : LLCommandHandler("voicecallavatar", UNTRUSTED_THROTTLE) + { + } + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + //Make sure we have some parameters + if (params.size() == 0) + { + return false; + } + + //Get the ID + LLUUID id; + if (!id.set( params[0], FALSE )) + { + return false; + } + + //instigate call with this avatar + LLAvatarActions::startCall( id ); + return true; + } +}; + +LLVoiceCallAvatarHandler gVoiceCallAvatarHandler; + diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 6c44f639ec..730f022c50 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -35,6 +35,7 @@ #include "llnotificationsutil.h" #include "llsdserialize.h" #include "llui.h" +#include "llkeyboard.h" const F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f; @@ -113,8 +114,18 @@ LLVoiceClient::LLVoiceClient() mVoiceModule(NULL), m_servicePump(NULL), mVoiceEffectEnabled(LLCachedControl<bool>(gSavedSettings, "VoiceMorphingEnabled")), - mVoiceEffectDefault(LLCachedControl<std::string>(gSavedPerAccountSettings, "VoiceEffectDefault")) + mVoiceEffectDefault(LLCachedControl<std::string>(gSavedPerAccountSettings, "VoiceEffectDefault")), + mPTTDirty(true), + mPTT(true), + mUsePTT(true), + mPTTIsMiddleMouse(false), + mPTTKey(0), + mPTTIsToggle(false), + mUserPTTState(false), + mMuteMic(false), + mDisableMic(false) { + updateSettings(); } //--------------------------------------------------- @@ -173,6 +184,14 @@ const LLVoiceVersionInfo LLVoiceClient::getVersion() void LLVoiceClient::updateSettings() { + setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); + std::string keyString = gSavedSettings.getString("PushToTalkButton"); + setPTTKey(keyString); + setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); + mDisableMic = gSavedSettings.getBOOL("VoiceDisableMic"); + + updateMicMuteLogic(); + if (mVoiceModule) mVoiceModule->updateSettings(); } @@ -481,6 +500,26 @@ void LLVoiceClient::setVoiceEnabled(bool enabled) if (mVoiceModule) mVoiceModule->setVoiceEnabled(enabled); } +void LLVoiceClient::updateMicMuteLogic() +{ + // If not configured to use PTT, the mic should be open (otherwise the user will be unable to speak). + bool new_mic_mute = false; + + if(mUsePTT) + { + // If configured to use PTT, track the user state. + new_mic_mute = !mUserPTTState; + } + + if(mMuteMic || mDisableMic) + { + // Either of these always overrides any other PTT setting. + new_mic_mute = true; + } + + if (mVoiceModule) mVoiceModule->setMuteMic(new_mic_mute); +} + void LLVoiceClient::setLipSyncEnabled(BOOL enabled) { if (mVoiceModule) mVoiceModule->setLipSyncEnabled(enabled); @@ -500,7 +539,8 @@ BOOL LLVoiceClient::lipSyncEnabled() void LLVoiceClient::setMuteMic(bool muted) { - if (mVoiceModule) mVoiceModule->setMuteMic(muted); + mMuteMic = muted; + updateMicMuteLogic(); } @@ -509,64 +549,116 @@ void LLVoiceClient::setMuteMic(bool muted) void LLVoiceClient::setUserPTTState(bool ptt) { - if (mVoiceModule) mVoiceModule->setUserPTTState(ptt); + mUserPTTState = ptt; + updateMicMuteLogic(); } bool LLVoiceClient::getUserPTTState() { - if (mVoiceModule) - { - return mVoiceModule->getUserPTTState(); - } - else - { - return false; - } + return mUserPTTState; } void LLVoiceClient::setUsePTT(bool usePTT) { - if (mVoiceModule) mVoiceModule->setUsePTT(usePTT); + if(usePTT && !mUsePTT) + { + // When the user turns on PTT, reset the current state. + mUserPTTState = false; + } + mUsePTT = usePTT; + + updateMicMuteLogic(); } void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle) { - if (mVoiceModule) mVoiceModule->setPTTIsToggle(PTTIsToggle); + if(!PTTIsToggle && mPTTIsToggle) + { + // When the user turns off toggle, reset the current state. + mUserPTTState = false; + } + + mPTTIsToggle = PTTIsToggle; + + updateMicMuteLogic(); } bool LLVoiceClient::getPTTIsToggle() { - if (mVoiceModule) + return mPTTIsToggle; +} + +void LLVoiceClient::setPTTKey(std::string &key) +{ + if(key == "MiddleMouse") { - return mVoiceModule->getPTTIsToggle(); + mPTTIsMiddleMouse = true; } - else { - return false; + else + { + mPTTIsMiddleMouse = false; + if(!LLKeyboard::keyFromString(key, &mPTTKey)) + { + // If the call failed, don't match any key. + key = KEY_NONE; + } } - } void LLVoiceClient::inputUserControlState(bool down) { - if (mVoiceModule) mVoiceModule->inputUserControlState(down); + if(mPTTIsToggle) + { + if(down) // toggle open-mic state on 'down' + { + toggleUserPTTState(); + } + } + else // set open-mic state as an absolute + { + setUserPTTState(down); + } } void LLVoiceClient::toggleUserPTTState(void) { - if (mVoiceModule) mVoiceModule->toggleUserPTTState(); + setUserPTTState(!getUserPTTState()); } void LLVoiceClient::keyDown(KEY key, MASK mask) { - if (mVoiceModule) mVoiceModule->keyDown(key, mask); + if (gKeyboard->getKeyRepeated(key)) + { + // ignore auto-repeat keys + return; + } + + if(!mPTTIsMiddleMouse) + { + bool down = (mPTTKey != KEY_NONE) + && gKeyboard->getKeyDown(mPTTKey); + inputUserControlState(down); + } + } void LLVoiceClient::keyUp(KEY key, MASK mask) { - if (mVoiceModule) mVoiceModule->keyUp(key, mask); + if(!mPTTIsMiddleMouse) + { + bool down = (mPTTKey != KEY_NONE) + && gKeyboard->getKeyDown(mPTTKey); + inputUserControlState(down); + } } void LLVoiceClient::middleMouseState(bool down) { - if (mVoiceModule) mVoiceModule->middleMouseState(down); + if(mPTTIsMiddleMouse) + { + if(mPTTIsMiddleMouse) + { + inputUserControlState(down); + } + } } diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 24d7d7163e..c9aeea35a9 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -191,25 +191,9 @@ public: virtual void setVoiceEnabled(bool enabled)=0; virtual void setLipSyncEnabled(BOOL enabled)=0; virtual BOOL lipSyncEnabled()=0; - virtual void setMuteMic(bool muted)=0; // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. + virtual void setMuteMic(bool muted)=0; // Set the mute state of the local mic. //@} - - //////////////////////// - /// @name PTT - //@{ - virtual void setUserPTTState(bool ptt)=0; - virtual bool getUserPTTState()=0; - virtual void setUsePTT(bool usePTT)=0; - virtual void setPTTIsToggle(bool PTTIsToggle)=0; - virtual bool getPTTIsToggle()=0; - virtual void toggleUserPTTState(void)=0; - virtual void inputUserControlState(bool down)=0; // interpret any sort of up-down mic-open control input according to ptt-toggle prefs - - virtual void keyDown(KEY key, MASK mask)=0; - virtual void keyUp(KEY key, MASK mask)=0; - virtual void middleMouseState(bool down)=0; - //@} - + ////////////////////////// /// @name nearby speaker accessors //@{ @@ -406,6 +390,9 @@ public: void setUsePTT(bool usePTT); void setPTTIsToggle(bool PTTIsToggle); bool getPTTIsToggle(); + void setPTTKey(std::string &key); + + void updateMicMuteLogic(); BOOL lipSyncEnabled(); @@ -471,6 +458,17 @@ protected: LLCachedControl<bool> mVoiceEffectEnabled; LLCachedControl<std::string> mVoiceEffectDefault; + + bool mPTTDirty; + bool mPTT; + + bool mUsePTT; + bool mPTTIsMiddleMouse; + KEY mPTTKey; + bool mPTTIsToggle; + bool mUserPTTState; + bool mMuteMic; + bool mDisableMic; }; /** diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 019629084f..08e242af8e 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -46,7 +46,6 @@ #include "llviewernetwork.h" // for gGridChoice #include "llbase64.h" #include "llviewercontrol.h" -#include "llkeyboard.h" #include "llappviewer.h" // for gDisconnected, gDisableVoice // Viewer includes @@ -326,14 +325,8 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mRenderDeviceDirty(false), mSpatialCoordsDirty(false), - mPTTDirty(true), - mPTT(true), - mUsePTT(true), - mPTTIsMiddleMouse(false), - mPTTKey(0), - mPTTIsToggle(false), - mUserPTTState(false), mMuteMic(false), + mMuteMicDirty(false), mFriendsListDirty(true), mEarLocation(0), @@ -435,10 +428,6 @@ const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion() void LLVivoxVoiceClient::updateSettings() { setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat")); - setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); - std::string keyString = gSavedSettings.getString("PushToTalkButton"); - setPTTKey(keyString); - setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); @@ -950,7 +939,7 @@ void LLVivoxVoiceClient::stateMachine() setState(stateDaemonLaunched); // Dirty the states we'll need to sync with the daemon when it comes up. - mPTTDirty = true; + mMuteMicDirty = true; mMicVolumeDirty = true; mSpeakerVolumeDirty = true; mSpeakerMuteDirty = true; @@ -1535,7 +1524,7 @@ void LLVivoxVoiceClient::stateMachine() if(mAudioSession && mAudioSession->mVoiceEnabled) { // Dirty state that may need to be sync'ed with the daemon. - mPTTDirty = true; + mMuteMicDirty = true; mSpeakerVolumeDirty = true; mSpatialCoordsDirty = true; @@ -1576,35 +1565,6 @@ void LLVivoxVoiceClient::stateMachine() } else { - - // Figure out whether the PTT state needs to change - { - bool newPTT; - if(mUsePTT) - { - // If configured to use PTT, track the user state. - newPTT = mUserPTTState; - } - else - { - // If not configured to use PTT, it should always be true (otherwise the user will be unable to speak). - newPTT = true; - } - - if(mMuteMic) - { - // This always overrides any other PTT setting. - newPTT = false; - } - - // Dirty if state changed. - if(newPTT != mPTT) - { - mPTT = newPTT; - mPTTDirty = true; - } - } - if(!inSpatialChannel()) { // When in a non-spatial channel, never send positional updates. @@ -1626,7 +1586,7 @@ void LLVivoxVoiceClient::stateMachine() // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often // -- the user can only click so fast) or every 10hz, whichever is sooner. // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged. - if((mAudioSession && mAudioSession->mMuteDirty) || mPTTDirty || mUpdateTimer.hasExpired()) + if((mAudioSession && mAudioSession->mMuteDirty) || mMuteMicDirty || mUpdateTimer.hasExpired()) { mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); sendPositionalUpdate(); @@ -2749,19 +2709,17 @@ void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) buildSetRenderDevice(stream); - if(mPTTDirty) + if(mMuteMicDirty) { - mPTTDirty = false; + mMuteMicDirty = false; // Send a local mute command. - // NOTE that the state of "PTT" is the inverse of "local mute". - // (i.e. when PTT is true, we send a mute command with "false", and vice versa) - LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL; + LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic?"true":"false") << LL_ENDL; stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" - << "<Value>" << (mPTT?"false":"true") << "</Value>" + << "<Value>" << (mMuteMic?"true":"false") << "</Value>" << "</Request>\n\n\n"; } @@ -5238,40 +5196,13 @@ void LLVivoxVoiceClient::leaveChannel(void) void LLVivoxVoiceClient::setMuteMic(bool muted) { - mMuteMic = muted; -} - -void LLVivoxVoiceClient::setUserPTTState(bool ptt) -{ - mUserPTTState = ptt; -} - -bool LLVivoxVoiceClient::getUserPTTState() -{ - return mUserPTTState; -} - -void LLVivoxVoiceClient::inputUserControlState(bool down) -{ - if(mPTTIsToggle) + if(mMuteMic != muted) { - if(down) // toggle open-mic state on 'down' - { - toggleUserPTTState(); - } - } - else // set open-mic state as an absolute - { - setUserPTTState(down); + mMuteMic = muted; + mMuteMicDirty = true; } } - -void LLVivoxVoiceClient::toggleUserPTTState(void) -{ - mUserPTTState = !mUserPTTState; -} - void LLVivoxVoiceClient::setVoiceEnabled(bool enabled) { if (enabled != mVoiceEnabled) @@ -5320,48 +5251,6 @@ BOOL LLVivoxVoiceClient::lipSyncEnabled() } } -void LLVivoxVoiceClient::setUsePTT(bool usePTT) -{ - if(usePTT && !mUsePTT) - { - // When the user turns on PTT, reset the current state. - mUserPTTState = false; - } - mUsePTT = usePTT; -} - -void LLVivoxVoiceClient::setPTTIsToggle(bool PTTIsToggle) -{ - if(!PTTIsToggle && mPTTIsToggle) - { - // When the user turns off toggle, reset the current state. - mUserPTTState = false; - } - - mPTTIsToggle = PTTIsToggle; -} - -bool LLVivoxVoiceClient::getPTTIsToggle() -{ - return mPTTIsToggle; -} - -void LLVivoxVoiceClient::setPTTKey(std::string &key) -{ - if(key == "MiddleMouse") - { - mPTTIsMiddleMouse = true; - } - else - { - mPTTIsMiddleMouse = false; - if(!LLKeyboard::keyFromString(key, &mPTTKey)) - { - // If the call failed, don't match any key. - key = KEY_NONE; - } - } -} void LLVivoxVoiceClient::setEarLocation(S32 loc) { @@ -5402,44 +5291,6 @@ void LLVivoxVoiceClient::setMicGain(F32 volume) } } -void LLVivoxVoiceClient::keyDown(KEY key, MASK mask) -{ - if (gKeyboard->getKeyRepeated(key)) - { - // ignore auto-repeat keys - return; - } - - if(!mPTTIsMiddleMouse) - { - bool down = (mPTTKey != KEY_NONE) - && gKeyboard->getKeyDown(mPTTKey); - inputUserControlState(down); - } - - -} -void LLVivoxVoiceClient::keyUp(KEY key, MASK mask) -{ - if(!mPTTIsMiddleMouse) - { - bool down = (mPTTKey != KEY_NONE) - && gKeyboard->getKeyDown(mPTTKey); - inputUserControlState(down); - } - -} -void LLVivoxVoiceClient::middleMouseState(bool down) -{ - if(mPTTIsMiddleMouse) - { - if(mPTTIsMiddleMouse) - { - inputUserControlState(down); - } - } -} - ///////////////////////////// // Accessors for data related to nearby speakers BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id) @@ -7015,8 +6866,8 @@ void LLVivoxVoiceClient::captureBufferRecordStartSendMessage() << "<Value>false</Value>" << "</Request>\n\n\n"; - // Dirty the PTT state so that it will get reset when we finishing previewing - mPTTDirty = true; + // Dirty the mute mic state so that it will get reset when we finishing previewing + mMuteMicDirty = true; writeString(stream.str()); } @@ -7030,7 +6881,7 @@ void LLVivoxVoiceClient::captureBufferRecordStopSendMessage() LL_DEBUGS("Voice") << "Stopping audio capture to buffer." << LL_ENDL; - // Mute the mic. PTT state was dirtied at recording start, so will be reset when finished previewing. + // Mute the mic. Mic mute state was dirtied at recording start, so will be reset when finished previewing. stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" << "<Value>true</Value>" diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 3ba517bf36..471545de56 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -173,25 +173,9 @@ public: virtual void setVoiceEnabled(bool enabled); virtual BOOL lipSyncEnabled(); virtual void setLipSyncEnabled(BOOL enabled); - virtual void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. + virtual void setMuteMic(bool muted); // Set the mute state of the local mic. //@} - - //////////////////////// - /// @name PTT - //@{ - virtual void setUserPTTState(bool ptt); - virtual bool getUserPTTState(); - virtual void setUsePTT(bool usePTT); - virtual void setPTTIsToggle(bool PTTIsToggle); - virtual bool getPTTIsToggle(); - virtual void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs - virtual void toggleUserPTTState(void); - - virtual void keyDown(KEY key, MASK mask); - virtual void keyUp(KEY key, MASK mask); - virtual void middleMouseState(bool down); - //@} - + ////////////////////////// /// @name nearby speaker accessors //@{ @@ -534,9 +518,6 @@ protected: // Use this to determine whether to show a "no speech" icon in the menu bar. - // PTT - void setPTTKey(std::string &key); - ///////////////////////////// // Recording controls void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); @@ -800,15 +781,8 @@ private: LLVector3 mAvatarVelocity; LLMatrix3 mAvatarRot; - bool mPTTDirty; - bool mPTT; - - bool mUsePTT; - bool mPTTIsMiddleMouse; - KEY mPTTKey; - bool mPTTIsToggle; - bool mUserPTTState; bool mMuteMic; + bool mMuteMicDirty; // Set to true when the friends list is known to have changed. bool mFriendsListDirty; diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp index eba600b50a..2eb4398488 100644 --- a/indra/newview/llvosurfacepatch.cpp +++ b/indra/newview/llvosurfacepatch.cpp @@ -80,7 +80,7 @@ public: //============================================================================ LLVOSurfacePatch::LLVOSurfacePatch(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) - : LLStaticViewerObject(id, LL_VO_SURFACE_PATCH, regionp), + : LLStaticViewerObject(id, pcode, regionp), mDirtiedPatch(FALSE), mPool(NULL), mBaseComp(0), diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp index 598938b710..71f08ec36d 100644 --- a/indra/newview/llvowater.cpp +++ b/indra/newview/llvowater.cpp @@ -60,8 +60,11 @@ const U32 WIDTH = (N_RES * WAVE_STEP); //128.f //64 // width of wave tile, in const F32 WAVE_STEP_INV = (1. / WAVE_STEP); -LLVOWater::LLVOWater(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) -: LLStaticViewerObject(id, LL_VO_WATER, regionp) +LLVOWater::LLVOWater(const LLUUID &id, + const LLPCode pcode, + LLViewerRegion *regionp) : + LLStaticViewerObject(id, pcode, regionp), + mRenderType(LLPipeline::RENDER_TYPE_WATER) { // Terrain must draw during selection passes so it can block objects behind it. mbCanSelect = FALSE; @@ -114,7 +117,7 @@ LLDrawable *LLVOWater::createDrawable(LLPipeline *pipeline) { pipeline->allocDrawable(this); mDrawable->setLit(FALSE); - mDrawable->setRenderType(LLPipeline::RENDER_TYPE_WATER); + mDrawable->setRenderType(mRenderType); LLDrawPoolWater *pool = (LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER); @@ -152,11 +155,17 @@ BOOL LLVOWater::updateGeometry(LLDrawable *drawable) LLStrider<U16> indicesp; U16 index_offset; - S32 size = 16; - S32 num_quads = size*size; - face->setSize(4*num_quads, 6*num_quads); + // A quad is 4 vertices and 6 indices (making 2 triangles) + static const unsigned int vertices_per_quad = 4; + static const unsigned int indices_per_quad = 6; + const S32 size = gSavedSettings.getBOOL("RenderTransparentWater") ? 16 : 1; + + const S32 num_quads = size * size; + face->setSize(vertices_per_quad * num_quads, + indices_per_quad * num_quads); + if (face->mVertexBuffer.isNull()) { face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_DYNAMIC_DRAW_ARB); @@ -268,6 +277,11 @@ U32 LLVOWater::getPartitionType() const return LLViewerRegion::PARTITION_WATER; } +U32 LLVOVoidWater::getPartitionType() const +{ + return LLViewerRegion::PARTITION_VOIDWATER; +} + LLWaterPartition::LLWaterPartition() : LLSpatialPartition(0, FALSE, 0) { @@ -275,3 +289,9 @@ LLWaterPartition::LLWaterPartition() mDrawableType = LLPipeline::RENDER_TYPE_WATER; mPartitionType = LLViewerRegion::PARTITION_WATER; } + +LLVoidWaterPartition::LLVoidWaterPartition() +{ + mDrawableType = LLPipeline::RENDER_TYPE_VOIDWATER; + mPartitionType = LLViewerRegion::PARTITION_VOIDWATER; +} diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h index beefc3f17f..cb9584cabf 100644 --- a/indra/newview/llvowater.h +++ b/indra/newview/llvowater.h @@ -29,6 +29,7 @@ #include "llviewerobject.h" #include "llviewertexture.h" +#include "pipeline.h" #include "v2math.h" const U32 N_RES = 16; //32 // number of subdivisions of wave tile @@ -77,6 +78,19 @@ public: protected: BOOL mUseTexture; BOOL mIsEdgePatch; + S32 mRenderType; }; +class LLVOVoidWater : public LLVOWater +{ +public: + LLVOVoidWater(LLUUID const& id, LLPCode pcode, LLViewerRegion* regionp) : LLVOWater(id, pcode, regionp) + { + mRenderType = LLPipeline::RENDER_TYPE_VOIDWATER; + } + + /*virtual*/ U32 getPartitionType() const; +}; + + #endif // LL_VOSURFACEPATCH_H diff --git a/indra/newview/llwaterparammanager.cpp b/indra/newview/llwaterparammanager.cpp index 7314894c2e..d239347810 100644 --- a/indra/newview/llwaterparammanager.cpp +++ b/indra/newview/llwaterparammanager.cpp @@ -89,7 +89,7 @@ void LLWaterParamManager::loadAllPresets(const std::string& file_name) while(found) { std::string name; - found = gDirUtilp->getNextFileInDir(path_name, "*.xml", name, false); + found = gDirUtilp->getNextFileInDir(path_name, "*.xml", name); if(found) { @@ -115,7 +115,7 @@ void LLWaterParamManager::loadAllPresets(const std::string& file_name) while(found) { std::string name; - found = gDirUtilp->getNextFileInDir(path_name2, "*.xml", name, false); + found = gDirUtilp->getNextFileInDir(path_name2, "*.xml", name); if(found) { name=name.erase(name.length()-4); diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index 73a37a6993..b73017a51a 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -35,6 +35,7 @@ #include "llagent.h" #include "llappviewer.h" #include "llfloatermediabrowser.h" +#include "llfloaterwebcontent.h" #include "llfloaterreg.h" #include "lllogininstance.h" #include "llparcel.h" @@ -95,6 +96,23 @@ void LLWeb::loadURL(const std::string& url, const std::string& target, const std } } +// static +void LLWeb::loadWebURL(const std::string& url, const std::string& target, const std::string& uuid) +{ + if(target == "_internal") + { + // Force load in the internal browser, as if with a blank target. + loadWebURLInternal(url, "", uuid); + } + else if (gSavedSettings.getBOOL("UseExternalBrowser") || (target == "_external")) + { + loadURLExternal(url); + } + else + { + loadWebURLInternal(url, target, uuid); + } +} // static void LLWeb::loadURLInternal(const std::string &url, const std::string& target, const std::string& uuid) @@ -102,6 +120,13 @@ void LLWeb::loadURLInternal(const std::string &url, const std::string& target, c LLFloaterMediaBrowser::create(url, target, uuid); } +// static +// Explicitly open a Web URL using the Web content floater +void LLWeb::loadWebURLInternal(const std::string &url, const std::string& target, const std::string& uuid) +{ + LLFloaterWebContent::create(url, target, uuid); +} + // static void LLWeb::loadURLExternal(const std::string& url, const std::string& uuid) @@ -116,6 +141,13 @@ void LLWeb::loadURLExternal(const std::string& url, bool async, const std::strin // Act like the proxy window was closed, since we won't be able to track targeted windows in the external browser. LLViewerMedia::proxyWindowClosed(uuid); + if(gSavedSettings.getBOOL("DisableExternalBrowser")) + { + // Don't open an external browser under any circumstances. + llwarns << "Blocked attempt to open external browser." << llendl; + return; + } + LLSD payload; payload["url"] = url; LLNotificationsUtil::add( "WebLaunchExternalTarget", LLSD(), payload, boost::bind(on_load_url_external_response, _1, _2, async)); diff --git a/indra/newview/llweb.h b/indra/newview/llweb.h index 2915376583..dc5958e57f 100644 --- a/indra/newview/llweb.h +++ b/indra/newview/llweb.h @@ -57,6 +57,11 @@ public: static void loadURLExternal(const std::string& url, const std::string& uuid); static void loadURLExternal(const std::string& url, bool async, const std::string& uuid = LLStringUtil::null); + // Explicitly open a Web URL using the Web content floater vs. the more general media browser + static void loadWebURL(const std::string& url, const std::string& target, const std::string& uuid); + static void loadWebURLInternal(const std::string &url, const std::string& target, const std::string& uuid); + static void loadWebURLInternal(const std::string &url) { loadWebURLInternal(url, LLStringUtil::null, LLStringUtil::null); } + /// Returns escaped url (eg, " " to "%20") - used by all loadURL methods static std::string escapeURL(const std::string& url); /// Expands various strings like [LANG], [VERSION], etc. in a URL diff --git a/indra/newview/llwlparammanager.cpp b/indra/newview/llwlparammanager.cpp index 9b6047395a..e5f52dfc97 100644 --- a/indra/newview/llwlparammanager.cpp +++ b/indra/newview/llwlparammanager.cpp @@ -104,7 +104,7 @@ void LLWLParamManager::loadPresets(const std::string& file_name) while(found) { std::string name; - found = gDirUtilp->getNextFileInDir(path_name, "*.xml", name, false); + found = gDirUtilp->getNextFileInDir(path_name, "*.xml", name); if(found) { @@ -130,7 +130,7 @@ void LLWLParamManager::loadPresets(const std::string& file_name) while(found) { std::string name; - found = gDirUtilp->getNextFileInDir(path_name2, "*.xml", name, false); + found = gDirUtilp->getNextFileInDir(path_name2, "*.xml", name); if(found) { name=name.erase(name.length()-4); diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 5760d04a08..399442e5c4 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -55,6 +55,11 @@ #include "pipeline.h" #include "llappviewer.h" // for do_disconnect() +#include <deque> +#include <queue> +#include <map> +#include <cstring> + // // Globals // @@ -121,7 +126,10 @@ void LLWorld::destroyClass() LLViewerRegion* region_to_delete = *region_it++; removeRegion(region_to_delete->getHost()); } - LLVOCache::getInstance()->destroyClass() ; + if(LLVOCache::hasInstance()) + { + LLVOCache::getInstance()->destroyClass() ; + } LLViewerPartSim::getInstance()->destroyClass(); } @@ -423,14 +431,15 @@ BOOL LLWorld::positionRegionValidGlobal(const LLVector3d &pos_global) // Allow objects to go up to their radius underground. -F32 LLWorld::getMinAllowedZ(LLViewerObject* object) +F32 LLWorld::getMinAllowedZ(LLViewerObject* object, const LLVector3d &global_pos) { - F32 land_height = resolveLandHeightGlobal(object->getPositionGlobal()); + F32 land_height = resolveLandHeightGlobal(global_pos); F32 radius = 0.5f * object->getScale().length(); return land_height - radius; } + LLViewerRegion* LLWorld::resolveRegionGlobal(LLVector3 &pos_region, const LLVector3d &pos_global) { LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global); @@ -834,10 +843,69 @@ F32 LLWorld::getLandFarClip() const void LLWorld::setLandFarClip(const F32 far_clip) { + static S32 const rwidth = (S32)REGION_WIDTH_U32; + S32 const n1 = (llceil(mLandFarClip) - 1) / rwidth; + S32 const n2 = (llceil(far_clip) - 1) / rwidth; + bool need_water_objects_update = n1 != n2; + mLandFarClip = far_clip; + + if (need_water_objects_update) + { + updateWaterObjects(); + } } +// Some region that we're connected to, but not the one we're in, gave us +// a (possibly) new water height. Update it in our local copy. +void LLWorld::waterHeightRegionInfo(std::string const& sim_name, F32 water_height) +{ + for (region_list_t::iterator iter = mRegionList.begin(); iter != mRegionList.end(); ++iter) + { + if ((*iter)->getName() == sim_name) + { + (*iter)->setWaterHeight(water_height); + break; + } + } +} +// There are three types of water objects: +// Region water objects: the water in a region. +// Hole water objects: water in the void but within current draw distance. +// Edge water objects: the water outside the draw distance, up till the horizon. +// +// For example: +// +// -----------------------horizon------------------------- +// | | | | +// | Edge Water | | | +// | | | | +// | | | | +// | | | | +// | | | | +// | | rwidth | | +// | | <-----> | | +// ------------------------------------------------------- +// | |Hole |other| | | +// | |Water|reg. | | | +// | |-----------------| | +// | |other|cur. |<--> | | +// | |reg. | reg.| \__|_ draw distance | +// | |-----------------| | +// | | | |<--->| | +// | | | | \__|_ range | +// ------------------------------------------------------- +// | |<----width------>|<--horizon ext.->| +// | | | | +// | | | | +// | | | | +// | | | | +// | | | | +// | | | | +// | | | | +// ------------------------------------------------------- +// void LLWorld::updateWaterObjects() { if (!gAgent.getRegion()) @@ -850,128 +918,265 @@ void LLWorld::updateWaterObjects() return; } - // First, determine the min and max "box" of water objects - S32 min_x = 0; - S32 min_y = 0; - S32 max_x = 0; - S32 max_y = 0; + // Region width in meters. + S32 const rwidth = (S32)REGION_WIDTH_U32; + + // The distance we might see into the void + // when standing on the edge of a region, in meters. + S32 const draw_distance = llceil(mLandFarClip); + + // We can only have "holes" in the water (where there no region) if we + // can have existing regions around it. Taking into account that this + // code is only executed when we enter a region, and not when we walk + // around in it, we (only) need to take into account regions that fall + // within the draw_distance. + // + // Set 'range' to draw_distance, rounded up to the nearest multiple of rwidth. + S32 const nsims = (draw_distance + rwidth - 1) / rwidth; + S32 const range = nsims * rwidth; + + // Get South-West corner of current region. + LLViewerRegion const* regionp = gAgent.getRegion(); U32 region_x, region_y; - - S32 rwidth = 256; - - // We only want to fill in water for stuff that's near us, say, within 256 or 512m - S32 range = LLViewerCamera::getInstance()->getFar() > 256.f ? 512 : 256; - - LLViewerRegion* regionp = gAgent.getRegion(); from_region_handle(regionp->getHandle(), ®ion_x, ®ion_y); - min_x = (S32)region_x - range; - min_y = (S32)region_y - range; - max_x = (S32)region_x + range; - max_y = (S32)region_y + range; + // The min. and max. coordinates of the South-West corners of the Hole water objects. + S32 const min_x = (S32)region_x - range; + S32 const min_y = (S32)region_y - range; + S32 const max_x = (S32)region_x + range; + S32 const max_y = (S32)region_y + range; + + // Attempt to determine a sensible water height for all the + // Hole Water objects. + // + // It make little sense to try to guess what the best water + // height should be when that isn't completely obvious: if it's + // impossible to satisfy every region's water height without + // getting a jump in the water height. + // + // In order to keep the reasoning simple, we assume something + // logical as a group of connected regions, where the coastline + // is at the outer edge. Anything more complex that would "break" + // under such an assumption would probably break anyway (would + // depend on terrain editing and existing mega prims, say, if + // anything would make sense at all). + // + // So, what we do is find all connected regions within the + // draw distance that border void, and then pick the lowest + // water height of those (coast) regions. + S32 const n = 2 * nsims + 1; + S32 const origin = nsims + nsims * n; + std::vector<F32> water_heights(n * n); + std::vector<U8> checked(n * n, 0); // index = nx + ny * n + origin; + U8 const region_bit = 1; + U8 const hole_bit = 2; + U8 const bordering_hole_bit = 4; + U8 const bordering_edge_bit = 8; + // Use the legacy waterheight for the Edge water in the case + // that we don't find any Hole water at all. + F32 water_height = DEFAULT_WATER_HEIGHT; + int max_count = 0; + LL_DEBUGS("WaterHeight") << "Current region: " << regionp->getName() << "; water height: " << regionp->getWaterHeight() << " m." << LL_ENDL; + std::map<S32, int> water_height_counts; + typedef std::queue<std::pair<S32, S32>, std::deque<std::pair<S32, S32> > > nxny_pairs_type; + nxny_pairs_type nxny_pairs; + nxny_pairs.push(nxny_pairs_type::value_type(0, 0)); + water_heights[origin] = regionp->getWaterHeight(); + checked[origin] = region_bit; + // For debugging purposes. + int number_of_connected_regions = 1; + int uninitialized_regions = 0; + int bordering_hole = 0; + int bordering_edge = 0; + while(!nxny_pairs.empty()) + { + S32 const nx = nxny_pairs.front().first; + S32 const ny = nxny_pairs.front().second; + LL_DEBUGS("WaterHeight") << "nx,ny = " << nx << "," << ny << LL_ENDL; + S32 const index = nx + ny * n + origin; + nxny_pairs.pop(); + for (S32 dir = 0; dir < 4; ++dir) + { + S32 const cnx = nx + gDirAxes[dir][0]; + S32 const cny = ny + gDirAxes[dir][1]; + LL_DEBUGS("WaterHeight") << "dir = " << dir << "; cnx,cny = " << cnx << "," << cny << LL_ENDL; + S32 const cindex = cnx + cny * n + origin; + bool is_hole = false; + bool is_edge = false; + LLViewerRegion* new_region_found = NULL; + if (cnx < -nsims || cnx > nsims || + cny < -nsims || cny > nsims) + { + LL_DEBUGS("WaterHeight") << " Edge Water!" << LL_ENDL; + // Bumped into Edge water object. + is_edge = true; + } + else if (checked[cindex]) + { + LL_DEBUGS("WaterHeight") << " Already checked before!" << LL_ENDL; + // Already checked. + is_hole = (checked[cindex] & hole_bit); + } + else + { + S32 x = (S32)region_x + cnx * rwidth; + S32 y = (S32)region_y + cny * rwidth; + U64 region_handle = to_region_handle(x, y); + new_region_found = getRegionFromHandle(region_handle); + is_hole = !new_region_found; + checked[cindex] = is_hole ? hole_bit : region_bit; + } + if (is_hole) + { + // This was a region that borders at least one 'hole'. + // Count the found coastline. + F32 new_water_height = water_heights[index]; + LL_DEBUGS("WaterHeight") << " This is void; counting coastline with water height of " << new_water_height << LL_ENDL; + S32 new_water_height_cm = llround(new_water_height * 100); + int count = (water_height_counts[new_water_height_cm] += 1); + // Just use the lowest water height: this is mainly about the horizon water, + // and whatever we do, we don't want it to be possible to look under the water + // when looking in the distance: it is better to make a step downwards in water + // height when going away from the avie than a step upwards. However, since + // everyone is used to DEFAULT_WATER_HEIGHT, don't allow a single region + // to drag the water level below DEFAULT_WATER_HEIGHT on it's own. + if (bordering_hole == 0 || // First time we get here. + (new_water_height >= DEFAULT_WATER_HEIGHT && + new_water_height < water_height) || + (new_water_height < DEFAULT_WATER_HEIGHT && + count > max_count) + ) + { + water_height = new_water_height; + } + if (count > max_count) + { + max_count = count; + } + if (!(checked[index] & bordering_hole_bit)) + { + checked[index] |= bordering_hole_bit; + ++bordering_hole; + } + } + else if (is_edge && !(checked[index] & bordering_edge_bit)) + { + checked[index] |= bordering_edge_bit; + ++bordering_edge; + } + if (!new_region_found) + { + // Dead end, there is no region here. + continue; + } + // Found a new connected region. + ++number_of_connected_regions; + if (new_region_found->getName().empty()) + { + // Uninitialized LLViewerRegion, don't use it's water height. + LL_DEBUGS("WaterHeight") << " Uninitialized region." << LL_ENDL; + ++uninitialized_regions; + continue; + } + nxny_pairs.push(nxny_pairs_type::value_type(cnx, cny)); + water_heights[cindex] = new_region_found->getWaterHeight(); + LL_DEBUGS("WaterHeight") << " Found a new region (name: " << new_region_found->getName() << "; water height: " << water_heights[cindex] << " m)!" << LL_ENDL; + } + } + llinfos << "Number of connected regions: " << number_of_connected_regions << " (" << uninitialized_regions << + " uninitialized); number of regions bordering Hole water: " << bordering_hole << + "; number of regions bordering Edge water: " << bordering_edge << llendl; + llinfos << "Coastline count (height, count): "; + bool first = true; + for (std::map<S32, int>::iterator iter = water_height_counts.begin(); iter != water_height_counts.end(); ++iter) + { + if (!first) llcont << ", "; + llcont << "(" << (iter->first / 100.f) << ", " << iter->second << ")"; + first = false; + } + llcont << llendl; + llinfos << "Water height used for Hole and Edge water objects: " << water_height << llendl; - F32 height = 0.f; - - for (region_list_t::iterator iter = mRegionList.begin(); - iter != mRegionList.end(); ++iter) + // Update all Region water objects. + for (region_list_t::iterator iter = mRegionList.begin(); iter != mRegionList.end(); ++iter) { LLViewerRegion* regionp = *iter; LLVOWater* waterp = regionp->getLand().getWaterObj(); - height += regionp->getWaterHeight(); if (waterp) { gObjectList.updateActive(waterp); } } + // Clean up all existing Hole water objects. for (std::list<LLVOWater*>::iterator iter = mHoleWaterObjects.begin(); - iter != mHoleWaterObjects.end(); ++ iter) + iter != mHoleWaterObjects.end(); ++iter) { LLVOWater* waterp = *iter; gObjectList.killObject(waterp); } mHoleWaterObjects.clear(); - // Now, get a list of the holes - S32 x, y; - for (x = min_x; x <= max_x; x += rwidth) + // Let the Edge and Hole water boxes be 1024 meter high so that they + // are never too small to be drawn (A LL_VO_*_WATER box has water + // rendered on it's bottom surface only), and put their bottom at + // the current regions water height. + F32 const box_height = 1024; + F32 const water_center_z = water_height + box_height / 2; + + // Create new Hole water objects within 'range' where there is no region. + for (S32 x = min_x; x <= max_x; x += rwidth) { - for (y = min_y; y <= max_y; y += rwidth) + for (S32 y = min_y; y <= max_y; y += rwidth) { U64 region_handle = to_region_handle(x, y); if (!getRegionFromHandle(region_handle)) { - LLVOWater* waterp = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_WATER, gAgent.getRegion()); + LLVOWater* waterp = (LLVOWater*)gObjectList.createObjectViewer(LLViewerObject::LL_VO_VOID_WATER, gAgent.getRegion()); waterp->setUseTexture(FALSE); - waterp->setPositionGlobal(LLVector3d(x + rwidth/2, - y + rwidth/2, - 256.f+DEFAULT_WATER_HEIGHT)); - waterp->setScale(LLVector3((F32)rwidth, (F32)rwidth, 512.f)); + waterp->setPositionGlobal(LLVector3d(x + rwidth / 2, y + rwidth / 2, water_center_z)); + waterp->setScale(LLVector3((F32)rwidth, (F32)rwidth, box_height)); gPipeline.createObject(waterp); mHoleWaterObjects.push_back(waterp); } } } - // Update edge water objects - S32 wx, wy; - S32 center_x, center_y; - wx = (max_x - min_x) + rwidth; - wy = (max_y - min_y) + rwidth; - center_x = min_x + (wx >> 1); - center_y = min_y + (wy >> 1); - - S32 add_boundary[4] = { - 512 - (max_x - region_x), - 512 - (max_y - region_y), - 512 - (region_x - min_x), - 512 - (region_y - min_y) }; + // Center of the region. + S32 const center_x = region_x + rwidth / 2; + S32 const center_y = region_y + rwidth / 2; + // Width of the area with Hole water objects. + S32 const width = rwidth + 2 * range; + S32 const horizon_extend = 2048 + 512 - range; // Legacy value. + // The overlap is needed to get rid of sky pixels being visible between the + // Edge and Hole water object at greater distances (due to floating point + // round off errors). + S32 const edge_hole_overlap = 1; // Twice the actual overlap. - S32 dir; - for (dir = 0; dir < 8; dir++) + for (S32 dir = 0; dir < 8; ++dir) { - S32 dim[2] = { 0 }; - switch (gDirAxes[dir][0]) - { - case -1: dim[0] = add_boundary[2]; break; - case 0: dim[0] = wx; break; - default: dim[0] = add_boundary[0]; break; - } - switch (gDirAxes[dir][1]) - { - case -1: dim[1] = add_boundary[3]; break; - case 0: dim[1] = wy; break; - default: dim[1] = add_boundary[1]; break; - } + // Size of the Edge water objects. + S32 const dim_x = (gDirAxes[dir][0] == 0) ? width : (horizon_extend + edge_hole_overlap); + S32 const dim_y = (gDirAxes[dir][1] == 0) ? width : (horizon_extend + edge_hole_overlap); + // And their position. + S32 const water_center_x = center_x + (width + horizon_extend) / 2 * gDirAxes[dir][0]; + S32 const water_center_y = center_y + (width + horizon_extend) / 2 * gDirAxes[dir][1]; - // Resize and reshape the water objects - const S32 water_center_x = center_x + llround((wx + dim[0]) * 0.5f * gDirAxes[dir][0]); - const S32 water_center_y = center_y + llround((wy + dim[1]) * 0.5f * gDirAxes[dir][1]); - LLVOWater* waterp = mEdgeWaterObjects[dir]; if (!waterp || waterp->isDead()) { // The edge water objects can be dead because they're attached to the region that the // agent was in when they were originally created. - mEdgeWaterObjects[dir] = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_WATER, - gAgent.getRegion()); + mEdgeWaterObjects[dir] = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_VOID_WATER, gAgent.getRegion()); waterp = mEdgeWaterObjects[dir]; waterp->setUseTexture(FALSE); - waterp->setIsEdgePatch(TRUE); + waterp->setIsEdgePatch(TRUE); // Mark that this is edge water and not hole water. gPipeline.createObject(waterp); } waterp->setRegion(gAgent.getRegion()); - LLVector3d water_pos(water_center_x, water_center_y, - DEFAULT_WATER_HEIGHT+256.f); - LLVector3 water_scale((F32) dim[0], (F32) dim[1], 512.f); - - //stretch out to horizon - water_scale.mV[0] += fabsf(2048.f * gDirAxes[dir][0]); - water_scale.mV[1] += fabsf(2048.f * gDirAxes[dir][1]); - - water_pos.mdV[0] += 1024.f * gDirAxes[dir][0]; - water_pos.mdV[1] += 1024.f * gDirAxes[dir][1]; + LLVector3d water_pos(water_center_x, water_center_y, water_center_z); + LLVector3 water_scale((F32) dim_x, (F32) dim_y, box_height); waterp->setPositionGlobal(water_pos); waterp->setScale(water_scale); diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h index 4465fde210..d8ab4bc508 100644 --- a/indra/newview/llworld.h +++ b/indra/newview/llworld.h @@ -89,7 +89,7 @@ public: // Return the lowest allowed Z point to prevent objects from being moved // underground. - F32 getMinAllowedZ(LLViewerObject* object); + F32 getMinAllowedZ(LLViewerObject* object, const LLVector3d &global_pos); // takes a line segment defined by point_a and point_b, then // determines the closest (to point_a) point of intersection that is @@ -137,6 +137,7 @@ public: LLViewerTexture *getDefaultWaterTexture(); void updateWaterObjects(); + void waterHeightRegionInfo(std::string const& sim_name, F32 water_height); void shiftRegions(const LLVector3& offset); void setSpaceTimeUSec(const U64 space_time_usec); diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 0c17b5e297..8ef3a3b839 100644 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -880,8 +880,10 @@ void LLWorldMapView::drawFrustum() F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 ); F32 half_width_pixels = half_width_meters * meters_to_pixels; - F32 ctr_x = getLocalRect().getWidth() * 0.5f + sPanX; - F32 ctr_y = getLocalRect().getHeight() * 0.5f + sPanY; + // Compute the frustum coordinates. Take the UI scale into account. + F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); + F32 ctr_x = (getLocalRect().getWidth() * 0.5f + sPanX) * ui_scale_factor; + F32 ctr_y = (getLocalRect().getHeight() * 0.5f + sPanY) * ui_scale_factor; gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp index be8298daab..74ed844376 100644 --- a/indra/newview/llworldmipmap.cpp +++ b/indra/newview/llworldmipmap.cpp @@ -181,8 +181,7 @@ LLPointer<LLViewerFetchedTexture> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 LLPointer<LLViewerFetchedTexture> LLWorldMipmap::loadObjectsTile(U32 grid_x, U32 grid_y, S32 level) { // Get the grid coordinates - std::string imageurl = gSavedSettings.getString("MapServerURL") + llformat("map-%d-%d-%d-objects.jpg", level, grid_x, grid_y); - + std::string imageurl = gSavedSettings.getString("CurrentMapServerURL") + llformat("map-%d-%d-%d-objects.jpg", level, grid_x, grid_y); // DO NOT COMMIT!! DEBUG ONLY!!! // Use a local jpeg for every tile to test map speed without S3 access diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 03e6e65788..15477e0a80 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -130,8 +130,6 @@ static S32 sDelayedVBOEnable = 0; BOOL gAvatarBacklight = FALSE; -BOOL gRenderForSelect = FALSE; - BOOL gDebugPipeline = FALSE; LLPipeline gPipeline; const LLMatrix4* gGLLastMatrix = NULL; @@ -629,14 +627,14 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) //static void LLPipeline::updateRenderDeferred() { - BOOL deferred = (gSavedSettings.getBOOL("RenderDeferred") && - LLRenderTarget::sUseFBO && - LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && - gSavedSettings.getBOOL("VertexShaderEnable") && - gSavedSettings.getBOOL("RenderAvatarVP") && - (gSavedSettings.getBOOL("WindLightUseAtmosShaders")) ? TRUE : FALSE) && - !gUseWireframe; - + BOOL deferred = ((gSavedSettings.getBOOL("RenderDeferred") && + LLRenderTarget::sUseFBO && + LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && + gSavedSettings.getBOOL("VertexShaderEnable") && + gSavedSettings.getBOOL("RenderAvatarVP") && + gSavedSettings.getBOOL("WindLightUseAtmosShaders")) ? TRUE : FALSE) && + !gUseWireframe; + sRenderDeferred = deferred; } @@ -1638,20 +1636,14 @@ void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result, S32 water_cl camera.disableUserClipPlane(); - if (gSky.mVOSkyp.notNull() && gSky.mVOSkyp->mDrawable.notNull()) + if (hasRenderType(LLPipeline::RENDER_TYPE_SKY) && + gSky.mVOSkyp.notNull() && + gSky.mVOSkyp->mDrawable.notNull()) { - // Hack for sky - always visible. - if (hasRenderType(LLPipeline::RENDER_TYPE_SKY)) - { - gSky.mVOSkyp->mDrawable->setVisible(camera); - sCull->pushDrawable(gSky.mVOSkyp->mDrawable); - gSky.updateCull(); - stop_glerror(); - } - } - else - { - llinfos << "No sky drawable!" << llendl; + gSky.mVOSkyp->mDrawable->setVisible(camera); + sCull->pushDrawable(gSky.mVOSkyp->mDrawable); + gSky.updateCull(); + stop_glerror(); } if (hasRenderType(LLPipeline::RENDER_TYPE_GROUND) && @@ -2221,6 +2213,7 @@ void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) LLPipeline::RENDER_TYPE_TERRAIN, LLPipeline::RENDER_TYPE_TREE, LLPipeline::RENDER_TYPE_SKY, + LLPipeline::RENDER_TYPE_VOIDWATER, LLPipeline::RENDER_TYPE_WATER, LLPipeline::END_RENDER_TYPES)) { @@ -3812,185 +3805,6 @@ void LLPipeline::renderDebug() gGL.flush(); } -void LLPipeline::renderForSelect(std::set<LLViewerObject*>& objects, BOOL render_transparent, const LLRect& screen_rect) -{ - assertInitialized(); - - gGL.setColorMask(true, false); - gPipeline.resetDrawOrders(); - - LLViewerCamera* camera = LLViewerCamera::getInstance(); - for (std::set<LLViewerObject*>::iterator iter = objects.begin(); iter != objects.end(); ++iter) - { - stateSort((*iter)->mDrawable, *camera); - } - - LLMemType mt(LLMemType::MTYPE_PIPELINE_RENDER_SELECT); - - - - glMatrixMode(GL_MODELVIEW); - - LLGLSDefault gls_default; - LLGLSObjectSelect gls_object_select; - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLGLDepthTest gls_depth(GL_TRUE,GL_TRUE); - disableLights(); - - LLVertexBuffer::unbind(); - - //for each drawpool - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - U32 last_type = 0; - - // If we don't do this, we crash something on changing graphics settings - // from Medium -> Low, because we unload all the shaders and the - // draw pools aren't aware. I don't know if this has to be a separate - // loop before actual rendering. JC - for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) - { - LLDrawPool *poolp = *iter; - if (poolp->isFacePool() && hasRenderType(poolp->getType())) - { - poolp->prerender(); - } - } - for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) - { - LLDrawPool *poolp = *iter; - if (poolp->isFacePool() && hasRenderType(poolp->getType())) - { - LLFacePool* face_pool = (LLFacePool*) poolp; - face_pool->renderForSelect(); - LLVertexBuffer::unbind(); - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - - if (poolp->getType() != last_type) - { - last_type = poolp->getType(); - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - } - } - } - - LLGLEnable alpha_test(GL_ALPHA_TEST); - if (render_transparent) - { - gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.f); - } - else - { - gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.2f); - } - - gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_VERT_COLOR); - gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_TEX_ALPHA, LLTexUnit::TBS_VERT_ALPHA); - - U32 prim_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_TEXCOORD0; - - for (std::set<LLViewerObject*>::iterator i = objects.begin(); i != objects.end(); ++i) - { - LLViewerObject* vobj = *i; - LLDrawable* drawable = vobj->mDrawable; - if (vobj->isDead() || - vobj->isHUDAttachment() || - (LLSelectMgr::getInstance()->mHideSelectedObjects && vobj->isSelected()) || - drawable->isDead() || - !hasRenderType(drawable->getRenderType())) - { - continue; - } - - for (S32 j = 0; j < drawable->getNumFaces(); ++j) - { - LLFace* facep = drawable->getFace(j); - if (!facep->getPool()) - { - facep->renderForSelect(prim_mask); - } - } - } - - // pick HUD objects - if (isAgentAvatarValid() && sShowHUDAttachments) - { - glh::matrix4f save_proj(glh_get_current_projection()); - glh::matrix4f save_model(glh_get_current_modelview()); - - setup_hud_matrices(screen_rect); - for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); - iter != gAgentAvatarp->mAttachmentPoints.end(); ) - { - LLVOAvatar::attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - if (attachment->getIsHUDAttachment()) - { - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - if (LLViewerObject* attached_object = (*attachment_iter)) - { - LLDrawable* drawable = attached_object->mDrawable; - if (drawable->isDead()) - { - continue; - } - - for (S32 j = 0; j < drawable->getNumFaces(); ++j) - { - LLFace* facep = drawable->getFace(j); - if (!facep->getPool()) - { - facep->renderForSelect(prim_mask); - } - } - - //render child faces - LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) - { - LLViewerObject* child = *iter; - LLDrawable* child_drawable = child->mDrawable; - for (S32 l = 0; l < child_drawable->getNumFaces(); ++l) - { - LLFace* facep = child_drawable->getFace(l); - if (!facep->getPool()) - { - facep->renderForSelect(prim_mask); - } - } - } - } - } - } - } - - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(save_proj.m); - glh_set_current_projection(save_proj); - - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(save_model.m); - glh_set_current_modelview(save_model); - - - } - - gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); - - LLVertexBuffer::unbind(); - - gGL.setColorMask(true, true); -} - void LLPipeline::rebuildPools() { LLMemType mt(LLMemType::MTYPE_PIPELINE_REBUILD_POOLS); @@ -5012,6 +4826,10 @@ void LLPipeline::setLight(LLDrawable *drawablep, BOOL is_light) void LLPipeline::toggleRenderType(U32 type) { gPipeline.mRenderTypeEnabled[type] = !gPipeline.mRenderTypeEnabled[type]; + if (type == LLPipeline::RENDER_TYPE_WATER) + { + gPipeline.mRenderTypeEnabled[LLPipeline::RENDER_TYPE_VOIDWATER] = !gPipeline.mRenderTypeEnabled[LLPipeline::RENDER_TYPE_VOIDWATER]; + } } //static @@ -7339,6 +7157,7 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) gPipeline.pushRenderTypeMask(); clearRenderTypeMask(LLPipeline::RENDER_TYPE_WATER, + LLPipeline::RENDER_TYPE_VOIDWATER, LLPipeline::RENDER_TYPE_GROUND, LLPipeline::RENDER_TYPE_SKY, LLPipeline::RENDER_TYPE_CLOUDS, @@ -7391,6 +7210,7 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) { camera.setFar(camera_in.getFar()); clearRenderTypeMask(LLPipeline::RENDER_TYPE_WATER, + LLPipeline::RENDER_TYPE_VOIDWATER, LLPipeline::RENDER_TYPE_GROUND, END_RENDER_TYPES); stop_glerror(); @@ -7907,6 +7727,7 @@ void LLPipeline::generateGI(LLCamera& camera, LLVector3& lightDir, std::vector<L LLPipeline::RENDER_TYPE_TREE, LLPipeline::RENDER_TYPE_TERRAIN, LLPipeline::RENDER_TYPE_WATER, + LLPipeline::RENDER_TYPE_VOIDWATER, LLPipeline::RENDER_TYPE_PASS_ALPHA_SHADOW, LLPipeline::RENDER_TYPE_AVATAR, LLPipeline::RENDER_TYPE_PASS_SIMPLE, @@ -8090,6 +7911,7 @@ void LLPipeline::generateSunShadow(LLCamera& camera) LLPipeline::RENDER_TYPE_TREE, LLPipeline::RENDER_TYPE_TERRAIN, LLPipeline::RENDER_TYPE_WATER, + LLPipeline::RENDER_TYPE_VOIDWATER, LLPipeline::RENDER_TYPE_PASS_ALPHA_SHADOW, LLPipeline::RENDER_TYPE_PASS_SIMPLE, LLPipeline::RENDER_TYPE_PASS_BUMP, diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index b80765dac6..3f785a99fe 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -245,7 +245,6 @@ public: void renderHighlights(); void renderDebug(); - void renderForSelect(std::set<LLViewerObject*>& objects, BOOL render_transparent, const LLRect& screen_rect); void rebuildPools(); // Rebuild pools void findReferences(LLDrawable *drawablep); // Find the lists which have references to this object @@ -359,6 +358,7 @@ public: RENDER_TYPE_AVATAR = LLDrawPool::POOL_AVATAR, RENDER_TYPE_TREE = LLDrawPool::POOL_TREE, RENDER_TYPE_INVISIBLE = LLDrawPool::POOL_INVISIBLE, + RENDER_TYPE_VOIDWATER = LLDrawPool::POOL_VOIDWATER, RENDER_TYPE_WATER = LLDrawPool::POOL_WATER, RENDER_TYPE_ALPHA = LLDrawPool::POOL_ALPHA, RENDER_TYPE_GLOW = LLDrawPool::POOL_GLOW, @@ -712,7 +712,6 @@ void render_bbox(const LLVector3 &min, const LLVector3 &max); void render_hud_elements(); extern LLPipeline gPipeline; -extern BOOL gRenderForSelect; extern BOOL gDebugPipeline; extern const LLMatrix4* gGLLastMatrix; diff --git a/indra/newview/res/toolbuy.cur b/indra/newview/res/toolbuy.cur Binary files differindex a1bc278116..65bbf01d45 100644 --- a/indra/newview/res/toolbuy.cur +++ b/indra/newview/res/toolbuy.cur diff --git a/indra/newview/res/toolopen.cur b/indra/newview/res/toolopen.cur Binary files differindex a72cdfe4c0..22ecbd5228 100644 --- a/indra/newview/res/toolopen.cur +++ b/indra/newview/res/toolopen.cur diff --git a/indra/newview/res/toolsit.cur b/indra/newview/res/toolsit.cur Binary files differindex 6327bdb281..d26b6f8638 100644 --- a/indra/newview/res/toolsit.cur +++ b/indra/newview/res/toolsit.cur diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index ddd2ff196b..62441fd984 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -139,9 +139,6 @@ name="AvatarListItemIconVoiceLeftColor" reference="AvatarListItemIconOfflineColor" /> <color - name="BackgroundChatColor" - reference="DkGray_66" /> - <color name="ButtonBorderColor" reference="Unused?" /> <color @@ -400,9 +397,6 @@ name="HighlightParentColor" value="0.67 0.83 0.96 1" /> <color - name="IMChatColor" - reference="LtGray" /> - <color name="IMHistoryBgColor" reference="Unused?" /> <color @@ -767,9 +761,6 @@ name="SysWellItemSelected" value="0.3 0.3 0.3 1.0" /> <color - name="ChatToastAgentNameColor" - reference="EmphasisColor" /> - <color name="ColorSwatchBorderColor" value="0.45098 0.517647 0.607843 1"/> <color diff --git a/indra/newview/skins/default/textures/arrow_keys.png b/indra/newview/skins/default/textures/arrow_keys.png Binary files differnew file mode 100644 index 0000000000..f19af59251 --- /dev/null +++ b/indra/newview/skins/default/textures/arrow_keys.png diff --git a/indra/newview/skins/default/textures/icons/Web_Profile_Off.png b/indra/newview/skins/default/textures/icons/Web_Profile_Off.png Binary files differnew file mode 100644 index 0000000000..f5fb774a6f --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Web_Profile_Off.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index b2658d2525..2c00120177 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -371,8 +371,8 @@ with the same filename but different name <texture name="Play_Off" file_name="icons/Play_Off.png" preload="false" /> <texture name="Play_Over" file_name="icons/Play_Over.png" preload="false" /> <texture name="Play_Press" file_name="icons/Play_Press.png" preload="false" /> - - <texture name="ProgressBar" file_name="widgets/ProgressBar.png" preload="true" scale.left="4" scale.top="10" scale.right="48" scale.bottom="2" /> + + <texture name="ProgressBar" file_name="widgets/ProgressBar.png" preload="true" scale.left="4" scale.top="11" scale.right="48" scale.bottom="3" /> <texture name="ProgressTrack" file_name="widgets/ProgressTrack.png" preload="true" scale.left="4" scale.top="13" scale.right="148" scale.bottom="2" /> <texture name="PushButton_Disabled" file_name="widgets/PushButton_Disabled.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" /> @@ -392,7 +392,7 @@ with the same filename but different name <texture name="RadioButton_On_Disabled" file_name="widgets/RadioButton_On_Disabled.png" preload="true" /> - <texture name="Refresh_Off" file_name="icons/Refresh_Off.png" preload="false" /> + <texture name="Refresh_Off" file_name="icons/Refresh_Off.png" preload="true" /> <texture name="Resize_Corner" file_name="windows/Resize_Corner.png" preload="true" /> @@ -468,7 +468,7 @@ with the same filename but different name <texture name="Stepper_Up_Off" file_name="widgets/Stepper_Up_Off.png" preload="false" /> <texture name="Stepper_Up_Press" file_name="widgets/Stepper_Up_Press.png" preload="false" /> - <texture name="Stop_Off" file_name="icons/Stop_Off.png" preload="false" /> + <texture name="Stop_Off" file_name="icons/Stop_Off.png" preload="true" /> <texture name="StopReload_Off" file_name="icons/StopReload_Off.png" preload="false" /> <texture name="StopReload_Over" file_name="icons/StopReload_Over.png" preload="false" /> @@ -553,11 +553,11 @@ with the same filename but different name <texture name="Wearables_Divider" file_name="windows/Wearables_Divider.png" preload="false" /> + <texture name="Web_Profile_Off" file_name="icons/Web_Profile_Off.png" preload="false" /> + <texture name="WellButton_Lit" file_name="bottomtray/WellButton_Lit.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" /> <texture name="WellButton_Lit_Selected" file_name="bottomtray/WellButton_Lit_Selected.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" /> - - <texture name="Window_Background" file_name="windows/Window_Background.png" preload="true" scale.left="4" scale.top="24" scale.right="26" scale.bottom="4" /> <texture name="Window_Foreground" file_name="windows/Window_Foreground.png" preload="true" diff --git a/indra/newview/skins/default/textures/widgets/ProgressBar.png b/indra/newview/skins/default/textures/widgets/ProgressBar.png Binary files differindex edf11ac1f5..3f0e4eba28 100644 --- a/indra/newview/skins/default/textures/widgets/ProgressBar.png +++ b/indra/newview/skins/default/textures/widgets/ProgressBar.png diff --git a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml index 58d58a6ca9..76b188220d 100644 --- a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml @@ -78,7 +78,7 @@ <menu_item_call.on_click function="InspectAvatar.Freeze"/> <menu_item_call.on_visible - function="InspectAvatar.VisibleFreezeEject"/> + function="InspectAvatar.VisibleFreeze"/> </menu_item_call> <menu_item_call label="Eject" @@ -86,7 +86,23 @@ <menu_item_call.on_click function="InspectAvatar.Eject"/> <menu_item_call.on_visible - function="InspectAvatar.VisibleFreezeEject"/> + function="InspectAvatar.VisibleEject"/> + </menu_item_call> + <menu_item_call + label="Kick" + name="kick"> + <menu_item_call.on_click + function="InspectAvatar.Kick"/> + <menu_item_call.on_visible + function="InspectAvatar.EnableGod"/> + </menu_item_call> + <menu_item_call + label="CSR" + name="csr"> + <menu_item_call.on_click + function="InspectAvatar.CSR" /> + <menu_item_call.on_visible + function="InspectAvatar.EnableGod" /> </menu_item_call> <menu_item_call label="Debug Textures" diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index db50b89620..9e321db889 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -40,6 +40,7 @@ #if defined(LL_WINDOWS) #pragma warning(disable: 4355) // using 'this' in base-class ctor initializer expr +#pragma warning(disable: 4702) // disable 'unreachable code' so we can safely use skip(). #endif // Constants @@ -48,6 +49,9 @@ const std::string VIEWERLOGIN_GRIDLABEL("viewerlogin_grid"); const std::string APPVIEWER_SERIALNUMBER("appviewer_serialno"); +const std::string VIEWERLOGIN_CHANNEL("invalid_channel"); +const std::string VIEWERLOGIN_VERSION_CHANNEL("invalid_version"); + // Link seams. //----------------------------------------------------------------------------- @@ -65,6 +69,7 @@ static bool gDisconnectCalled = false; #include "../llviewerwindow.h" void LLViewerWindow::setShowProgress(BOOL show) {} +LLProgressView * LLViewerWindow::getProgressView(void) const { return 0; } LLViewerWindow* gViewerWindow; @@ -160,7 +165,6 @@ std::string LLGridManager::getAppSLURLBase(const std::string& grid_name) //----------------------------------------------------------------------------- #include "../llviewercontrol.h" LLControlGroup gSavedSettings("Global"); -std::string gCurrentVersion = "invalid_version"; LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker<LLControlGroup, std::string>(name){} @@ -177,6 +181,45 @@ BOOL LLControlGroup::declareString(const std::string& name, const std::string &i #include "lluicolortable.h" void LLUIColorTable::saveUserSettings(void)const {} +//----------------------------------------------------------------------------- +#include "../llversioninfo.h" +const std::string &LLVersionInfo::getChannelAndVersion() { return VIEWERLOGIN_VERSION_CHANNEL; } +const std::string &LLVersionInfo::getChannel() { return VIEWERLOGIN_CHANNEL; } + +//----------------------------------------------------------------------------- +#include "../llappviewer.h" +void LLAppViewer::forceQuit(void) {} +LLAppViewer * LLAppViewer::sInstance = 0; + +//----------------------------------------------------------------------------- +#include "llnotificationsutil.h" +LLNotificationPtr LLNotificationsUtil::add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + boost::function<void (const LLSD&, const LLSD&)> functor) { return LLNotificationPtr((LLNotification*)0); } + + +//----------------------------------------------------------------------------- +#include "llupdaterservice.h" + +std::string const & LLUpdaterService::pumpName(void) +{ + static std::string wakka = "wakka wakka wakka"; + return wakka; +} +bool LLUpdaterService::updateReadyToInstall(void) { return false; } +void LLUpdaterService::initialize(const std::string& protocol_version, + const std::string& url, + const std::string& path, + const std::string& channel, + const std::string& version) {} + +void LLUpdaterService::setCheckPeriod(unsigned int seconds) {} +void LLUpdaterService::startChecking(bool install_if_ready) {} +void LLUpdaterService::stopChecking() {} +bool LLUpdaterService::isChecking() { return false; } +LLUpdaterService::eUpdaterState LLUpdaterService::getState() { return INITIAL; } +std::string LLUpdaterService::updatedVersion() { return ""; } //----------------------------------------------------------------------------- #include "llnotifications.h" @@ -192,6 +235,12 @@ LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, return NULL; } +//---------------------------------------------------------------------------- +#include "../llprogressview.h" +void LLProgressView::setText(std::string const &){} +void LLProgressView::setPercent(float){} +void LLProgressView::setMessage(std::string const &){} + //----------------------------------------------------------------------------- // LLNotifications class MockNotifications : public LLNotificationsInterface @@ -290,7 +339,6 @@ namespace tut gSavedSettings.declareBOOL("UseDebugMenus", FALSE, "", FALSE); gSavedSettings.declareBOOL("ForceMandatoryUpdate", FALSE, "", FALSE); gSavedSettings.declareString("ClientSettingsFile", "test_settings.xml", "", FALSE); - gSavedSettings.declareString("VersionChannelName", "test_version_string", "", FALSE); gSavedSettings.declareString("NextLoginLocation", "", "", FALSE); gSavedSettings.declareBOOL("LoginLastLocation", FALSE, "", FALSE); @@ -430,6 +478,8 @@ namespace tut template<> template<> void lllogininstance_object::test<3>() { + skip(); + set_test_name("Test Mandatory Update User Accepts"); // Part 1 - Mandatory Update, with User accepts response. @@ -457,6 +507,8 @@ namespace tut template<> template<> void lllogininstance_object::test<4>() { + skip(); + set_test_name("Test Mandatory Update User Decline"); // Test connect with update needed. diff --git a/indra/newview/tests/llremoteparcelrequest_test.cpp b/indra/newview/tests/llremoteparcelrequest_test.cpp new file mode 100644 index 0000000000..a6c1f69c82 --- /dev/null +++ b/indra/newview/tests/llremoteparcelrequest_test.cpp @@ -0,0 +1,134 @@ +/**
+ * @file llremoteparcelrequest_test.cpp
+ * @author Brad Kittenbrink <brad@lindenlab.com>
+ *
+ * $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 "../test/lltut.h"
+
+#include "../llremoteparcelrequest.h"
+
+#include "../llagent.h"
+#include "message.h"
+
+namespace {
+ LLControlGroup s_saved_settings("dummy_settings");
+ const LLUUID TEST_PARCEL_ID("11111111-1111-1111-1111-111111111111");
+}
+
+LLCurl::Responder::Responder() { }
+LLCurl::Responder::~Responder() { }
+void LLCurl::Responder::error(U32,std::string const &) { }
+void LLCurl::Responder::result(LLSD const &) { }
+void LLCurl::Responder::errorWithContent(U32 status,std::string const &,LLSD const &) { }
+void LLCurl::Responder::completedRaw(U32 status, std::string const &, LLChannelDescriptors const &,boost::shared_ptr<LLBufferArray> const &) { }
+void LLCurl::Responder::completed(U32 status, std::string const &, LLSD const &) { }
+void LLCurl::Responder::completedHeader(U32 status, std::string const &, LLSD const &) { }
+void LLMessageSystem::getF32(char const *,char const *,F32 &,S32) { }
+void LLMessageSystem::getU8(char const *,char const *,U8 &,S32) { }
+void LLMessageSystem::getS32(char const *,char const *,S32 &,S32) { }
+void LLMessageSystem::getString(char const *,char const *, std::string &,S32) { }
+void LLMessageSystem::getUUID(char const *,char const *, LLUUID & out_id,S32)
+{
+ out_id = TEST_PARCEL_ID;
+}
+void LLMessageSystem::nextBlock(char const *) { }
+void LLMessageSystem::addUUID(char const *,LLUUID const &) { }
+void LLMessageSystem::addUUIDFast(char const *,LLUUID const &) { }
+void LLMessageSystem::nextBlockFast(char const *) { }
+void LLMessageSystem::newMessage(char const *) { }
+LLMessageSystem * gMessageSystem;
+char * _PREHASH_AgentID;
+char * _PREHASH_AgentData;
+LLAgent gAgent;
+LLAgent::LLAgent() : mAgentAccess(s_saved_settings) { }
+LLAgent::~LLAgent() { }
+void LLAgent::sendReliableMessage(void) { }
+LLUUID gAgentSessionID;
+LLUUID gAgentID;
+LLUIColor::LLUIColor(void) { }
+LLAgentAccess::LLAgentAccess(LLControlGroup & settings) : mSavedSettings(settings) { }
+LLControlGroup::LLControlGroup(std::string const & name) : LLInstanceTracker<LLControlGroup, std::string>(name) { }
+LLControlGroup::~LLControlGroup(void) { }
+
+namespace tut
+{
+ struct TestObserver : public LLRemoteParcelInfoObserver {
+ TestObserver() : mProcessed(false) { }
+
+ virtual void processParcelInfo(const LLParcelData& parcel_data)
+ {
+ mProcessed = true;
+ }
+
+ virtual void setParcelID(const LLUUID& parcel_id) { }
+
+ virtual void setErrorStatus(U32 status, const std::string& reason) { }
+
+ bool mProcessed;
+ };
+
+ struct RemoteParcelRequestData
+ {
+ RemoteParcelRequestData()
+ {
+ }
+ };
+
+ typedef test_group<RemoteParcelRequestData> remoteparcelrequest_t;
+ typedef remoteparcelrequest_t::object remoteparcelrequest_object_t;
+ tut::remoteparcelrequest_t tut_remoteparcelrequest("LLRemoteParcelRequest");
+
+ template<> template<>
+ void remoteparcelrequest_object_t::test<1>()
+ {
+ set_test_name("observer pointer");
+
+ boost::scoped_ptr<TestObserver> observer(new TestObserver());
+
+ LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance();
+ processor.addObserver(LLUUID(TEST_PARCEL_ID), observer.get());
+
+ processor.processParcelInfoReply(gMessageSystem, NULL);
+
+ ensure(observer->mProcessed);
+ }
+
+ template<> template<>
+ void remoteparcelrequest_object_t::test<2>()
+ {
+ set_test_name("CHOP-220: dangling observer pointer");
+
+ LLRemoteParcelInfoObserver * observer = new TestObserver();
+
+ LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance();
+ processor.addObserver(LLUUID(TEST_PARCEL_ID), observer);
+
+ delete observer;
+ observer = NULL;
+
+ processor.processParcelInfoReply(gMessageSystem, NULL);
+ }
+}
diff --git a/indra/newview/tests/llsimplestat_test.cpp b/indra/newview/tests/llsimplestat_test.cpp new file mode 100644 index 0000000000..60a8cac995 --- /dev/null +++ b/indra/newview/tests/llsimplestat_test.cpp @@ -0,0 +1,586 @@ +/** + * @file llsimplestats_test.cpp + * @date 2010-10-22 + * @brief Test cases for some of llsimplestat.h + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include <tut/tut.hpp> + +#include "lltut.h" +#include "../llsimplestat.h" +#include "llsd.h" +#include "llmath.h" + +// @brief Used as a pointer cast type to get access to LLSimpleStatCounter +class TutStatCounter: public LLSimpleStatCounter +{ +public: + TutStatCounter(); // Not defined + ~TutStatCounter(); // Not defined + void operator=(const TutStatCounter &); // Not defined + + void setRawCount(U32 c) { mCount = c; } + U32 getRawCount() const { return mCount; } +}; + + +namespace tut +{ + struct stat_counter_index + {}; + typedef test_group<stat_counter_index> stat_counter_index_t; + typedef stat_counter_index_t::object stat_counter_index_object_t; + tut::stat_counter_index_t tut_stat_counter_index("stat_counter_test"); + + // Testing LLSimpleStatCounter's external interface + template<> template<> + void stat_counter_index_object_t::test<1>() + { + LLSimpleStatCounter c1; + ensure("Initialized counter is zero", (0 == c1.getCount())); + + ensure("Counter increment return is 1", (1 == ++c1)); + ensure("Counter increment return is 2", (2 == ++c1)); + + ensure("Current counter is 2", (2 == c1.getCount())); + + c1.reset(); + ensure("Counter is 0 after reset", (0 == c1.getCount())); + + ensure("Counter increment return is 1", (1 == ++c1)); + } + + // Testing LLSimpleStatCounter's internal state + template<> template<> + void stat_counter_index_object_t::test<2>() + { + LLSimpleStatCounter c1; + TutStatCounter * tc1 = (TutStatCounter *) &c1; + + ensure("Initialized private counter is zero", (0 == tc1->getRawCount())); + + ++c1; + ++c1; + + ensure("Current private counter is 2", (2 == tc1->getRawCount())); + + c1.reset(); + ensure("Raw counter is 0 after reset", (0 == tc1->getRawCount())); + } + + // Testing LLSimpleStatCounter's wrapping behavior + template<> template<> + void stat_counter_index_object_t::test<3>() + { + LLSimpleStatCounter c1; + TutStatCounter * tc1 = (TutStatCounter *) &c1; + + tc1->setRawCount(U32_MAX); + ensure("Initialized private counter is zero", (U32_MAX == c1.getCount())); + + ensure("Increment of max value wraps to 0", (0 == ++c1)); + } + + // Testing LLSimpleStatMMM's external behavior + template<> template<> + void stat_counter_index_object_t::test<4>() + { + LLSimpleStatMMM<> m1; + typedef LLSimpleStatMMM<>::Value lcl_float; + lcl_float zero(0); + + // Freshly-constructed + ensure("Constructed MMM<> has 0 count", (0 == m1.getCount())); + ensure("Constructed MMM<> has 0 min", (zero == m1.getMin())); + ensure("Constructed MMM<> has 0 max", (zero == m1.getMax())); + ensure("Constructed MMM<> has 0 mean no div-by-zero", (zero == m1.getMean())); + + // Single insert + m1.record(1.0); + ensure("Single insert MMM<> has 1 count", (1 == m1.getCount())); + ensure("Single insert MMM<> has 1.0 min", (1.0 == m1.getMin())); + ensure("Single insert MMM<> has 1.0 max", (1.0 == m1.getMax())); + ensure("Single insert MMM<> has 1.0 mean", (1.0 == m1.getMean())); + + // Second insert + m1.record(3.0); + ensure("2nd insert MMM<> has 2 count", (2 == m1.getCount())); + ensure("2nd insert MMM<> has 1.0 min", (1.0 == m1.getMin())); + ensure("2nd insert MMM<> has 3.0 max", (3.0 == m1.getMax())); + ensure_approximately_equals("2nd insert MMM<> has 2.0 mean", m1.getMean(), lcl_float(2.0), 1); + + // Third insert + m1.record(5.0); + ensure("3rd insert MMM<> has 3 count", (3 == m1.getCount())); + ensure("3rd insert MMM<> has 1.0 min", (1.0 == m1.getMin())); + ensure("3rd insert MMM<> has 5.0 max", (5.0 == m1.getMax())); + ensure_approximately_equals("3rd insert MMM<> has 3.0 mean", m1.getMean(), lcl_float(3.0), 1); + + // Fourth insert + m1.record(1000000.0); + ensure("4th insert MMM<> has 4 count", (4 == m1.getCount())); + ensure("4th insert MMM<> has 1.0 min", (1.0 == m1.getMin())); + ensure("4th insert MMM<> has 100000.0 max", (1000000.0 == m1.getMax())); + ensure_approximately_equals("4th insert MMM<> has 250002.0 mean", m1.getMean(), lcl_float(250002.0), 1); + + // Reset + m1.reset(); + ensure("Reset MMM<> has 0 count", (0 == m1.getCount())); + ensure("Reset MMM<> has 0 min", (zero == m1.getMin())); + ensure("Reset MMM<> has 0 max", (zero == m1.getMax())); + ensure("Reset MMM<> has 0 mean no div-by-zero", (zero == m1.getMean())); + } + + // Testing LLSimpleStatMMM's response to large values + template<> template<> + void stat_counter_index_object_t::test<5>() + { + LLSimpleStatMMM<> m1; + typedef LLSimpleStatMMM<>::Value lcl_float; + lcl_float zero(0); + + // Insert overflowing values + const lcl_float bignum(F32_MAX / 2); + + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(zero); + + ensure("Overflowed MMM<> has 8 count", (8 == m1.getCount())); + ensure("Overflowed MMM<> has 0 min", (zero == m1.getMin())); + ensure("Overflowed MMM<> has huge max", (bignum == m1.getMax())); + ensure("Overflowed MMM<> has fetchable mean", (1.0 == m1.getMean() || true)); + // We should be infinte but not interested in proving the IEEE standard here. + LLSD sd1(m1.getMean()); + // std::cout << "Thingy: " << m1.getMean() << " and as LLSD: " << sd1 << std::endl; + ensure("Overflowed MMM<> produces LLSDable Real", (sd1.isReal())); + } + + // Testing LLSimpleStatMMM<F32>'s external behavior + template<> template<> + void stat_counter_index_object_t::test<6>() + { + LLSimpleStatMMM<F32> m1; + typedef LLSimpleStatMMM<F32>::Value lcl_float; + lcl_float zero(0); + + // Freshly-constructed + ensure("Constructed MMM<F32> has 0 count", (0 == m1.getCount())); + ensure("Constructed MMM<F32> has 0 min", (zero == m1.getMin())); + ensure("Constructed MMM<F32> has 0 max", (zero == m1.getMax())); + ensure("Constructed MMM<F32> has 0 mean no div-by-zero", (zero == m1.getMean())); + + // Single insert + m1.record(1.0); + ensure("Single insert MMM<F32> has 1 count", (1 == m1.getCount())); + ensure("Single insert MMM<F32> has 1.0 min", (1.0 == m1.getMin())); + ensure("Single insert MMM<F32> has 1.0 max", (1.0 == m1.getMax())); + ensure("Single insert MMM<F32> has 1.0 mean", (1.0 == m1.getMean())); + + // Second insert + m1.record(3.0); + ensure("2nd insert MMM<F32> has 2 count", (2 == m1.getCount())); + ensure("2nd insert MMM<F32> has 1.0 min", (1.0 == m1.getMin())); + ensure("2nd insert MMM<F32> has 3.0 max", (3.0 == m1.getMax())); + ensure_approximately_equals("2nd insert MMM<F32> has 2.0 mean", m1.getMean(), lcl_float(2.0), 1); + + // Third insert + m1.record(5.0); + ensure("3rd insert MMM<F32> has 3 count", (3 == m1.getCount())); + ensure("3rd insert MMM<F32> has 1.0 min", (1.0 == m1.getMin())); + ensure("3rd insert MMM<F32> has 5.0 max", (5.0 == m1.getMax())); + ensure_approximately_equals("3rd insert MMM<F32> has 3.0 mean", m1.getMean(), lcl_float(3.0), 1); + + // Fourth insert + m1.record(1000000.0); + ensure("4th insert MMM<F32> has 4 count", (4 == m1.getCount())); + ensure("4th insert MMM<F32> has 1.0 min", (1.0 == m1.getMin())); + ensure("4th insert MMM<F32> has 1000000.0 max", (1000000.0 == m1.getMax())); + ensure_approximately_equals("4th insert MMM<F32> has 250002.0 mean", m1.getMean(), lcl_float(250002.0), 1); + + // Reset + m1.reset(); + ensure("Reset MMM<F32> has 0 count", (0 == m1.getCount())); + ensure("Reset MMM<F32> has 0 min", (zero == m1.getMin())); + ensure("Reset MMM<F32> has 0 max", (zero == m1.getMax())); + ensure("Reset MMM<F32> has 0 mean no div-by-zero", (zero == m1.getMean())); + } + + // Testing LLSimpleStatMMM's response to large values + template<> template<> + void stat_counter_index_object_t::test<7>() + { + LLSimpleStatMMM<F32> m1; + typedef LLSimpleStatMMM<F32>::Value lcl_float; + lcl_float zero(0); + + // Insert overflowing values + const lcl_float bignum(F32_MAX / 2); + + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(zero); + + ensure("Overflowed MMM<F32> has 8 count", (8 == m1.getCount())); + ensure("Overflowed MMM<F32> has 0 min", (zero == m1.getMin())); + ensure("Overflowed MMM<F32> has huge max", (bignum == m1.getMax())); + ensure("Overflowed MMM<F32> has fetchable mean", (1.0 == m1.getMean() || true)); + // We should be infinte but not interested in proving the IEEE standard here. + LLSD sd1(m1.getMean()); + // std::cout << "Thingy: " << m1.getMean() << " and as LLSD: " << sd1 << std::endl; + ensure("Overflowed MMM<F32> produces LLSDable Real", (sd1.isReal())); + } + + // Testing LLSimpleStatMMM<F64>'s external behavior + template<> template<> + void stat_counter_index_object_t::test<8>() + { + LLSimpleStatMMM<F64> m1; + typedef LLSimpleStatMMM<F64>::Value lcl_float; + lcl_float zero(0); + + // Freshly-constructed + ensure("Constructed MMM<F64> has 0 count", (0 == m1.getCount())); + ensure("Constructed MMM<F64> has 0 min", (zero == m1.getMin())); + ensure("Constructed MMM<F64> has 0 max", (zero == m1.getMax())); + ensure("Constructed MMM<F64> has 0 mean no div-by-zero", (zero == m1.getMean())); + + // Single insert + m1.record(1.0); + ensure("Single insert MMM<F64> has 1 count", (1 == m1.getCount())); + ensure("Single insert MMM<F64> has 1.0 min", (1.0 == m1.getMin())); + ensure("Single insert MMM<F64> has 1.0 max", (1.0 == m1.getMax())); + ensure("Single insert MMM<F64> has 1.0 mean", (1.0 == m1.getMean())); + + // Second insert + m1.record(3.0); + ensure("2nd insert MMM<F64> has 2 count", (2 == m1.getCount())); + ensure("2nd insert MMM<F64> has 1.0 min", (1.0 == m1.getMin())); + ensure("2nd insert MMM<F64> has 3.0 max", (3.0 == m1.getMax())); + ensure_approximately_equals("2nd insert MMM<F64> has 2.0 mean", m1.getMean(), lcl_float(2.0), 1); + + // Third insert + m1.record(5.0); + ensure("3rd insert MMM<F64> has 3 count", (3 == m1.getCount())); + ensure("3rd insert MMM<F64> has 1.0 min", (1.0 == m1.getMin())); + ensure("3rd insert MMM<F64> has 5.0 max", (5.0 == m1.getMax())); + ensure_approximately_equals("3rd insert MMM<F64> has 3.0 mean", m1.getMean(), lcl_float(3.0), 1); + + // Fourth insert + m1.record(1000000.0); + ensure("4th insert MMM<F64> has 4 count", (4 == m1.getCount())); + ensure("4th insert MMM<F64> has 1.0 min", (1.0 == m1.getMin())); + ensure("4th insert MMM<F64> has 1000000.0 max", (1000000.0 == m1.getMax())); + ensure_approximately_equals("4th insert MMM<F64> has 250002.0 mean", m1.getMean(), lcl_float(250002.0), 1); + + // Reset + m1.reset(); + ensure("Reset MMM<F64> has 0 count", (0 == m1.getCount())); + ensure("Reset MMM<F64> has 0 min", (zero == m1.getMin())); + ensure("Reset MMM<F64> has 0 max", (zero == m1.getMax())); + ensure("Reset MMM<F64> has 0 mean no div-by-zero", (zero == m1.getMean())); + } + + // Testing LLSimpleStatMMM's response to large values + template<> template<> + void stat_counter_index_object_t::test<9>() + { + LLSimpleStatMMM<F64> m1; + typedef LLSimpleStatMMM<F64>::Value lcl_float; + lcl_float zero(0); + + // Insert overflowing values + const lcl_float bignum(F64_MAX / 2); + + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(zero); + + ensure("Overflowed MMM<F64> has 8 count", (8 == m1.getCount())); + ensure("Overflowed MMM<F64> has 0 min", (zero == m1.getMin())); + ensure("Overflowed MMM<F64> has huge max", (bignum == m1.getMax())); + ensure("Overflowed MMM<F64> has fetchable mean", (1.0 == m1.getMean() || true)); + // We should be infinte but not interested in proving the IEEE standard here. + LLSD sd1(m1.getMean()); + // std::cout << "Thingy: " << m1.getMean() << " and as LLSD: " << sd1 << std::endl; + ensure("Overflowed MMM<F64> produces LLSDable Real", (sd1.isReal())); + } + + // Testing LLSimpleStatMMM<U64>'s external behavior + template<> template<> + void stat_counter_index_object_t::test<10>() + { + LLSimpleStatMMM<U64> m1; + typedef LLSimpleStatMMM<U64>::Value lcl_int; + lcl_int zero(0); + + // Freshly-constructed + ensure("Constructed MMM<U64> has 0 count", (0 == m1.getCount())); + ensure("Constructed MMM<U64> has 0 min", (zero == m1.getMin())); + ensure("Constructed MMM<U64> has 0 max", (zero == m1.getMax())); + ensure("Constructed MMM<U64> has 0 mean no div-by-zero", (zero == m1.getMean())); + + // Single insert + m1.record(1); + ensure("Single insert MMM<U64> has 1 count", (1 == m1.getCount())); + ensure("Single insert MMM<U64> has 1 min", (1 == m1.getMin())); + ensure("Single insert MMM<U64> has 1 max", (1 == m1.getMax())); + ensure("Single insert MMM<U64> has 1 mean", (1 == m1.getMean())); + + // Second insert + m1.record(3); + ensure("2nd insert MMM<U64> has 2 count", (2 == m1.getCount())); + ensure("2nd insert MMM<U64> has 1 min", (1 == m1.getMin())); + ensure("2nd insert MMM<U64> has 3 max", (3 == m1.getMax())); + ensure("2nd insert MMM<U64> has 2 mean", (2 == m1.getMean())); + + // Third insert + m1.record(5); + ensure("3rd insert MMM<U64> has 3 count", (3 == m1.getCount())); + ensure("3rd insert MMM<U64> has 1 min", (1 == m1.getMin())); + ensure("3rd insert MMM<U64> has 5 max", (5 == m1.getMax())); + ensure("3rd insert MMM<U64> has 3 mean", (3 == m1.getMean())); + + // Fourth insert + m1.record(U64L(1000000000000)); + ensure("4th insert MMM<U64> has 4 count", (4 == m1.getCount())); + ensure("4th insert MMM<U64> has 1 min", (1 == m1.getMin())); + ensure("4th insert MMM<U64> has 1000000000000ULL max", (U64L(1000000000000) == m1.getMax())); + ensure("4th insert MMM<U64> has 250000000002ULL mean", (U64L( 250000000002) == m1.getMean())); + + // Reset + m1.reset(); + ensure("Reset MMM<U64> has 0 count", (0 == m1.getCount())); + ensure("Reset MMM<U64> has 0 min", (zero == m1.getMin())); + ensure("Reset MMM<U64> has 0 max", (zero == m1.getMax())); + ensure("Reset MMM<U64> has 0 mean no div-by-zero", (zero == m1.getMean())); + } + + // Testing LLSimpleStatMMM's response to large values + template<> template<> + void stat_counter_index_object_t::test<11>() + { + LLSimpleStatMMM<U64> m1; + typedef LLSimpleStatMMM<U64>::Value lcl_int; + lcl_int zero(0); + + // Insert overflowing values + const lcl_int bignum(U64L(0xffffffffffffffff) / 2); + + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(bignum); + m1.record(zero); + + ensure("Overflowed MMM<U64> has 8 count", (8 == m1.getCount())); + ensure("Overflowed MMM<U64> has 0 min", (zero == m1.getMin())); + ensure("Overflowed MMM<U64> has huge max", (bignum == m1.getMax())); + ensure("Overflowed MMM<U64> has fetchable mean", (zero == m1.getMean() || true)); + } + + // Testing LLSimpleStatCounter's merge() method + template<> template<> + void stat_counter_index_object_t::test<12>() + { + LLSimpleStatCounter c1; + LLSimpleStatCounter c2; + + ++c1; + ++c1; + ++c1; + ++c1; + + ++c2; + ++c2; + c2.merge(c1); + + ensure_equals("4 merged into 2 results in 6", 6, c2.getCount()); + + ensure_equals("Source of merge is undamaged", 4, c1.getCount()); + } + + // Testing LLSimpleStatMMM's merge() method + template<> template<> + void stat_counter_index_object_t::test<13>() + { + LLSimpleStatMMM<> m1; + LLSimpleStatMMM<> m2; + + m1.record(3.5); + m1.record(4.5); + m1.record(5.5); + m1.record(6.5); + + m2.record(5.0); + m2.record(7.0); + m2.record(9.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p1)", 7, m2.getCount()); + ensure_approximately_equals("Min after merge (p1)", F32(3.5), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p1)", F32(9.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p1)", F32(41.000/7.000), m2.getMean(), 22); + + + ensure_equals("Source count of merge is undamaged (p1)", 4, m1.getCount()); + ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(3.5), m1.getMin(), 22); + ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(6.5), m1.getMax(), 22); + ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(5.0), m1.getMean(), 22); + + m2.reset(); + + m2.record(-22.0); + m2.record(-1.0); + m2.record(30.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p2)", 7, m2.getCount()); + ensure_approximately_equals("Min after merge (p2)", F32(-22.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p2)", F32(30.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p2)", F32(27.000/7.000), m2.getMean(), 22); + + } + + // Testing LLSimpleStatMMM's merge() method when src contributes nothing + template<> template<> + void stat_counter_index_object_t::test<14>() + { + LLSimpleStatMMM<> m1; + LLSimpleStatMMM<> m2; + + m2.record(5.0); + m2.record(7.0); + m2.record(9.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p1)", 3, m2.getCount()); + ensure_approximately_equals("Min after merge (p1)", F32(5.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p1)", F32(9.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p1)", F32(7.000), m2.getMean(), 22); + + ensure_equals("Source count of merge is undamaged (p1)", 0, m1.getCount()); + ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(0), m1.getMin(), 22); + ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(0), m1.getMax(), 22); + ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(0), m1.getMean(), 22); + + m2.reset(); + + m2.record(-22.0); + m2.record(-1.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p2)", 2, m2.getCount()); + ensure_approximately_equals("Min after merge (p2)", F32(-22.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p2)", F32(-1.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p2)", F32(-11.5), m2.getMean(), 22); + } + + // Testing LLSimpleStatMMM's merge() method when dst contributes nothing + template<> template<> + void stat_counter_index_object_t::test<15>() + { + LLSimpleStatMMM<> m1; + LLSimpleStatMMM<> m2; + + m1.record(5.0); + m1.record(7.0); + m1.record(9.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p1)", 3, m2.getCount()); + ensure_approximately_equals("Min after merge (p1)", F32(5.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p1)", F32(9.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p1)", F32(7.000), m2.getMean(), 22); + + ensure_equals("Source count of merge is undamaged (p1)", 3, m1.getCount()); + ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(5.0), m1.getMin(), 22); + ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(9.0), m1.getMax(), 22); + ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(7.0), m1.getMean(), 22); + + m1.reset(); + m2.reset(); + + m1.record(-22.0); + m1.record(-1.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p2)", 2, m2.getCount()); + ensure_approximately_equals("Min after merge (p2)", F32(-22.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p2)", F32(-1.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p2)", F32(-11.5), m2.getMean(), 22); + } + + // Testing LLSimpleStatMMM's merge() method when neither dst nor src contributes + template<> template<> + void stat_counter_index_object_t::test<16>() + { + LLSimpleStatMMM<> m1; + LLSimpleStatMMM<> m2; + + m2.merge(m1); + + ensure_equals("Count after merge (p1)", 0, m2.getCount()); + ensure_approximately_equals("Min after merge (p1)", F32(0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p1)", F32(0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p1)", F32(0), m2.getMean(), 22); + + ensure_equals("Source count of merge is undamaged (p1)", 0, m1.getCount()); + ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(0), m1.getMin(), 22); + ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(0), m1.getMax(), 22); + ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(0), m1.getMean(), 22); + } +} diff --git a/indra/newview/tests/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp new file mode 100644 index 0000000000..398d8f16ed --- /dev/null +++ b/indra/newview/tests/llversioninfo_test.cpp @@ -0,0 +1,114 @@ +/** + * @file llversioninfo_test.cpp + * + * $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 "../test/lltut.h" + +#include "../llversioninfo.h" +#include "llversionviewer.h" + +namespace tut +{ + struct versioninfo + { + versioninfo() + : mResetChannel("Reset Channel") + { + std::ostringstream stream; + stream << LL_VERSION_MAJOR << "." + << LL_VERSION_MINOR << "." + << LL_VERSION_PATCH << "." + << LL_VERSION_BUILD; + mVersion = stream.str(); + stream.str(""); + + stream << LL_VERSION_MAJOR << "." + << LL_VERSION_MINOR << "." + << LL_VERSION_PATCH; + mShortVersion = stream.str(); + stream.str(""); + + stream << LL_CHANNEL + << " " + << mVersion; + mVersionAndChannel = stream.str(); + stream.str(""); + + stream << mResetChannel + << " " + << mVersion; + mResetVersionAndChannel = stream.str(); + } + std::string mResetChannel; + std::string mVersion; + std::string mShortVersion; + std::string mVersionAndChannel; + std::string mResetVersionAndChannel; + }; + + typedef test_group<versioninfo> versioninfo_t; + typedef versioninfo_t::object versioninfo_object_t; + tut::versioninfo_t tut_versioninfo("LLVersionInfo"); + + template<> template<> + void versioninfo_object_t::test<1>() + { + ensure_equals("Major version", + LLVersionInfo::getMajor(), + LL_VERSION_MAJOR); + ensure_equals("Minor version", + LLVersionInfo::getMinor(), + LL_VERSION_MINOR); + ensure_equals("Patch version", + LLVersionInfo::getPatch(), + LL_VERSION_PATCH); + ensure_equals("Build version", + LLVersionInfo::getBuild(), + LL_VERSION_BUILD); + ensure_equals("Channel version", + LLVersionInfo::getChannel(), + LL_CHANNEL); + + ensure_equals("Version String", + LLVersionInfo::getVersion(), + mVersion); + ensure_equals("Short Version String", + LLVersionInfo::getShortVersion(), + mShortVersion); + ensure_equals("Version and channel String", + LLVersionInfo::getChannelAndVersion(), + mVersionAndChannel); + + LLVersionInfo::resetChannel(mResetChannel); + ensure_equals("Reset channel version", + LLVersionInfo::getChannel(), + mResetChannel); + + ensure_equals("Reset Version and channel String", + LLVersionInfo::getChannelAndVersion(), + mResetVersionAndChannel); + } +} diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp new file mode 100644 index 0000000000..1bb4fb7c0c --- /dev/null +++ b/indra/newview/tests/llviewerassetstats_test.cpp @@ -0,0 +1,990 @@ +/** + * @file llviewerassetstats_tut.cpp + * @date 2010-10-28 + * @brief Test cases for some of newview/llviewerassetstats.cpp + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include <tut/tut.hpp> +#include <iostream> + +#include "lltut.h" +#include "../llviewerassetstats.h" +#include "lluuid.h" +#include "llsdutil.h" +#include "llregionhandle.h" + +static const char * all_keys[] = +{ + "duration", + "fps", + "get_other", + "get_texture_temp_http", + "get_texture_temp_udp", + "get_texture_non_temp_http", + "get_texture_non_temp_udp", + "get_wearable_udp", + "get_sound_udp", + "get_gesture_udp" +}; + +static const char * resp_keys[] = +{ + "get_other", + "get_texture_temp_http", + "get_texture_temp_udp", + "get_texture_non_temp_http", + "get_texture_non_temp_udp", + "get_wearable_udp", + "get_sound_udp", + "get_gesture_udp" +}; + +static const char * sub_keys[] = +{ + "dequeued", + "enqueued", + "resp_count", + "resp_max", + "resp_min", + "resp_mean" +}; + +static const char * mmm_resp_keys[] = +{ + "fps" +}; + +static const char * mmm_sub_keys[] = +{ + "count", + "max", + "min", + "mean" +}; + +static const LLUUID region1("4e2d81a3-6263-6ffe-ad5c-8ce04bee07e8"); +static const LLUUID region2("68762cc8-b68b-4e45-854b-e830734f2d4a"); +static const U64 region1_handle(0x0000040000003f00ULL); +static const U64 region2_handle(0x0000030000004200ULL); +static const std::string region1_handle_str("0000040000003f00"); +static const std::string region2_handle_str("0000030000004200"); + +#if 0 +static bool +is_empty_map(const LLSD & sd) +{ + return sd.isMap() && 0 == sd.size(); +} + +static bool +is_single_key_map(const LLSD & sd, const std::string & key) +{ + return sd.isMap() && 1 == sd.size() && sd.has(key); +} +#endif + +static bool +is_double_key_map(const LLSD & sd, const std::string & key1, const std::string & key2) +{ + return sd.isMap() && 2 == sd.size() && sd.has(key1) && sd.has(key2); +} + +static bool +is_no_stats_map(const LLSD & sd) +{ + return is_double_key_map(sd, "duration", "regions"); +} + +static bool +is_single_slot_array(const LLSD & sd, U64 region_handle) +{ + U32 grid_x(0), grid_y(0); + grid_from_region_handle(region_handle, &grid_x, &grid_y); + + return (sd.isArray() && + 1 == sd.size() && + sd[0].has("grid_x") && + sd[0].has("grid_y") && + sd[0]["grid_x"].isInteger() && + sd[0]["grid_y"].isInteger() && + grid_x == sd[0]["grid_x"].asInteger() && + grid_y == sd[0]["grid_y"].asInteger()); +} + +static bool +is_double_slot_array(const LLSD & sd, U64 region_handle1, U64 region_handle2) +{ + U32 grid_x1(0), grid_y1(0); + U32 grid_x2(0), grid_y2(0); + grid_from_region_handle(region_handle1, &grid_x1, &grid_y1); + grid_from_region_handle(region_handle2, &grid_x2, &grid_y2); + + return (sd.isArray() && + 2 == sd.size() && + sd[0].has("grid_x") && + sd[0].has("grid_y") && + sd[0]["grid_x"].isInteger() && + sd[0]["grid_y"].isInteger() && + sd[1].has("grid_x") && + sd[1].has("grid_y") && + sd[1]["grid_x"].isInteger() && + sd[1]["grid_y"].isInteger() && + ((grid_x1 == sd[0]["grid_x"].asInteger() && + grid_y1 == sd[0]["grid_y"].asInteger() && + grid_x2 == sd[1]["grid_x"].asInteger() && + grid_y2 == sd[1]["grid_y"].asInteger()) || + (grid_x1 == sd[1]["grid_x"].asInteger() && + grid_y1 == sd[1]["grid_y"].asInteger() && + grid_x2 == sd[0]["grid_x"].asInteger() && + grid_y2 == sd[0]["grid_y"].asInteger()))); +} + +static LLSD +get_region(const LLSD & sd, U64 region_handle1) +{ + U32 grid_x(0), grid_y(0); + grid_from_region_handle(region_handle1, &grid_x, &grid_y); + + for (LLSD::array_const_iterator it(sd["regions"].beginArray()); + sd["regions"].endArray() != it; + ++it) + { + if ((*it).has("grid_x") && + (*it).has("grid_y") && + (*it)["grid_x"].isInteger() && + (*it)["grid_y"].isInteger() && + (*it)["grid_x"].asInteger() == grid_x && + (*it)["grid_y"].asInteger() == grid_y) + { + return *it; + } + } + return LLSD(); +} + +namespace tut +{ + struct tst_viewerassetstats_index + {}; + typedef test_group<tst_viewerassetstats_index> tst_viewerassetstats_index_t; + typedef tst_viewerassetstats_index_t::object tst_viewerassetstats_index_object_t; + tut::tst_viewerassetstats_index_t tut_tst_viewerassetstats_index("tst_viewerassetstats_test"); + + // Testing free functions without global stats allocated + template<> template<> + void tst_viewerassetstats_index_object_t::test<1>() + { + // Check that helpers aren't bothered by missing global stats + ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain)); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); + + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + + LLViewerAssetStatsFF::record_response_main(LLViewerAssetType::AT_GESTURE, false, false, 12300000ULL); + } + + // Create a non-global instance and check the structure + template<> template<> + void tst_viewerassetstats_index_object_t::test<2>() + { + ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain)); + + LLViewerAssetStats * it = new LLViewerAssetStats(); + + ensure("Global gViewerAssetStatsMain should still be NULL", (NULL == gViewerAssetStatsMain)); + + LLSD sd_full = it->asLLSD(false); + + // Default (NULL) region ID doesn't produce LLSD results so should + // get an empty map back from output + ensure("Stat-less LLSD initially", is_no_stats_map(sd_full)); + + // Once the region is set, we will get a response even with no data collection + it->setRegion(region1_handle); + sd_full = it->asLLSD(false); + ensure("Correct single-key LLSD map root", is_double_key_map(sd_full, "duration", "regions")); + ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd_full["regions"], region1_handle)); + + LLSD sd = sd_full["regions"][0]; + + delete it; + + // Check the structure of the LLSD + for (int i = 0; i < LL_ARRAY_SIZE(all_keys); ++i) + { + std::string line = llformat("Has '%s' key", all_keys[i]); + ensure(line, sd.has(all_keys[i])); + } + + for (int i = 0; i < LL_ARRAY_SIZE(resp_keys); ++i) + { + for (int j = 0; j < LL_ARRAY_SIZE(sub_keys); ++j) + { + std::string line = llformat("Key '%s' has '%s' key", resp_keys[i], sub_keys[j]); + ensure(line, sd[resp_keys[i]].has(sub_keys[j])); + } + } + + for (int i = 0; i < LL_ARRAY_SIZE(mmm_resp_keys); ++i) + { + for (int j = 0; j < LL_ARRAY_SIZE(mmm_sub_keys); ++j) + { + std::string line = llformat("Key '%s' has '%s' key", mmm_resp_keys[i], mmm_sub_keys[j]); + ensure(line, sd[mmm_resp_keys[i]].has(mmm_sub_keys[j])); + } + } + } + + // Create a non-global instance and check some content + template<> template<> + void tst_viewerassetstats_index_object_t::test<3>() + { + LLViewerAssetStats * it = new LLViewerAssetStats(); + it->setRegion(region1_handle); + + LLSD sd = it->asLLSD(false); + ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration")); + ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle)); + sd = sd[0]; + + delete it; + + // Check a few points on the tree for content + ensure("sd[get_texture_temp_http][dequeued] is 0", (0 == sd["get_texture_temp_http"]["dequeued"].asInteger())); + ensure("sd[get_sound_udp][resp_min] is 0", (0.0 == sd["get_sound_udp"]["resp_min"].asReal())); + } + + // Create a global instance and verify free functions do something useful + template<> template<> + void tst_viewerassetstats_index_object_t::test<4>() + { + gViewerAssetStatsMain = new LLViewerAssetStats(); + LLViewerAssetStatsFF::set_region_main(region1_handle); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + + LLSD sd = gViewerAssetStatsMain->asLLSD(false); + ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration")); + ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle)); + sd = sd["regions"][0]; + + // Check a few points on the tree for content + ensure("sd[get_texture_non_temp_udp][enqueued] is 1", (1 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger())); + ensure("sd[get_texture_temp_udp][enqueued] is 0", (0 == sd["get_texture_temp_udp"]["enqueued"].asInteger())); + ensure("sd[get_texture_non_temp_http][enqueued] is 0", (0 == sd["get_texture_non_temp_http"]["enqueued"].asInteger())); + ensure("sd[get_texture_temp_http][enqueued] is 0", (0 == sd["get_texture_temp_http"]["enqueued"].asInteger())); + ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); + + // Reset and check zeros... + // Reset leaves current region in place + gViewerAssetStatsMain->reset(); + sd = gViewerAssetStatsMain->asLLSD(false)["regions"][region1_handle_str]; + + delete gViewerAssetStatsMain; + gViewerAssetStatsMain = NULL; + + ensure("sd[get_texture_non_temp_udp][enqueued] is reset", (0 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger())); + ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); + } + + // Create two global instances and verify no interactions + template<> template<> + void tst_viewerassetstats_index_object_t::test<5>() + { + gViewerAssetStatsThread1 = new LLViewerAssetStats(); + gViewerAssetStatsMain = new LLViewerAssetStats(); + LLViewerAssetStatsFF::set_region_main(region1_handle); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + + LLSD sd = gViewerAssetStatsThread1->asLLSD(false); + ensure("Other collector is empty", is_no_stats_map(sd)); + sd = gViewerAssetStatsMain->asLLSD(false); + ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration")); + ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle)); + sd = sd["regions"][0]; + + // Check a few points on the tree for content + ensure("sd[get_texture_non_temp_udp][enqueued] is 1", (1 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger())); + ensure("sd[get_texture_temp_udp][enqueued] is 0", (0 == sd["get_texture_temp_udp"]["enqueued"].asInteger())); + ensure("sd[get_texture_non_temp_http][enqueued] is 0", (0 == sd["get_texture_non_temp_http"]["enqueued"].asInteger())); + ensure("sd[get_texture_temp_http][enqueued] is 0", (0 == sd["get_texture_temp_http"]["enqueued"].asInteger())); + ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); + + // Reset and check zeros... + // Reset leaves current region in place + gViewerAssetStatsMain->reset(); + sd = gViewerAssetStatsMain->asLLSD(false)["regions"][0]; + + delete gViewerAssetStatsMain; + gViewerAssetStatsMain = NULL; + delete gViewerAssetStatsThread1; + gViewerAssetStatsThread1 = NULL; + + ensure("sd[get_texture_non_temp_udp][enqueued] is reset", (0 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger())); + ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); + } + + // Check multiple region collection + template<> template<> + void tst_viewerassetstats_index_object_t::test<6>() + { + gViewerAssetStatsMain = new LLViewerAssetStats(); + + LLViewerAssetStatsFF::set_region_main(region1_handle); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + + LLViewerAssetStatsFF::set_region_main(region2_handle); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + + LLSD sd = gViewerAssetStatsMain->asLLSD(false); + + // std::cout << sd << std::endl; + + ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions")); + ensure("Correct double-slot LLSD array regions", is_double_slot_array(sd["regions"], region1_handle, region2_handle)); + LLSD sd1 = get_region(sd, region1_handle); + LLSD sd2 = get_region(sd, region2_handle); + ensure("Region1 is present in results", sd1.isMap()); + ensure("Region2 is present in results", sd2.isMap()); + + // Check a few points on the tree for content + ensure_equals("sd1[get_texture_non_temp_udp][enqueued] is 1", sd1["get_texture_non_temp_udp"]["enqueued"].asInteger(), 1); + ensure_equals("sd1[get_texture_temp_udp][enqueued] is 0", sd1["get_texture_temp_udp"]["enqueued"].asInteger(), 0); + ensure_equals("sd1[get_texture_non_temp_http][enqueued] is 0", sd1["get_texture_non_temp_http"]["enqueued"].asInteger(), 0); + ensure_equals("sd1[get_texture_temp_http][enqueued] is 0", sd1["get_texture_temp_http"]["enqueued"].asInteger(), 0); + ensure_equals("sd1[get_gesture_udp][dequeued] is 0", sd1["get_gesture_udp"]["dequeued"].asInteger(), 0); + + // Check a few points on the tree for content + ensure("sd2[get_gesture_udp][enqueued] is 4", (4 == sd2["get_gesture_udp"]["enqueued"].asInteger())); + ensure("sd2[get_gesture_udp][dequeued] is 0", (0 == sd2["get_gesture_udp"]["dequeued"].asInteger())); + ensure("sd2[get_texture_non_temp_udp][enqueued] is 0", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger())); + + // Reset and check zeros... + // Reset leaves current region in place + gViewerAssetStatsMain->reset(); + sd = gViewerAssetStatsMain->asLLSD(false); + ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration")); + ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle)); + sd2 = sd["regions"][0]; + + delete gViewerAssetStatsMain; + gViewerAssetStatsMain = NULL; + + ensure("sd2[get_texture_non_temp_udp][enqueued] is reset", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger())); + ensure("sd2[get_gesture_udp][enqueued] is reset", (0 == sd2["get_gesture_udp"]["enqueued"].asInteger())); + } + + // Check multiple region collection jumping back-and-forth between regions + template<> template<> + void tst_viewerassetstats_index_object_t::test<7>() + { + gViewerAssetStatsMain = new LLViewerAssetStats(); + + LLViewerAssetStatsFF::set_region_main(region1_handle); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + + LLViewerAssetStatsFF::set_region_main(region2_handle); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + + LLViewerAssetStatsFF::set_region_main(region1_handle); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, true, true); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, true, true); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + + LLViewerAssetStatsFF::set_region_main(region2_handle); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false); + + LLSD sd = gViewerAssetStatsMain->asLLSD(false); + + ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions")); + ensure("Correct double-slot LLSD array regions", is_double_slot_array(sd["regions"], region1_handle, region2_handle)); + LLSD sd1 = get_region(sd, region1_handle); + LLSD sd2 = get_region(sd, region2_handle); + ensure("Region1 is present in results", sd1.isMap()); + ensure("Region2 is present in results", sd2.isMap()); + + // Check a few points on the tree for content + ensure("sd1[get_texture_non_temp_udp][enqueued] is 1", (1 == sd1["get_texture_non_temp_udp"]["enqueued"].asInteger())); + ensure("sd1[get_texture_temp_udp][enqueued] is 0", (0 == sd1["get_texture_temp_udp"]["enqueued"].asInteger())); + ensure("sd1[get_texture_non_temp_http][enqueued] is 0", (0 == sd1["get_texture_non_temp_http"]["enqueued"].asInteger())); + ensure("sd1[get_texture_temp_http][enqueued] is 1", (1 == sd1["get_texture_temp_http"]["enqueued"].asInteger())); + ensure("sd1[get_gesture_udp][dequeued] is 0", (0 == sd1["get_gesture_udp"]["dequeued"].asInteger())); + + // Check a few points on the tree for content + ensure("sd2[get_gesture_udp][enqueued] is 8", (8 == sd2["get_gesture_udp"]["enqueued"].asInteger())); + ensure("sd2[get_gesture_udp][dequeued] is 0", (0 == sd2["get_gesture_udp"]["dequeued"].asInteger())); + ensure("sd2[get_texture_non_temp_udp][enqueued] is 0", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger())); + + // Reset and check zeros... + // Reset leaves current region in place + gViewerAssetStatsMain->reset(); + sd = gViewerAssetStatsMain->asLLSD(false); + ensure("Correct single-key LLSD map root", is_double_key_map(sd, "duration", "regions")); + ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle)); + sd2 = get_region(sd, region2_handle); + ensure("Region2 is present in results", sd2.isMap()); + + delete gViewerAssetStatsMain; + gViewerAssetStatsMain = NULL; + + ensure_equals("sd2[get_texture_non_temp_udp][enqueued] is reset", sd2["get_texture_non_temp_udp"]["enqueued"].asInteger(), 0); + ensure_equals("sd2[get_gesture_udp][enqueued] is reset", sd2["get_gesture_udp"]["enqueued"].asInteger(), 0); + } + + // Non-texture assets ignore transport and persistence flags + template<> template<> + void tst_viewerassetstats_index_object_t::test<8>() + { + gViewerAssetStatsThread1 = new LLViewerAssetStats(); + gViewerAssetStatsMain = new LLViewerAssetStats(); + LLViewerAssetStatsFF::set_region_main(region1_handle); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, true); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, true); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, true, false); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, true, false); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, true, true); + LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, true, true); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, false, false); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, false, true); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, true, false); + + LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + LLSD sd = gViewerAssetStatsThread1->asLLSD(false); + ensure("Other collector is empty", is_no_stats_map(sd)); + sd = gViewerAssetStatsMain->asLLSD(false); + ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration")); + ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle)); + sd = get_region(sd, region1_handle); + ensure("Region1 is present in results", sd.isMap()); + + // Check a few points on the tree for content + ensure("sd[get_gesture_udp][enqueued] is 0", (0 == sd["get_gesture_udp"]["enqueued"].asInteger())); + ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); + + ensure("sd[get_wearable_udp][enqueued] is 4", (4 == sd["get_wearable_udp"]["enqueued"].asInteger())); + ensure("sd[get_wearable_udp][dequeued] is 4", (4 == sd["get_wearable_udp"]["dequeued"].asInteger())); + + ensure("sd[get_other][enqueued] is 4", (4 == sd["get_other"]["enqueued"].asInteger())); + ensure("sd[get_other][dequeued] is 0", (0 == sd["get_other"]["dequeued"].asInteger())); + + // Reset and check zeros... + // Reset leaves current region in place + gViewerAssetStatsMain->reset(); + sd = get_region(gViewerAssetStatsMain->asLLSD(false), region1_handle); + ensure("Region1 is present in results", sd.isMap()); + + delete gViewerAssetStatsMain; + gViewerAssetStatsMain = NULL; + delete gViewerAssetStatsThread1; + gViewerAssetStatsThread1 = NULL; + + ensure_equals("sd[get_texture_non_temp_udp][enqueued] is reset", sd["get_texture_non_temp_udp"]["enqueued"].asInteger(), 0); + ensure_equals("sd[get_gesture_udp][dequeued] is reset", sd["get_gesture_udp"]["dequeued"].asInteger(), 0); + } + + + // LLViewerAssetStats::merge() basic functions work + template<> template<> + void tst_viewerassetstats_index_object_t::test<9>() + { + LLViewerAssetStats s1; + LLViewerAssetStats s2; + + s1.setRegion(region1_handle); + s2.setRegion(region1_handle); + + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 5000000); + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 6000000); + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 8000000); + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 7000000); + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 9000000); + + s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 2000000); + s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 3000000); + s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 4000000); + + s2.merge(s1); + + LLSD s2_llsd = get_region(s2.asLLSD(false), region1_handle); + ensure("Region1 is present in results", s2_llsd.isMap()); + + ensure_equals("count after merge", s2_llsd["get_texture_temp_http"]["resp_count"].asInteger(), 8); + ensure_approximately_equals("min after merge", s2_llsd["get_texture_temp_http"]["resp_min"].asReal(), 2.0, 22); + ensure_approximately_equals("max after merge", s2_llsd["get_texture_temp_http"]["resp_max"].asReal(), 9.0, 22); + ensure_approximately_equals("max after merge", s2_llsd["get_texture_temp_http"]["resp_mean"].asReal(), 5.5, 22); + } + + // LLViewerAssetStats::merge() basic functions work without corrupting source data + template<> template<> + void tst_viewerassetstats_index_object_t::test<10>() + { + LLViewerAssetStats s1; + LLViewerAssetStats s2; + + s1.setRegion(region1_handle); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900); + + + s2.setRegion(region2_handle); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000); + s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000); + + { + s2.merge(s1); + + LLSD src = s1.asLLSD(false); + LLSD dst = s2.asLLSD(false); + + ensure_equals("merge src has single region", src["regions"].size(), 1); + ensure_equals("merge dst has dual regions", dst["regions"].size(), 2); + + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][0].erase("duration"); + dst.erase("duration"); + dst["regions"][0].erase("duration"); + dst["regions"][1].erase("duration"); + + LLSD s1_llsd = get_region(src, region1_handle); + ensure("Region1 is present in src", s1_llsd.isMap()); + LLSD s2_llsd = get_region(dst, region1_handle); + ensure("Region1 is present in dst", s2_llsd.isMap()); + + ensure("result from src is in dst", llsd_equals(s1_llsd, s2_llsd)); + } + + s1.setRegion(region1_handle); + s2.setRegion(region1_handle); + s1.reset(); + s2.reset(); + + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900); + + + s2.setRegion(region1_handle); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000); + s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000); + + { + s2.merge(s1); + + LLSD src = s1.asLLSD(false); + LLSD dst = s2.asLLSD(false); + + ensure_equals("merge src has single region (p2)", src["regions"].size(), 1); + ensure_equals("merge dst has single region (p2)", dst["regions"].size(), 1); + + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][0].erase("duration"); + dst.erase("duration"); + dst["regions"][0].erase("duration"); + + LLSD s1_llsd = get_region(src, region1_handle); + ensure("Region1 is present in src", s1_llsd.isMap()); + LLSD s2_llsd = get_region(dst, region1_handle); + ensure("Region1 is present in dst", s2_llsd.isMap()); + + ensure_equals("src counts okay (enq)", s1_llsd["get_other"]["enqueued"].asInteger(), 4); + ensure_equals("src counts okay (deq)", s1_llsd["get_other"]["dequeued"].asInteger(), 4); + ensure_equals("src resp counts okay", s1_llsd["get_other"]["resp_count"].asInteger(), 2); + ensure_approximately_equals("src respmin okay", s1_llsd["get_other"]["resp_min"].asReal(), 0.2829, 20); + ensure_approximately_equals("src respmax okay", s1_llsd["get_other"]["resp_max"].asReal(), 23.2892, 20); + + ensure_equals("dst counts okay (enq)", s2_llsd["get_other"]["enqueued"].asInteger(), 12); + ensure_equals("src counts okay (deq)", s2_llsd["get_other"]["dequeued"].asInteger(), 11); + ensure_equals("dst resp counts okay", s2_llsd["get_other"]["resp_count"].asInteger(), 4); + ensure_approximately_equals("dst respmin okay", s2_llsd["get_other"]["resp_min"].asReal(), 0.010, 20); + ensure_approximately_equals("dst respmax okay", s2_llsd["get_other"]["resp_max"].asReal(), 23.2892, 20); + } + } + + + // Maximum merges are interesting when one side contributes nothing + template<> template<> + void tst_viewerassetstats_index_object_t::test<11>() + { + LLViewerAssetStats s1; + LLViewerAssetStats s2; + + s1.setRegion(region1_handle); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + // Want to test negative numbers here but have to work in U64 + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + + s2.setRegion(region1_handle); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + { + s2.merge(s1); + + LLSD src = s1.asLLSD(false); + LLSD dst = s2.asLLSD(false); + + ensure_equals("merge src has single region", src["regions"].size(), 1); + ensure_equals("merge dst has single region", dst["regions"].size(), 1); + + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][0].erase("duration"); + dst.erase("duration"); + dst["regions"][0].erase("duration"); + + LLSD s2_llsd = get_region(dst, region1_handle); + ensure("Region1 is present in dst", s2_llsd.isMap()); + + ensure_equals("dst counts come from src only", s2_llsd["get_other"]["resp_count"].asInteger(), 3); + + ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum", + s2_llsd["get_other"]["resp_max"].asReal(), F64(0.0), 20); + } + + // Other way around + s1.setRegion(region1_handle); + s2.setRegion(region1_handle); + s1.reset(); + s2.reset(); + + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + // Want to test negative numbers here but have to work in U64 + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + { + s1.merge(s2); + + LLSD src = s2.asLLSD(false); + LLSD dst = s1.asLLSD(false); + + ensure_equals("merge src has single region", src["regions"].size(), 1); + ensure_equals("merge dst has single region", dst["regions"].size(), 1); + + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][0].erase("duration"); + dst.erase("duration"); + dst["regions"][0].erase("duration"); + + LLSD s2_llsd = get_region(dst, region1_handle); + ensure("Region1 is present in dst", s2_llsd.isMap()); + + ensure_equals("dst counts come from src only (flipped)", s2_llsd["get_other"]["resp_count"].asInteger(), 3); + + ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum (flipped)", + s2_llsd["get_other"]["resp_max"].asReal(), F64(0.0), 20); + } + } + + // Minimum merges are interesting when one side contributes nothing + template<> template<> + void tst_viewerassetstats_index_object_t::test<12>() + { + LLViewerAssetStats s1; + LLViewerAssetStats s2; + + s1.setRegion(region1_handle); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000); + + s2.setRegion(region1_handle); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + { + s2.merge(s1); + + LLSD src = s1.asLLSD(false); + LLSD dst = s2.asLLSD(false); + + ensure_equals("merge src has single region", src["regions"].size(), 1); + ensure_equals("merge dst has single region", dst["regions"].size(), 1); + + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][0].erase("duration"); + dst.erase("duration"); + dst["regions"][0].erase("duration"); + + LLSD s2_llsd = get_region(dst, region1_handle); + ensure("Region1 is present in dst", s2_llsd.isMap()); + + ensure_equals("dst counts come from src only", s2_llsd["get_other"]["resp_count"].asInteger(), 3); + + ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum", + s2_llsd["get_other"]["resp_min"].asReal(), F64(2.7), 20); + } + + // Other way around + s1.setRegion(region1_handle); + s2.setRegion(region1_handle); + s1.reset(); + s2.reset(); + + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000); + + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + { + s1.merge(s2); + + LLSD src = s2.asLLSD(false); + LLSD dst = s1.asLLSD(false); + + ensure_equals("merge src has single region", src["regions"].size(), 1); + ensure_equals("merge dst has single region", dst["regions"].size(), 1); + + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][0].erase("duration"); + dst.erase("duration"); + dst["regions"][0].erase("duration"); + + LLSD s2_llsd = get_region(dst, region1_handle); + ensure("Region1 is present in dst", s2_llsd.isMap()); + + ensure_equals("dst counts come from src only (flipped)", s2_llsd["get_other"]["resp_count"].asInteger(), 3); + + ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum (flipped)", + s2_llsd["get_other"]["resp_min"].asReal(), F64(2.7), 20); + } + } + +} diff --git a/indra/newview/tests/llviewerhelputil_test.cpp b/indra/newview/tests/llviewerhelputil_test.cpp index a0f1d1c3c3..b425b50c8b 100644 --- a/indra/newview/tests/llviewerhelputil_test.cpp +++ b/indra/newview/tests/llviewerhelputil_test.cpp @@ -72,16 +72,13 @@ static void substitute_string(std::string &input, const std::string &search, con } } -class LLAgent -{ -public: - LLAgent() {} - ~LLAgent() {} -#ifdef __GNUC__ - __attribute__ ((noinline)) -#endif - bool isGodlike() const { return FALSE; } -}; +#include "../llagent.h" +LLAgent::LLAgent() : mAgentAccess(gSavedSettings) { } +LLAgent::~LLAgent() { } +bool LLAgent::isGodlike() const { return FALSE; } +LLAgentAccess::LLAgentAccess(LLControlGroup& settings) : mSavedSettings(settings) { } +LLUIColor::LLUIColor() {} + LLAgent gAgent; std::string LLWeb::expandURLSubstitutions(const std::string &url, diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index b976ac5ea9..acc6e814bc 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -25,13 +25,16 @@ * $/LicenseInfo$ */ -// Precompiled header: almost always required for newview cpp files -#include "../llviewerprecompiledheaders.h" -// Class to test -#include "../llworldmap.h" // Dependencies -#include "../llviewerimagelist.h" +#include "linden_common.h" +#include "llapr.h" +#include "llsingleton.h" +#include "lltrans.h" +#include "lluistring.h" +#include "../llviewertexture.h" #include "../llworldmapmessage.h" +// Class to test +#include "../llworldmap.h" // Tut header #include "../test/lltut.h" @@ -44,34 +47,29 @@ // * A simulator for a class can be implemented here. Please comment and document thoroughly. // Stub image calls -LLViewerImageList::LLViewerImageList() { } -LLViewerImageList::~LLViewerImageList() { } -LLViewerImageList gImageList; -LLViewerImage* LLViewerImageList::getImage(const LLUUID &image_id, - BOOL usemipmaps, - BOOL level_immediate, - LLGLint internal_format, - LLGLenum primary_format, - LLHost request_from_host) -{ return NULL; } -void LLViewerImage::setBoostLevel(S32 level) { } -void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode) { } +void LLViewerTexture::setBoostLevel(S32 ) { } +void LLViewerTexture::setAddressMode(LLTexUnit::eTextureAddressMode ) { } +LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTexture(const LLUUID&, BOOL, LLViewerTexture::EBoostLevel, S8, + LLGLint, LLGLenum, LLHost ) { return NULL; } // Stub related map calls LLWorldMapMessage::LLWorldMapMessage() { } LLWorldMapMessage::~LLWorldMapMessage() { } void LLWorldMapMessage::sendItemRequest(U32 type, U64 handle) { } void LLWorldMapMessage::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent) { } + LLWorldMipmap::LLWorldMipmap() { } LLWorldMipmap::~LLWorldMipmap() { } void LLWorldMipmap::reset() { } void LLWorldMipmap::dropBoostLevels() { } void LLWorldMipmap::equalizeBoostLevels() { } -LLPointer<LLViewerImage> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load) -{ return NULL; } +LLPointer<LLViewerFetchedTexture> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load) { return NULL; } // Stub other stuff -BOOL gPacificDaylightTime; +std::string LLTrans::getString(const std::string &, const LLStringUtil::format_map_t& ) { return std::string("test_trans"); } +void LLUIString::updateResult() const { } +void LLUIString::setArg(const std::string& , const std::string& ) { } +void LLUIString::assign(const std::string& ) { } // End Stubbing // ------------------------------------------------------------------------------------------- @@ -237,7 +235,7 @@ namespace tut // Test 9 : setLandForSaleImage() / getLandForSaleImage() LLUUID id; mSim->setLandForSaleImage(id); - LLPointer<LLViewerImage> image = mSim->getLandForSaleImage(); + LLPointer<LLViewerFetchedTexture> image = mSim->getLandForSaleImage(); ensure("LLSimInfo::getLandForSaleImage() test failed", image.isNull()); // Test 10 : isPG() mSim->setAccess(SIM_ACCESS_PG); @@ -370,7 +368,7 @@ namespace tut } // Test 7 : getObjectsTile() try { - LLPointer<LLViewerImage> image = mWorld->getObjectsTile((U32)(X_WORLD_TEST/REGION_WIDTH_METERS), (U32)(Y_WORLD_TEST/REGION_WIDTH_METERS), 1); + LLPointer<LLViewerFetchedTexture> image = mWorld->getObjectsTile((U32)(X_WORLD_TEST/REGION_WIDTH_METERS), (U32)(Y_WORLD_TEST/REGION_WIDTH_METERS), 1); ensure("LLWorldMap::getObjectsTile() failed", image.isNull()); } catch (...) { fail("LLWorldMap::getObjectsTile() test failed with exception"); diff --git a/indra/newview/tests/llworldmipmap_test.cpp b/indra/newview/tests/llworldmipmap_test.cpp index 54887ae219..4c0959d1a9 100644 --- a/indra/newview/tests/llworldmipmap_test.cpp +++ b/indra/newview/tests/llworldmipmap_test.cpp @@ -25,12 +25,12 @@ * $/LicenseInfo$ */ -// Precompiled header: almost always required for newview cpp files -#include "../llviewerprecompiledheaders.h" +// Dependencies +#include "linden_common.h" +#include "../llviewertexture.h" +#include "../llviewercontrol.h" // Class to test #include "../llworldmipmap.h" -// Dependencies -#include "../llviewerimagelist.h" // Tut header #include "../test/lltut.h" @@ -42,19 +42,14 @@ // * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) // * A simulator for a class can be implemented here. Please comment and document thoroughly. -LLViewerImageList::LLViewerImageList() { } -LLViewerImageList::~LLViewerImageList() { } - -LLViewerImageList gImageList; +void LLViewerTexture::setBoostLevel(S32 ) { } +LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const std::string&, BOOL, LLViewerTexture::EBoostLevel, S8, + LLGLint, LLGLenum, const LLUUID& ) { return NULL; } -LLViewerImage* LLViewerImageList::getImageFromUrl(const std::string& url, - BOOL usemipmaps, - BOOL level_immediate, - LLGLint internal_format, - LLGLenum primary_format, - const LLUUID& force_id) -{ return NULL; } -void LLViewerImage::setBoostLevel(S32 level) { } +LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker<LLControlGroup, std::string>(name) { } +LLControlGroup::~LLControlGroup() { } +std::string LLControlGroup::getString(const std::string& ) { return std::string("test_url"); } +LLControlGroup gSavedSettings("test_settings"); // End Stubbing // ------------------------------------------------------------------------------------------- diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 6861f02bfb..338c62b9fb 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -247,18 +247,14 @@ class WindowsManifest(ViewerManifest): self.disable_manifest_check() + self.path(src="../viewer_components/updater/scripts/windows/update_install.bat", dst="update_install.bat") + # Get shared libs from the shared libs staging directory if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), dst=""): self.enable_crt_manifest_check() - # Get kdu dll, continue if missing. - try: - self.path('llkdu.dll', dst='llkdu.dll') - except RuntimeError: - print "Skipping llkdu.dll" - # Get llcommon and deps. If missing assume static linkage and continue. try: self.path('llcommon.dll') @@ -572,6 +568,8 @@ class DarwinManifest(ViewerManifest): # copy additional libs in <bundle>/Contents/MacOS/ self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib") + self.path("../viewer_components/updater/scripts/darwin/update_install", "MacOS/update_install") + # most everything goes in the Resources directory if self.prefix(src="", dst="Resources"): super(DarwinManifest, self).construct() @@ -621,21 +619,21 @@ class DarwinManifest(ViewerManifest): libdir = "../../libraries/universal-darwin/lib_release" dylibs = {} - # need to get the kdu dll from any of the build directories as well - for lib in "llkdu", "llcommon": - libfile = "lib%s.dylib" % lib - try: - self.path(self.find_existing_file(os.path.join(os.pardir, - lib, - self.args['configuration'], - libfile), - os.path.join(libdir, libfile)), - dst=libfile) - except RuntimeError: - print "Skipping %s" % libfile - dylibs[lib] = False - else: - dylibs[lib] = True + # Need to get the llcommon dll from any of the build directories as well + lib = "llcommon" + libfile = "lib%s.dylib" % lib + try: + self.path(self.find_existing_file(os.path.join(os.pardir, + lib, + self.args['configuration'], + libfile), + os.path.join(libdir, libfile)), + dst=libfile) + except RuntimeError: + print "Skipping %s" % libfile + dylibs[lib] = False + else: + dylibs[lib] = True if dylibs["llcommon"]: for libfile in ("libapr-1.0.3.7.dylib", @@ -707,6 +705,11 @@ class DarwinManifest(ViewerManifest): self.run_command('strip -S %(viewer_binary)r' % { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')}) + def copy_finish(self): + # Force executable permissions to be set for scripts + # see CHOP-223 and http://mercurial.selenic.com/bts/issue1802 + for script in 'Contents/MacOS/update_install',: + self.run_command("chmod +x %r" % os.path.join(self.get_dst_prefix(), script)) def package_finish(self): channel_standin = 'Second Life Viewer 2' # hah, our default channel is not usable on its own @@ -743,6 +746,11 @@ class DarwinManifest(ViewerManifest): devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() + if devfile != '/dev/disk1': + # adding more debugging info based upon nat's hunches to the + # logs to help track down 'SetFile -a V' failures -brad + print "WARNING: 'SetFile -a V' command below is probably gonna fail" + # Copy everything in to the mounted .dmg if self.default_channel() and not self.default_grid(): @@ -842,6 +850,8 @@ class LinuxManifest(ViewerManifest): # recurse self.end_prefix("res-sdl") + self.path("../viewer_components/updater/scripts/linux/update_install", "bin/update_install") + # plugins if self.prefix(src="", dst="bin/llplugin"): self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so") @@ -855,6 +865,12 @@ class LinuxManifest(ViewerManifest): self.path("featuretable_linux.txt") + def copy_finish(self): + # Force executable permissions to be set for scripts + # see CHOP-223 and http://mercurial.selenic.com/bts/issue1802 + for script in 'secondlife', 'bin/update_install': + self.run_command("chmod +x %r" % os.path.join(self.get_dst_prefix(), script)) + def package_finish(self): if 'installer_name' in self.args: installer_name = self.args['installer_name'] @@ -870,7 +886,7 @@ class LinuxManifest(ViewerManifest): if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer(): print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build" - self.run_command("find %(d)r/bin %(d)r/lib -type f | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure + self.run_command("find %(d)r/bin %(d)r/lib -type f \\! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure # Fix access permissions self.run_command(""" @@ -897,6 +913,9 @@ class LinuxManifest(ViewerManifest): 'dir': self.get_build_prefix(), 'inst_name': installer_name, 'inst_path':self.build_path_of(installer_name)}) + else: + print "Skipping %s.tar.bz2 for non-Release build (%s)" % \ + (installer_name, self.args['buildtype']) finally: self.run_command("mv %(inst)s %(dst)s" % { 'dst': self.get_dst_prefix(), @@ -906,15 +925,6 @@ class Linux_i686Manifest(LinuxManifest): def construct(self): super(Linux_i686Manifest, self).construct() - # install either the libllkdu we just built, or a prebuilt one, in - # decreasing order of preference. for linux package, this goes to bin/ - try: - self.path(self.find_existing_file('../llkdu/libllkdu.so', - '../../libraries/i686-linux/lib_release_client/libllkdu.so'), - dst='bin/libllkdu.so') - except: - print "Skipping libllkdu.so - not found" - if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"): self.path("libapr-1.so.0") self.path("libaprutil-1.so.0") @@ -931,12 +941,6 @@ class Linux_i686Manifest(LinuxManifest): self.path("libopenal.so", "libopenal.so.1") self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname try: - self.path("libkdu.so") - pass - except: - print "Skipping libkdu.so - not found" - pass - try: self.path("libfmod-3.75.so") pass except: diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 66c78a86c4..e9eb3c1884 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -27,6 +27,7 @@ include_directories( ${LLXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} ${GOOGLEMOCK_INCLUDE_DIRS} + ${TUT_INCLUDE_DIR} ) set(test_SOURCE_FILES diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp index 873fa23db8..4a2272032b 100644 --- a/indra/test_apps/llplugintest/llmediaplugintest.cpp +++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp @@ -2220,6 +2220,21 @@ void LLMediaPluginTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent e << ", height = " << self->getGeometryHeight() << std::endl; break; + + case MEDIA_EVENT_AUTH_REQUEST: + { + //std::cerr << "Media event: MEDIA_EVENT_AUTH_REQUEST, url " << self->getAuthURL() ", realm " << self->getAuthRealm() << std::endl; + + // TODO: display an auth dialog + self->sendAuthResponse(false, "", ""); + } + break; + + case MEDIA_EVENT_LINK_HOVERED: + { + std::cerr << "Media event: MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << std::endl; + }; + break; } } diff --git a/indra/viewer_components/CMakeLists.txt b/indra/viewer_components/CMakeLists.txt index 0993b64b14..74c9b4568d 100644 --- a/indra/viewer_components/CMakeLists.txt +++ b/indra/viewer_components/CMakeLists.txt @@ -1,4 +1,4 @@ # -*- cmake -*- add_subdirectory(login) - +add_subdirectory(updater) diff --git a/indra/viewer_components/updater/CMakeLists.txt b/indra/viewer_components/updater/CMakeLists.txt new file mode 100644 index 0000000000..0e288bb496 --- /dev/null +++ b/indra/viewer_components/updater/CMakeLists.txt @@ -0,0 +1,82 @@ +# -*- cmake -*- + +project(updater_service) + +include(00-Common) +if(LL_TESTS) + include(LLAddBuildTest) +endif(LL_TESTS) +include(CMakeCopyIfDifferent) +include(CURL) +include(LLCommon) +include(LLMessage) +include(LLPlugin) +include(LLVFS) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLMESSAGE_INCLUDE_DIRS} + ${LLPLUGIN_INCLUDE_DIRS} + ${LLVFS_INCLUDE_DIRS} + ${CURL_INCLUDE_DIRS} + ) + +set(updater_service_SOURCE_FILES + llupdaterservice.cpp + llupdatechecker.cpp + llupdatedownloader.cpp + llupdateinstaller.cpp + ) + +set(updater_service_HEADER_FILES + llupdaterservice.h + llupdatechecker.h + llupdatedownloader.h + llupdateinstaller.h + ) + +set_source_files_properties(${updater_service_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND + updater_service_SOURCE_FILES + ${updater_service_HEADER_FILES} + ) + +add_library(llupdaterservice + ${updater_service_SOURCE_FILES} + ) + +target_link_libraries(llupdaterservice + ${LLCOMMON_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLPLUGIN_LIBRARIES} + ${LLVFS_LIBRARIES} + ) + +if(LL_TESTS) + SET(llupdater_service_TEST_SOURCE_FILES + llupdaterservice.cpp + ) + +# *NOTE:Mani - I was trying to use the preprocessor seam to mock out +# llifstream (and other) llcommon classes. I didn't work +# because of the windows declspec(dllimport)attribute. +#set_source_files_properties( +# llupdaterservice.cpp +# PROPERTIES +# LL_TEST_ADDITIONAL_CFLAGS "-Dllifstream=llus_mock_llifstream" +# ) + + LL_ADD_PROJECT_UNIT_TESTS(llupdaterservice "${llupdater_service_TEST_SOURCE_FILES}") +endif(LL_TESTS) + +set(UPDATER_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/viewer_components/updater + CACHE INTERNAL "" +) + +set(UPDATER_LIBRARIES + llupdaterservice + CACHE INTERNAL "" +) diff --git a/indra/viewer_components/updater/llupdatechecker.cpp b/indra/viewer_components/updater/llupdatechecker.cpp new file mode 100644 index 0000000000..c6aa9b0f11 --- /dev/null +++ b/indra/viewer_components/updater/llupdatechecker.cpp @@ -0,0 +1,194 @@ +/** + * @file llupdaterservice.cpp + * + * $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 <stdexcept> +#include <boost/format.hpp> +#include "llhttpclient.h" +#include "llsd.h" +#include "llupdatechecker.h" +#include "lluri.h" + + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + + +class LLUpdateChecker::CheckError: + public std::runtime_error +{ +public: + CheckError(const char * message): + std::runtime_error(message) + { + ; // No op. + } +}; + + +class LLUpdateChecker::Implementation: + public LLHTTPClient::Responder +{ +public: + Implementation(Client & client); + ~Implementation(); + void check(std::string const & protocolVersion, std::string const & hostUrl, + std::string const & servicePath, std::string channel, std::string version); + + // Responder: + virtual void completed(U32 status, + const std::string & reason, + const LLSD& content); + virtual void error(U32 status, const std::string & reason); + +private: + static const char * sProtocolVersion; + + Client & mClient; + LLHTTPClient mHttpClient; + bool mInProgress; + std::string mVersion; + + std::string buildUrl(std::string const & protocolVersion, std::string const & hostUrl, + std::string const & servicePath, std::string channel, std::string version); + + LOG_CLASS(LLUpdateChecker::Implementation); +}; + + + +// LLUpdateChecker +//----------------------------------------------------------------------------- + + +LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client): + mImplementation(new LLUpdateChecker::Implementation(client)) +{ + ; // No op. +} + + +void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl, + std::string const & servicePath, std::string channel, std::string version) +{ + mImplementation->check(protocolVersion, hostUrl, servicePath, channel, version); +} + + + +// LLUpdateChecker::Implementation +//----------------------------------------------------------------------------- + + +const char * LLUpdateChecker::Implementation::sProtocolVersion = "v1.0"; + + +LLUpdateChecker::Implementation::Implementation(LLUpdateChecker::Client & client): + mClient(client), + mInProgress(false) +{ + ; // No op. +} + + +LLUpdateChecker::Implementation::~Implementation() +{ + ; // No op. +} + + +void LLUpdateChecker::Implementation::check(std::string const & protocolVersion, std::string const & hostUrl, + std::string const & servicePath, std::string channel, std::string version) +{ + llassert(!mInProgress); + + if(protocolVersion != sProtocolVersion) throw CheckError("unsupported protocol"); + + mInProgress = true; + mVersion = version; + std::string checkUrl = buildUrl(protocolVersion, hostUrl, servicePath, channel, version); + LL_INFOS("UpdateCheck") << "checking for updates at " << checkUrl << llendl; + + // The HTTP client will wrap a raw pointer in a boost::intrusive_ptr causing the + // passed object to be silently and automatically deleted. We pass a self- + // referential intrusive pointer to which we add a reference to keep the + // client from deleting the update checker implementation instance. + LLHTTPClient::ResponderPtr temporaryPtr(this); + boost::intrusive_ptr_add_ref(temporaryPtr.get()); + mHttpClient.get(checkUrl, temporaryPtr); +} + +void LLUpdateChecker::Implementation::completed(U32 status, + const std::string & reason, + const LLSD & content) +{ + mInProgress = false; + + if(status != 200) { + LL_WARNS("UpdateCheck") << "html error " << status << " (" << reason << ")" << llendl; + mClient.error(reason); + } else if(!content.asBoolean()) { + LL_INFOS("UpdateCheck") << "up to date" << llendl; + mClient.upToDate(); + } else if(content["required"].asBoolean()) { + LL_INFOS("UpdateCheck") << "version invalid" << llendl; + LLURI uri(content["url"].asString()); + mClient.requiredUpdate(content["version"].asString(), uri, content["hash"].asString()); + } else { + LL_INFOS("UpdateCheck") << "newer version " << content["version"].asString() << " available" << llendl; + LLURI uri(content["url"].asString()); + mClient.optionalUpdate(content["version"].asString(), uri, content["hash"].asString()); + } +} + + +void LLUpdateChecker::Implementation::error(U32 status, const std::string & reason) +{ + mInProgress = false; + LL_WARNS("UpdateCheck") << "update check failed; " << reason << llendl; + mClient.error(reason); +} + + +std::string LLUpdateChecker::Implementation::buildUrl(std::string const & protocolVersion, std::string const & hostUrl, + std::string const & servicePath, std::string channel, std::string version) +{ +#ifdef LL_WINDOWS + static const char * platform = "win"; +#elif LL_DARWIN + static const char * platform = "mac"; +#else + static const char * platform = "lnx"; +#endif + + LLSD path; + path.append(servicePath); + path.append(protocolVersion); + path.append(channel); + path.append(version); + path.append(platform); + return LLURI::buildHTTP(hostUrl, path).asString(); +} diff --git a/indra/viewer_components/updater/llupdatechecker.h b/indra/viewer_components/updater/llupdatechecker.h new file mode 100644 index 0000000000..cea1f13647 --- /dev/null +++ b/indra/viewer_components/updater/llupdatechecker.h @@ -0,0 +1,82 @@ +/** + * @file llupdatechecker.h + * + * $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_UPDATERCHECKER_H +#define LL_UPDATERCHECKER_H + + +#include <boost/shared_ptr.hpp> + + +// +// Implements asynchronous checking for updates. +// +class LLUpdateChecker { +public: + class Client; + class Implementation; + + // An exception that may be raised on check errors. + class CheckError; + + LLUpdateChecker(Client & client); + + // Check status of current app on the given host for the channel and version provided. + void check(std::string const & protocolVersion, std::string const & hostUrl, + std::string const & servicePath, std::string channel, std::string version); + +private: + boost::shared_ptr<Implementation> mImplementation; +}; + + +class LLURI; // From lluri.h + + +// +// The client interface implemented by a requestor checking for an update. +// +class LLUpdateChecker::Client +{ +public: + // An error occurred while checking for an update. + virtual void error(std::string const & message) = 0; + + // A newer version is available, but the current version may still be used. + virtual void optionalUpdate(std::string const & newVersion, + LLURI const & uri, + std::string const & hash) = 0; + + // A newer version is available, and the current version is no longer valid. + virtual void requiredUpdate(std::string const & newVersion, + LLURI const & uri, + std::string const & hash) = 0; + + // The checked version is up to date; no newer version exists. + virtual void upToDate(void) = 0; +}; + + +#endif diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp new file mode 100644 index 0000000000..e88d1bf811 --- /dev/null +++ b/indra/viewer_components/updater/llupdatedownloader.cpp @@ -0,0 +1,517 @@ +/** + * @file llupdatedownloader.cpp + * + * $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 "llupdatedownloader.h" + +#include <stdexcept> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <curl/curl.h> +#include "lldir.h" +#include "llevents.h" +#include "llfile.h" +#include "llmd5.h" +#include "llsd.h" +#include "llsdserialize.h" +#include "llthread.h" +#include "llupdaterservice.h" + + +class LLUpdateDownloader::Implementation: + public LLThread +{ +public: + Implementation(LLUpdateDownloader::Client & client); + ~Implementation(); + void cancel(void); + void download(LLURI const & uri, + std::string const & hash, + std::string const & updateVersion, + bool required); + bool isDownloading(void); + size_t onHeader(void * header, size_t size); + size_t onBody(void * header, size_t size); + int onProgress(double downloadSize, double bytesDownloaded); + void resume(void); + void setBandwidthLimit(U64 bytesPerSecond); + +private: + curl_off_t mBandwidthLimit; + bool mCancelled; + LLUpdateDownloader::Client & mClient; + CURL * mCurl; + LLSD mDownloadData; + llofstream mDownloadStream; + unsigned char mDownloadPercent; + std::string mDownloadRecordPath; + curl_slist * mHeaderList; + + void initializeCurlGet(std::string const & url, bool processHeader); + void resumeDownloading(size_t startByte); + void run(void); + void startDownloading(LLURI const & uri, std::string const & hash); + void throwOnCurlError(CURLcode code); + bool validateDownload(void); + + LOG_CLASS(LLUpdateDownloader::Implementation); +}; + + +namespace { + class DownloadError: + public std::runtime_error + { + public: + DownloadError(const char * message): + std::runtime_error(message) + { + ; // No op. + } + }; + + + const char * gSecondLifeUpdateRecord = "SecondLifeUpdateDownload.xml"; +}; + + + +// LLUpdateDownloader +//----------------------------------------------------------------------------- + + + +std::string LLUpdateDownloader::downloadMarkerPath(void) +{ + return gDirUtilp->getExpandedFilename(LL_PATH_LOGS, gSecondLifeUpdateRecord); +} + + +LLUpdateDownloader::LLUpdateDownloader(Client & client): + mImplementation(new LLUpdateDownloader::Implementation(client)) +{ + ; // No op. +} + + +void LLUpdateDownloader::cancel(void) +{ + mImplementation->cancel(); +} + + +void LLUpdateDownloader::download(LLURI const & uri, + std::string const & hash, + std::string const & updateVersion, + bool required) +{ + mImplementation->download(uri, hash, updateVersion, required); +} + + +bool LLUpdateDownloader::isDownloading(void) +{ + return mImplementation->isDownloading(); +} + + +void LLUpdateDownloader::resume(void) +{ + mImplementation->resume(); +} + + +void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond) +{ + mImplementation->setBandwidthLimit(bytesPerSecond); +} + + + +// LLUpdateDownloader::Implementation +//----------------------------------------------------------------------------- + + +namespace { + size_t write_function(void * data, size_t blockSize, size_t blocks, void * downloader) + { + size_t bytes = blockSize * blocks; + return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onBody(data, bytes); + } + + + size_t header_function(void * data, size_t blockSize, size_t blocks, void * downloader) + { + size_t bytes = blockSize * blocks; + return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onHeader(data, bytes); + } + + + int progress_callback(void * downloader, + double dowloadTotal, + double downloadNow, + double uploadTotal, + double uploadNow) + { + return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)-> + onProgress(dowloadTotal, downloadNow); + } +} + + +LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client & client): + LLThread("LLUpdateDownloader"), + mBandwidthLimit(0), + mCancelled(false), + mClient(client), + mCurl(0), + mDownloadPercent(0), + mHeaderList(0) +{ + CURLcode code = curl_global_init(CURL_GLOBAL_ALL); // Just in case. + llverify(code == CURLE_OK); // TODO: real error handling here. +} + + +LLUpdateDownloader::Implementation::~Implementation() +{ + if(isDownloading()) { + cancel(); + shutdown(); + } else { + ; // No op. + } + if(mCurl) curl_easy_cleanup(mCurl); +} + + +void LLUpdateDownloader::Implementation::cancel(void) +{ + mCancelled = true; +} + + +void LLUpdateDownloader::Implementation::download(LLURI const & uri, + std::string const & hash, + std::string const & updateVersion, + bool required) +{ + if(isDownloading()) mClient.downloadError("download in progress"); + + mDownloadRecordPath = downloadMarkerPath(); + mDownloadData = LLSD(); + mDownloadData["required"] = required; + mDownloadData["update_version"] = updateVersion; + try { + startDownloading(uri, hash); + } catch(DownloadError const & e) { + mClient.downloadError(e.what()); + } +} + + +bool LLUpdateDownloader::Implementation::isDownloading(void) +{ + return !isStopped(); +} + + +void LLUpdateDownloader::Implementation::resume(void) +{ + mCancelled = false; + + if(isDownloading()) { + mClient.downloadError("download in progress"); + } + + mDownloadRecordPath = downloadMarkerPath(); + llifstream dataStream(mDownloadRecordPath); + if(!dataStream) { + mClient.downloadError("no download marker"); + return; + } + + LLSDSerialize::fromXMLDocument(mDownloadData, dataStream); + + if(!mDownloadData.asBoolean()) { + mClient.downloadError("no download information in marker"); + return; + } + + std::string filePath = mDownloadData["path"].asString(); + try { + if(LLFile::isfile(filePath)) { + llstat fileStatus; + LLFile::stat(filePath, &fileStatus); + if(fileStatus.st_size != mDownloadData["size"].asInteger()) { + resumeDownloading(fileStatus.st_size); + } else if(!validateDownload()) { + LLFile::remove(filePath); + download(LLURI(mDownloadData["url"].asString()), + mDownloadData["hash"].asString(), + mDownloadData["update_version"].asString(), + mDownloadData["required"].asBoolean()); + } else { + mClient.downloadComplete(mDownloadData); + } + } else { + download(LLURI(mDownloadData["url"].asString()), + mDownloadData["hash"].asString(), + mDownloadData["update_version"].asString(), + mDownloadData["required"].asBoolean()); + } + } catch(DownloadError & e) { + mClient.downloadError(e.what()); + } +} + + +void LLUpdateDownloader::Implementation::setBandwidthLimit(U64 bytesPerSecond) +{ + if((mBandwidthLimit != bytesPerSecond) && isDownloading() && !mDownloadData["required"].asBoolean()) { + llassert(mCurl != 0); + mBandwidthLimit = bytesPerSecond; + CURLcode code = curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, &mBandwidthLimit); + if(code != CURLE_OK) LL_WARNS("UpdateDownload") << + "unable to change dowload bandwidth" << LL_ENDL; + } else { + mBandwidthLimit = bytesPerSecond; + } +} + + +size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size) +{ + char const * headerPtr = reinterpret_cast<const char *> (buffer); + std::string header(headerPtr, headerPtr + size); + size_t colonPosition = header.find(':'); + if(colonPosition == std::string::npos) return size; // HTML response; ignore. + + if(header.substr(0, colonPosition) == "Content-Length") { + try { + size_t firstDigitPos = header.find_first_of("0123456789", colonPosition); + size_t lastDigitPos = header.find_last_of("0123456789"); + std::string contentLength = header.substr(firstDigitPos, lastDigitPos - firstDigitPos + 1); + size_t size = boost::lexical_cast<size_t>(contentLength); + LL_INFOS("UpdateDownload") << "download size is " << size << LL_ENDL; + + mDownloadData["size"] = LLSD(LLSD::Integer(size)); + llofstream odataStream(mDownloadRecordPath); + LLSDSerialize::toPrettyXML(mDownloadData, odataStream); + } catch (std::exception const & e) { + LL_WARNS("UpdateDownload") << "unable to read content length (" + << e.what() << ")" << LL_ENDL; + } + } else { + ; // No op. + } + + return size; +} + + +size_t LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size) +{ + if(mCancelled) return 0; // Forces a write error which will halt curl thread. + if((size == 0) || (buffer == 0)) return 0; + + mDownloadStream.write(reinterpret_cast<const char *>(buffer), size); + if(mDownloadStream.bad()) { + return 0; + } else { + return size; + } +} + + +int LLUpdateDownloader::Implementation::onProgress(double downloadSize, double bytesDownloaded) +{ + int downloadPercent = static_cast<int>(100. * (bytesDownloaded / downloadSize)); + if(downloadPercent > mDownloadPercent) { + mDownloadPercent = downloadPercent; + + LLSD event; + event["pump"] = LLUpdaterService::pumpName(); + LLSD payload; + payload["type"] = LLSD(LLUpdaterService::PROGRESS); + payload["download_size"] = downloadSize; + payload["bytes_downloaded"] = bytesDownloaded; + event["payload"] = payload; + LLEventPumps::instance().obtain("mainlooprepeater").post(event); + + LL_INFOS("UpdateDownload") << "progress event " << payload << LL_ENDL; + } else { + ; // Keep events to a reasonalbe number. + } + + return 0; +} + + +void LLUpdateDownloader::Implementation::run(void) +{ + CURLcode code = curl_easy_perform(mCurl); + mDownloadStream.close(); + if(code == CURLE_OK) { + LLFile::remove(mDownloadRecordPath); + if(validateDownload()) { + LL_INFOS("UpdateDownload") << "download successful" << LL_ENDL; + mClient.downloadComplete(mDownloadData); + } else { + LL_INFOS("UpdateDownload") << "download failed hash check" << LL_ENDL; + std::string filePath = mDownloadData["path"].asString(); + if(filePath.size() != 0) LLFile::remove(filePath); + mClient.downloadError("failed hash check"); + } + } else if(mCancelled && (code == CURLE_WRITE_ERROR)) { + LL_INFOS("UpdateDownload") << "download canceled by user" << LL_ENDL; + // Do not call back client. + } else { + LL_WARNS("UpdateDownload") << "download failed with error '" << + curl_easy_strerror(code) << "'" << LL_ENDL; + LLFile::remove(mDownloadRecordPath); + if(mDownloadData.has("path")) LLFile::remove(mDownloadData["path"].asString()); + mClient.downloadError("curl error"); + } + + if(mHeaderList) { + curl_slist_free_all(mHeaderList); + mHeaderList = 0; + } +} + + +void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & url, bool processHeader) +{ + if(mCurl == 0) { + mCurl = curl_easy_init(); + } else { + curl_easy_reset(mCurl); + } + + if(mCurl == 0) throw DownloadError("failed to initialize curl"); + + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, true)); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_FOLLOWLOCATION, true)); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &write_function)); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this)); + if(processHeader) { + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, &header_function)); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, this)); + } + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPGET, true)); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str())); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSFUNCTION, &progress_callback)); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSDATA, this)); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOPROGRESS, false)); + // if it's a required update set the bandwidth limit to 0 (unlimited) + curl_off_t limit = mDownloadData["required"].asBoolean() ? 0 : mBandwidthLimit; + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, limit)); + + mDownloadPercent = 0; +} + + +void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte) +{ + LL_INFOS("UpdateDownload") << "resuming download from " << mDownloadData["url"].asString() + << " at byte " << startByte << LL_ENDL; + + initializeCurlGet(mDownloadData["url"].asString(), false); + + // The header 'Range: bytes n-' will request the bytes remaining in the + // source begining with byte n and ending with the last byte. + boost::format rangeHeaderFormat("Range: bytes=%u-"); + rangeHeaderFormat % startByte; + mHeaderList = curl_slist_append(mHeaderList, rangeHeaderFormat.str().c_str()); + if(mHeaderList == 0) throw DownloadError("cannot add Range header"); + throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaderList)); + + mDownloadStream.open(mDownloadData["path"].asString(), + std::ios_base::out | std::ios_base::binary | std::ios_base::app); + start(); +} + + +void LLUpdateDownloader::Implementation::startDownloading(LLURI const & uri, std::string const & hash) +{ + mDownloadData["url"] = uri.asString(); + mDownloadData["hash"] = hash; + mDownloadData["current_version"] = ll_get_version(); + LLSD path = uri.pathArray(); + if(path.size() == 0) throw DownloadError("no file path"); + std::string fileName = path[path.size() - 1].asString(); + std::string filePath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, fileName); + mDownloadData["path"] = filePath; + + LL_INFOS("UpdateDownload") << "downloading " << filePath + << " from " << uri.asString() << LL_ENDL; + LL_INFOS("UpdateDownload") << "hash of file is " << hash << LL_ENDL; + + llofstream dataStream(mDownloadRecordPath); + LLSDSerialize::toPrettyXML(mDownloadData, dataStream); + + mDownloadStream.open(filePath, std::ios_base::out | std::ios_base::binary); + initializeCurlGet(uri.asString(), true); + start(); +} + + +void LLUpdateDownloader::Implementation::throwOnCurlError(CURLcode code) +{ + if(code != CURLE_OK) { + const char * errorString = curl_easy_strerror(code); + if(errorString != 0) { + throw DownloadError(curl_easy_strerror(code)); + } else { + throw DownloadError("unknown curl error"); + } + } else { + ; // No op. + } +} + + +bool LLUpdateDownloader::Implementation::validateDownload(void) +{ + std::string filePath = mDownloadData["path"].asString(); + llifstream fileStream(filePath, std::ios_base::in | std::ios_base::binary); + if(!fileStream) return false; + + std::string hash = mDownloadData["hash"].asString(); + if(hash.size() != 0) { + LL_INFOS("UpdateDownload") << "checking hash..." << LL_ENDL; + char digest[33]; + LLMD5(fileStream).hex_digest(digest); + if(hash != digest) { + LL_WARNS("UpdateDownload") << "download hash mismatch; expeted " << hash << + " but download is " << digest << LL_ENDL; + } + return hash == digest; + } else { + return true; // No hash check provided. + } +} diff --git a/indra/viewer_components/updater/llupdatedownloader.h b/indra/viewer_components/updater/llupdatedownloader.h new file mode 100644 index 0000000000..0d635640cf --- /dev/null +++ b/indra/viewer_components/updater/llupdatedownloader.h @@ -0,0 +1,94 @@ +/** + * @file llupdatedownloader.h + * + * $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_UPDATE_DOWNLOADER_H +#define LL_UPDATE_DOWNLOADER_H + + +#include <string> +#include <boost/shared_ptr.hpp> +#include "lluri.h" + + +// +// An asynchronous download service for fetching updates. +// +class LLUpdateDownloader +{ +public: + class Client; + class Implementation; + + // Returns the path to the download marker file containing details of the + // latest download. + static std::string downloadMarkerPath(void); + + LLUpdateDownloader(Client & client); + + // Cancel any in progress download; a no op if none is in progress. The + // client will not receive a complete or error callback. + void cancel(void); + + // Start a new download. + void download(LLURI const & uri, + std::string const & hash, + std::string const & updateVersion, + bool required=false); + + // Returns true if a download is in progress. + bool isDownloading(void); + + // Resume a partial download. + void resume(void); + + // Set a limit on the dowload rate. + void setBandwidthLimit(U64 bytesPerSecond); + +private: + boost::shared_ptr<Implementation> mImplementation; +}; + + +// +// An interface to be implemented by clients initiating a update download. +// +class LLUpdateDownloader::Client { +public: + + // The download has completed successfully. + // data is a map containing the following items: + // url - source (remote) location + // hash - the md5 sum that should match the installer file. + // path - destination (local) location + // required - boolean indicating if this is a required update. + // size - the size of the installer in bytes + virtual void downloadComplete(LLSD const & data) = 0; + + // The download failed. + virtual void downloadError(std::string const & message) = 0; +}; + + +#endif diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp new file mode 100644 index 0000000000..d450c068ad --- /dev/null +++ b/indra/viewer_components/updater/llupdateinstaller.cpp @@ -0,0 +1,100 @@ +/** + * @file llupdateinstaller.cpp + * + * $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 <apr_file_io.h> +#include "llapr.h" +#include "llprocesslauncher.h" +#include "llupdateinstaller.h" +#include "lldir.h" + + +#if defined(LL_WINDOWS) +#pragma warning(disable: 4702) // disable 'unreachable code' so we can use lexical_cast (really!). +#endif +#include <boost/lexical_cast.hpp> + + +namespace { + class RelocateError {}; + + + std::string copy_to_temp(std::string const & path) + { + std::string scriptFile = gDirUtilp->getBaseFileName(path); + std::string newPath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, scriptFile); + apr_status_t status = apr_file_copy(path.c_str(), newPath.c_str(), APR_FILE_SOURCE_PERMS, gAPRPoolp); + if(status != APR_SUCCESS) throw RelocateError(); + + return newPath; + } +} + + +int ll_install_update(std::string const & script, + std::string const & updatePath, + bool required, + LLInstallScriptMode mode) +{ + std::string actualScriptPath; + switch(mode) { + case LL_COPY_INSTALL_SCRIPT_TO_TEMP: + try { + actualScriptPath = copy_to_temp(script); + } + catch (RelocateError &) { + return -1; + } + break; + case LL_RUN_INSTALL_SCRIPT_IN_PLACE: + actualScriptPath = script; + break; + default: + llassert(!"unpossible copy mode"); + } + + llinfos << "UpdateInstaller: installing " << updatePath << " using " << + actualScriptPath << LL_ENDL; + + LLProcessLauncher launcher; + launcher.setExecutable(actualScriptPath); + launcher.addArgument(updatePath); + launcher.addArgument(ll_install_failed_marker_path().c_str()); + launcher.addArgument(boost::lexical_cast<std::string>(required)); + int result = launcher.launch(); + launcher.orphan(); + + return result; +} + + +std::string const & ll_install_failed_marker_path(void) +{ + static std::string path; + if(path.empty()) { + path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeInstallFailed.marker"); + } + return path; +} diff --git a/indra/viewer_components/updater/llupdateinstaller.h b/indra/viewer_components/updater/llupdateinstaller.h new file mode 100644 index 0000000000..fe5b1d19b5 --- /dev/null +++ b/indra/viewer_components/updater/llupdateinstaller.h @@ -0,0 +1,58 @@ +/** + * @file llupdateinstaller.h + * + * $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_UPDATE_INSTALLER_H +#define LL_UPDATE_INSTALLER_H + + +#include <string> + + +enum LLInstallScriptMode { + LL_RUN_INSTALL_SCRIPT_IN_PLACE, + LL_COPY_INSTALL_SCRIPT_TO_TEMP +}; + +// +// Launch the installation script. +// +// The updater will overwrite the current installation, so it is highly recommended +// that the current application terminate once this function is called. +// +int ll_install_update( + std::string const & script, // Script to execute. + std::string const & updatePath, // Path to update file. + bool required, // Is the update required. + LLInstallScriptMode mode=LL_COPY_INSTALL_SCRIPT_TO_TEMP); // Run in place or copy to temp? + + +// +// Returns the path which points to the failed install marker file, should it +// exist. +// +std::string const & ll_install_failed_marker_path(void); + + +#endif diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp new file mode 100644 index 0000000000..ea242f45cd --- /dev/null +++ b/indra/viewer_components/updater/llupdaterservice.cpp @@ -0,0 +1,623 @@ +/** + * @file llupdaterservice.cpp + * + * $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 "llupdaterservice.h" + +#include "llupdatedownloader.h" +#include "llevents.h" +#include "lltimer.h" +#include "llupdatechecker.h" +#include "llupdateinstaller.h" +#include "llversionviewer.h" + +#include <boost/scoped_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include "lldir.h" +#include "llsdserialize.h" +#include "llfile.h" + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + + +namespace +{ + boost::weak_ptr<LLUpdaterServiceImpl> gUpdater; + + const std::string UPDATE_MARKER_FILENAME("SecondLifeUpdateReady.xml"); + std::string update_marker_path() + { + return gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + UPDATE_MARKER_FILENAME); + } + + std::string install_script_path(void) + { +#ifdef LL_WINDOWS + std::string scriptFile = "update_install.bat"; +#else + std::string scriptFile = "update_install"; +#endif + return gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, scriptFile); + } + + LLInstallScriptMode install_script_mode(void) + { +#ifdef LL_WINDOWS + return LL_COPY_INSTALL_SCRIPT_TO_TEMP; +#else + return LL_RUN_INSTALL_SCRIPT_IN_PLACE; +#endif + }; + +} + +class LLUpdaterServiceImpl : + public LLUpdateChecker::Client, + public LLUpdateDownloader::Client +{ + static const std::string sListenerName; + + std::string mProtocolVersion; + std::string mUrl; + std::string mPath; + std::string mChannel; + std::string mVersion; + + unsigned int mCheckPeriod; + bool mIsChecking; + bool mIsDownloading; + + LLUpdateChecker mUpdateChecker; + LLUpdateDownloader mUpdateDownloader; + LLTimer mTimer; + + LLUpdaterService::app_exit_callback_t mAppExitCallback; + + LLUpdaterService::eUpdaterState mState; + + LOG_CLASS(LLUpdaterServiceImpl); + +public: + LLUpdaterServiceImpl(); + virtual ~LLUpdaterServiceImpl(); + + void initialize(const std::string& protocol_version, + const std::string& url, + const std::string& path, + const std::string& channel, + const std::string& version); + + void setCheckPeriod(unsigned int seconds); + void setBandwidthLimit(U64 bytesPerSecond); + + void startChecking(bool install_if_ready); + void stopChecking(); + bool isChecking(); + LLUpdaterService::eUpdaterState getState(); + + void setAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { mAppExitCallback = aecb;} + std::string updatedVersion(void); + + bool checkForInstall(bool launchInstaller); // Test if a local install is ready. + bool checkForResume(); // Test for resumeable d/l. + + // LLUpdateChecker::Client: + virtual void error(std::string const & message); + virtual void optionalUpdate(std::string const & newVersion, + LLURI const & uri, + std::string const & hash); + virtual void requiredUpdate(std::string const & newVersion, + LLURI const & uri, + std::string const & hash); + virtual void upToDate(void); + + // LLUpdateDownloader::Client + void downloadComplete(LLSD const & data); + void downloadError(std::string const & message); + + bool onMainLoop(LLSD const & event); + +private: + std::string mNewVersion; + + void restartTimer(unsigned int seconds); + void setState(LLUpdaterService::eUpdaterState state); + void stopTimer(); +}; + +const std::string LLUpdaterServiceImpl::sListenerName = "LLUpdaterServiceImpl"; + +LLUpdaterServiceImpl::LLUpdaterServiceImpl() : + mIsChecking(false), + mIsDownloading(false), + mCheckPeriod(0), + mUpdateChecker(*this), + mUpdateDownloader(*this), + mState(LLUpdaterService::INITIAL) +{ +} + +LLUpdaterServiceImpl::~LLUpdaterServiceImpl() +{ + LL_INFOS("UpdaterService") << "shutting down updater service" << LL_ENDL; + LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName); +} + +void LLUpdaterServiceImpl::initialize(const std::string& protocol_version, + const std::string& url, + const std::string& path, + const std::string& channel, + const std::string& version) +{ + if(mIsChecking || mIsDownloading) + { + throw LLUpdaterService::UsageError("LLUpdaterService::initialize call " + "while updater is running."); + } + + mProtocolVersion = protocol_version; + mUrl = url; + mPath = path; + mChannel = channel; + mVersion = version; +} + +void LLUpdaterServiceImpl::setCheckPeriod(unsigned int seconds) +{ + mCheckPeriod = seconds; +} + +void LLUpdaterServiceImpl::setBandwidthLimit(U64 bytesPerSecond) +{ + mUpdateDownloader.setBandwidthLimit(bytesPerSecond); +} + +void LLUpdaterServiceImpl::startChecking(bool install_if_ready) +{ + if(mUrl.empty() || mChannel.empty() || mVersion.empty()) + { + throw LLUpdaterService::UsageError("Set params before call to " + "LLUpdaterService::startCheck()."); + } + + mIsChecking = true; + + // Check to see if an install is ready. + bool has_install = checkForInstall(install_if_ready); + if(!has_install) + { + checkForResume(); // will set mIsDownloading to true if resuming + + if(!mIsDownloading) + { + setState(LLUpdaterService::CHECKING_FOR_UPDATE); + + // Checking can only occur during the mainloop. + // reset the timer to 0 so that the next mainloop event + // triggers a check; + restartTimer(0); + } + else + { + setState(LLUpdaterService::DOWNLOADING); + } + } +} + +void LLUpdaterServiceImpl::stopChecking() +{ + if(mIsChecking) + { + mIsChecking = false; + stopTimer(); + } + + if(mIsDownloading) + { + mUpdateDownloader.cancel(); + mIsDownloading = false; + } + + setState(LLUpdaterService::TERMINAL); +} + +bool LLUpdaterServiceImpl::isChecking() +{ + return mIsChecking; +} + +LLUpdaterService::eUpdaterState LLUpdaterServiceImpl::getState() +{ + return mState; +} + +std::string LLUpdaterServiceImpl::updatedVersion(void) +{ + return mNewVersion; +} + +bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller) +{ + bool foundInstall = false; // return true if install is found. + + llifstream update_marker(update_marker_path(), + std::ios::in | std::ios::binary); + + if(update_marker.is_open()) + { + // Found an update info - now lets see if its valid. + LLSD update_info; + LLSDSerialize::fromXMLDocument(update_info, update_marker); + update_marker.close(); + + // Get the path to the installer file. + LLSD path = update_info.get("path"); + if(update_info["current_version"].asString() != ll_get_version()) + { + // This viewer is not the same version as the one that downloaded + // the update. Do not install this update. + if(!path.asString().empty()) + { + llinfos << "ignoring update dowloaded by different client version" << llendl; + LLFile::remove(path.asString()); + LLFile::remove(update_marker_path()); + } + else + { + ; // Nothing to clean up. + } + + foundInstall = false; + } + else if(path.isDefined() && !path.asString().empty()) + { + if(launchInstaller) + { + setState(LLUpdaterService::INSTALLING); + + LLFile::remove(update_marker_path()); + + int result = ll_install_update(install_script_path(), + update_info["path"].asString(), + update_info["required"].asBoolean(), + install_script_mode()); + + if((result == 0) && mAppExitCallback) + { + mAppExitCallback(); + } else if(result != 0) { + llwarns << "failed to run update install script" << LL_ENDL; + } else { + ; // No op. + } + } + + foundInstall = true; + } + } + return foundInstall; +} + +bool LLUpdaterServiceImpl::checkForResume() +{ + bool result = false; + std::string download_marker_path = mUpdateDownloader.downloadMarkerPath(); + if(LLFile::isfile(download_marker_path)) + { + llifstream download_marker_stream(download_marker_path, + std::ios::in | std::ios::binary); + if(download_marker_stream.is_open()) + { + LLSD download_info; + LLSDSerialize::fromXMLDocument(download_info, download_marker_stream); + download_marker_stream.close(); + if(download_info["current_version"].asString() == ll_get_version()) + { + mIsDownloading = true; + mNewVersion = download_info["update_version"].asString(); + mUpdateDownloader.resume(); + result = true; + } + else + { + // The viewer that started this download is not the same as this viewer; ignore. + llinfos << "ignoring partial download from different viewer version" << llendl; + std::string path = download_info["path"].asString(); + if(!path.empty()) LLFile::remove(path); + LLFile::remove(download_marker_path); + } + } + } + return result; +} + +void LLUpdaterServiceImpl::error(std::string const & message) +{ + if(mIsChecking) + { + setState(LLUpdaterService::TEMPORARY_ERROR); + restartTimer(mCheckPeriod); + } +} + +void LLUpdaterServiceImpl::optionalUpdate(std::string const & newVersion, + LLURI const & uri, + std::string const & hash) +{ + stopTimer(); + mNewVersion = newVersion; + mIsDownloading = true; + mUpdateDownloader.download(uri, hash, newVersion, false); + + setState(LLUpdaterService::DOWNLOADING); +} + +void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion, + LLURI const & uri, + std::string const & hash) +{ + stopTimer(); + mNewVersion = newVersion; + mIsDownloading = true; + mUpdateDownloader.download(uri, hash, newVersion, true); + + setState(LLUpdaterService::DOWNLOADING); +} + +void LLUpdaterServiceImpl::upToDate(void) +{ + if(mIsChecking) + { + restartTimer(mCheckPeriod); + } + + setState(LLUpdaterService::UP_TO_DATE); +} + +void LLUpdaterServiceImpl::downloadComplete(LLSD const & data) +{ + mIsDownloading = false; + + // Save out the download data to the SecondLifeUpdateReady + // marker file. + llofstream update_marker(update_marker_path()); + LLSDSerialize::toPrettyXML(data, update_marker); + + LLSD event; + event["pump"] = LLUpdaterService::pumpName(); + LLSD payload; + payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_COMPLETE); + payload["required"] = data["required"]; + payload["version"] = mNewVersion; + event["payload"] = payload; + LLEventPumps::instance().obtain("mainlooprepeater").post(event); + + setState(LLUpdaterService::TERMINAL); +} + +void LLUpdaterServiceImpl::downloadError(std::string const & message) +{ + LL_INFOS("UpdaterService") << "Error downloading: " << message << LL_ENDL; + + mIsDownloading = false; + + // Restart the timer on error + if(mIsChecking) + { + restartTimer(mCheckPeriod); + } + + LLSD event; + event["pump"] = LLUpdaterService::pumpName(); + LLSD payload; + payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_ERROR); + payload["message"] = message; + event["payload"] = payload; + LLEventPumps::instance().obtain("mainlooprepeater").post(event); + + setState(LLUpdaterService::FAILURE); +} + +void LLUpdaterServiceImpl::restartTimer(unsigned int seconds) +{ + LL_INFOS("UpdaterService") << "will check for update again in " << + seconds << " seconds" << LL_ENDL; + mTimer.start(); + mTimer.setTimerExpirySec(seconds); + LLEventPumps::instance().obtain("mainloop").listen( + sListenerName, boost::bind(&LLUpdaterServiceImpl::onMainLoop, this, _1)); +} + +void LLUpdaterServiceImpl::setState(LLUpdaterService::eUpdaterState state) +{ + if(state != mState) + { + mState = state; + + LLSD event; + event["pump"] = LLUpdaterService::pumpName(); + LLSD payload; + payload["type"] = LLSD(LLUpdaterService::STATE_CHANGE); + payload["state"] = state; + event["payload"] = payload; + LLEventPumps::instance().obtain("mainlooprepeater").post(event); + + LL_INFOS("UpdaterService") << "setting state to " << state << LL_ENDL; + } + else + { + ; // State unchanged; noop. + } +} + +void LLUpdaterServiceImpl::stopTimer() +{ + mTimer.stop(); + LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName); +} + +bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event) +{ + if(mTimer.getStarted() && mTimer.hasExpired()) + { + stopTimer(); + + // Check for failed install. + if(LLFile::isfile(ll_install_failed_marker_path())) + { + int requiredValue = 0; + { + llifstream stream(ll_install_failed_marker_path()); + stream >> requiredValue; + if(stream.fail()) requiredValue = 0; + } + // TODO: notify the user. + llinfos << "found marker " << ll_install_failed_marker_path() << llendl; + llinfos << "last install attempt failed" << llendl; + LLFile::remove(ll_install_failed_marker_path()); + + LLSD event; + event["type"] = LLSD(LLUpdaterService::INSTALL_ERROR); + event["required"] = LLSD(requiredValue); + LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).post(event); + + setState(LLUpdaterService::TERMINAL); + } + else + { + mUpdateChecker.check(mProtocolVersion, mUrl, mPath, mChannel, mVersion); + setState(LLUpdaterService::CHECKING_FOR_UPDATE); + } + } + else + { + // Keep on waiting... + } + + return false; +} + + +//----------------------------------------------------------------------- +// Facade interface + +std::string const & LLUpdaterService::pumpName(void) +{ + static std::string name("updater_service"); + return name; +} + +bool LLUpdaterService::updateReadyToInstall(void) +{ + return LLFile::isfile(update_marker_path()); +} + +LLUpdaterService::LLUpdaterService() +{ + if(gUpdater.expired()) + { + mImpl = + boost::shared_ptr<LLUpdaterServiceImpl>(new LLUpdaterServiceImpl()); + gUpdater = mImpl; + } + else + { + mImpl = gUpdater.lock(); + } +} + +LLUpdaterService::~LLUpdaterService() +{ +} + +void LLUpdaterService::initialize(const std::string& protocol_version, + const std::string& url, + const std::string& path, + const std::string& channel, + const std::string& version) +{ + mImpl->initialize(protocol_version, url, path, channel, version); +} + +void LLUpdaterService::setCheckPeriod(unsigned int seconds) +{ + mImpl->setCheckPeriod(seconds); +} + +void LLUpdaterService::setBandwidthLimit(U64 bytesPerSecond) +{ + mImpl->setBandwidthLimit(bytesPerSecond); +} + +void LLUpdaterService::startChecking(bool install_if_ready) +{ + mImpl->startChecking(install_if_ready); +} + +void LLUpdaterService::stopChecking() +{ + mImpl->stopChecking(); +} + +bool LLUpdaterService::isChecking() +{ + return mImpl->isChecking(); +} + +LLUpdaterService::eUpdaterState LLUpdaterService::getState() +{ + return mImpl->getState(); +} + +void LLUpdaterService::setImplAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) +{ + return mImpl->setAppExitCallback(aecb); +} + +std::string LLUpdaterService::updatedVersion(void) +{ + return mImpl->updatedVersion(); +} + + +std::string const & ll_get_version(void) { + static std::string version(""); + + if (version.empty()) { + std::ostringstream stream; + stream << LL_VERSION_MAJOR << "." + << LL_VERSION_MINOR << "." + << LL_VERSION_PATCH << "." + << LL_VERSION_BUILD; + version = stream.str(); + } + + return version; +} + diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h new file mode 100644 index 0000000000..450f19c1c6 --- /dev/null +++ b/indra/viewer_components/updater/llupdaterservice.h @@ -0,0 +1,108 @@ +/** + * @file llupdaterservice.h + * + * $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_UPDATERSERVICE_H +#define LL_UPDATERSERVICE_H + +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> + +class LLUpdaterServiceImpl; + +class LLUpdaterService +{ +public: + class UsageError: public std::runtime_error + { + public: + UsageError(const std::string& msg) : std::runtime_error(msg) {} + }; + + // Name of the event pump through which update events will be delivered. + static std::string const & pumpName(void); + + // Returns true if an update has been completely downloaded and is now ready to install. + static bool updateReadyToInstall(void); + + // Type codes for events posted by this service. Stored the event's 'type' element. + enum eUpdaterEvent { + INVALID, + DOWNLOAD_COMPLETE, + DOWNLOAD_ERROR, + INSTALL_ERROR, + PROGRESS, + STATE_CHANGE + }; + + enum eUpdaterState { + INITIAL, + CHECKING_FOR_UPDATE, + TEMPORARY_ERROR, + DOWNLOADING, + INSTALLING, + UP_TO_DATE, + TERMINAL, + FAILURE + }; + + LLUpdaterService(); + ~LLUpdaterService(); + + void initialize(const std::string& protocol_version, + const std::string& url, + const std::string& path, + const std::string& channel, + const std::string& version); + + void setCheckPeriod(unsigned int seconds); + void setBandwidthLimit(U64 bytesPerSecond); + + void startChecking(bool install_if_ready = false); + void stopChecking(); + bool isChecking(); + eUpdaterState getState(); + + typedef boost::function<void (void)> app_exit_callback_t; + template <typename F> + void setAppExitCallback(F const &callable) + { + app_exit_callback_t aecb = callable; + setImplAppExitCallback(aecb); + } + + // If an update is or has been downloaded, this method will return the + // version string for that update. An empty string will be returned + // otherwise. + std::string updatedVersion(void); + +private: + boost::shared_ptr<LLUpdaterServiceImpl> mImpl; + void setImplAppExitCallback(app_exit_callback_t aecb); +}; + +// Returns the full version as a string. +std::string const & ll_get_version(void); + +#endif // LL_UPDATERSERVICE_H diff --git a/indra/viewer_components/updater/scripts/darwin/update_install b/indra/viewer_components/updater/scripts/darwin/update_install new file mode 100644 index 0000000000..6a95f96d86 --- /dev/null +++ b/indra/viewer_components/updater/scripts/darwin/update_install @@ -0,0 +1,10 @@ +#! /bin/bash + +# +# The first argument contains the path to the installer app. The second a path +# to a marker file which should be created if the installer fails.q +# + +cd "$(dirname "$0")" +(../Resources/mac-updater.app/Contents/MacOS/mac-updater -dmg "$1" -name "Second Life Viewer 2"; if [ $? -ne 0 ]; then echo $3 >> "$2"; fi;) & +exit 0 diff --git a/indra/viewer_components/updater/scripts/linux/update_install b/indra/viewer_components/updater/scripts/linux/update_install new file mode 100644 index 0000000000..88451340ec --- /dev/null +++ b/indra/viewer_components/updater/scripts/linux/update_install @@ -0,0 +1,10 @@ +#! /bin/bash +INSTALL_DIR=$(cd "$(dirname "$0")/.." ; pwd) +export LD_LIBRARY_PATH="$INSTALL_DIR/lib" +bin/linux-updater.bin --file "$1" --dest "$INSTALL_DIR" --name "Second Life Viewer 2" --stringsdir "$INSTALL_DIR/skins/default/xui/en" --stringsfile "strings.xml" + +if [ $? -ne 0 ] + then echo $3 >> "$2" +fi + +rm -f "$1" diff --git a/indra/viewer_components/updater/scripts/windows/update_install.bat b/indra/viewer_components/updater/scripts/windows/update_install.bat new file mode 100644 index 0000000000..96687226a8 --- /dev/null +++ b/indra/viewer_components/updater/scripts/windows/update_install.bat @@ -0,0 +1,3 @@ +start /WAIT %1 /SKIP_DIALOGS
+IF ERRORLEVEL 1 ECHO %3 > %2
+DEL %1
diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp new file mode 100644 index 0000000000..5f8cd28f29 --- /dev/null +++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp @@ -0,0 +1,200 @@ +/**
+ * @file llupdaterservice_test.cpp
+ * @brief Tests of llupdaterservice.cpp.
+ *
+ * $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$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "../llupdaterservice.h"
+#include "../llupdatechecker.h"
+#include "../llupdatedownloader.h"
+#include "../llupdateinstaller.h"
+
+#include "../../../test/lltut.h"
+//#define DEBUG_ON
+#include "../../../test/debug.h"
+
+#include "llevents.h"
+#include "lldir.h"
+
+/*****************************************************************************
+* MOCK'd
+*****************************************************************************/
+LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client)
+{}
+void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl,
+ std::string const & servicePath, std::string channel, std::string version)
+{}
+LLUpdateDownloader::LLUpdateDownloader(Client & ) {}
+void LLUpdateDownloader::download(LLURI const & , std::string const &, std::string const &, bool){}
+
+class LLDir_Mock : public LLDir
+{
+ void initAppDirs(const std::string &app_name,
+ const std::string& app_read_only_data_dir = "") {}
+ U32 countFilesInDir(const std::string &dirname, const std::string &mask)
+ {
+ return 0;
+ }
+
+ BOOL getNextFileInDir(const std::string &dirname,
+ const std::string &mask,
+ std::string &fname)
+ {
+ return false;
+ }
+ void getRandomFileInDir(const std::string &dirname,
+ const std::string &mask,
+ std::string &fname) {}
+ std::string getCurPath() { return ""; }
+ BOOL fileExists(const std::string &filename) const { return false; }
+ std::string getLLPluginLauncher() { return ""; }
+ std::string getLLPluginFilename(std::string base_name) { return ""; }
+
+} gDirUtil;
+LLDir* gDirUtilp = &gDirUtil;
+LLDir::LLDir() {}
+LLDir::~LLDir() {}
+S32 LLDir::deleteFilesInDir(const std::string &dirname,
+ const std::string &mask)
+{ return 0; }
+
+void LLDir::setChatLogsDir(const std::string &path){}
+void LLDir::setPerAccountChatLogsDir(const std::string &username){}
+void LLDir::setLindenUserDir(const std::string &username){}
+void LLDir::setSkinFolder(const std::string &skin_folder){}
+bool LLDir::setCacheDir(const std::string &path){ return true; }
+void LLDir::dumpCurrentDirectories() {}
+
+std::string LLDir::getExpandedFilename(ELLPath location,
+ const std::string &filename) const
+{
+ return "";
+}
+
+std::string LLUpdateDownloader::downloadMarkerPath(void)
+{
+ return "";
+}
+
+void LLUpdateDownloader::resume(void) {}
+void LLUpdateDownloader::cancel(void) {}
+void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond) {}
+
+int ll_install_update(std::string const &, std::string const &, bool, LLInstallScriptMode)
+{
+ return 0;
+}
+
+std::string const & ll_install_failed_marker_path()
+{
+ static std::string wubba;
+ return wubba;
+}
+
+/*
+#pragma warning(disable: 4273)
+llus_mock_llifstream::llus_mock_llifstream(const std::string& _Filename,
+ ios_base::openmode _Mode,
+ int _Prot) :
+ std::basic_istream<char,std::char_traits< char > >(NULL,true)
+{}
+
+llus_mock_llifstream::~llus_mock_llifstream() {}
+bool llus_mock_llifstream::is_open() const {return true;}
+void llus_mock_llifstream::close() {}
+*/
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llupdaterservice_data
+ {
+ llupdaterservice_data() :
+ pumps(LLEventPumps::instance()),
+ test_url("dummy_url"),
+ test_channel("dummy_channel"),
+ test_version("dummy_version")
+ {}
+ LLEventPumps& pumps;
+ std::string test_url;
+ std::string test_channel;
+ std::string test_version;
+ };
+
+ typedef test_group<llupdaterservice_data> llupdaterservice_group;
+ typedef llupdaterservice_group::object llupdaterservice_object;
+ llupdaterservice_group llupdaterservicegrp("LLUpdaterService");
+
+ template<> template<>
+ void llupdaterservice_object::test<1>()
+ {
+ DEBUG;
+ LLUpdaterService updater;
+ bool got_usage_error = false;
+ try
+ {
+ updater.startChecking();
+ }
+ catch(LLUpdaterService::UsageError)
+ {
+ got_usage_error = true;
+ }
+ ensure("Caught start before params", got_usage_error);
+ }
+
+ template<> template<>
+ void llupdaterservice_object::test<2>()
+ {
+ DEBUG;
+ LLUpdaterService updater;
+ bool got_usage_error = false;
+ try
+ {
+ updater.initialize("1.0",test_url, "update" ,test_channel, test_version);
+ updater.startChecking();
+ updater.initialize("1.0", "other_url", "update", test_channel, test_version);
+ }
+ catch(LLUpdaterService::UsageError)
+ {
+ got_usage_error = true;
+ }
+ ensure("Caught params while running", got_usage_error);
+ }
+
+ template<> template<>
+ void llupdaterservice_object::test<3>()
+ {
+ DEBUG;
+ LLUpdaterService updater;
+ updater.initialize("1.0", test_url, "update", test_channel, test_version);
+ updater.startChecking();
+ ensure(updater.isChecking());
+ updater.stopChecking();
+ ensure(!updater.isChecking());
+ }
+}
|