Automated merge with file:///Users/Bill/Projects/viewer-release

master
Bill Curtis 2010-11-19 14:11:42 -08:00
commit 066d8c39ca
1036 changed files with 23682 additions and 21397 deletions

View File

@ -29,4 +29,10 @@ c6e6324f5be1401f077ad18a4a0f6b46451c2f7b last_sprint
9822eb3e25f7fe0c28ffd8aba45c507caa383cbc 2.2.0-beta2
b0cd7e150009809a0b5b0a9d5785cd4bb230413a 2.2.0-beta3
00a831292231faad7e44c69f76cb96f175b8dfad 2.2.0-beta4
98e0d6df638429fd2f0476667504bd5a6b298def 2.3.0-beta1
1415e6538d54fd5d568ee88343424d57c6803c2c 2.2.0-release
98e0d6df638429fd2f0476667504bd5a6b298def 2.3.0-start
a3c12342b1af0951b8aa3b828aacef17fcea8178 2.3.0-beta1
db0fe9bb65187f365e58a717dd23d0f4754a9c1d 2.3.0-beta2
6ad3d6fa35a4e320e9ce442fce2bf9c7fc852556 2.3.0-beta3
6ad3d6fa35a4e320e9ce442fce2bf9c7fc852556 2.3.0-release

View File

@ -51,6 +51,7 @@ viewer-release.viewer_channel = "Second Life Release"
viewer-release.login_channel = "Second Life Release"
viewer-release.build_debug_release_separately = true
viewer-release.build_viewer_update_version_manager = true
viewer-release.release-viewer.jira = DRTVWR-13
# ========================================
# aimee
@ -86,6 +87,7 @@ brad-parabuild.build_server_tests = false
cg_viewer-development_lenny.collect_metrics = true
cg_viewer-development_lenny.show_changes_since = 4b140ce7839d
cg_viewer-development_lenny.email = cg@lindenlab.com
# ========================================
# gooey
@ -101,6 +103,26 @@ gooey.login_channel = "Second Life Alpha"
gooey.viewer_grid = agni
gooey.build_viewer_update_version_manager = false
# ========================================
# Display Names project
# ========================================
#viewer-identity-evolution.email = leyla@lindenlab.com
viewer-identity.build_Debug = false
viewer-identity.build_RelWithDebInfo = false
viewer-identity.build_viewer = true
viewer-identity.build_server = false
viewer-identity.build_server_tests = false
viewer-identity.build_Linux = true
viewer-identity.build_hg_bundle = true
viewer-identity.bulld_docs = true
viewer-identity.viewer_channel = "Second Life Project Viewer"
viewer-identity.login_channel = "Second Life Project Viewer"
viewer-identity.viewer_grid = aditi
viewer-identity.build_viewer_update_version_manager = false
# ========================================
# palange
# ========================================

View File

@ -59,6 +59,7 @@ pre_build()
-t $variant \
-G "$cmake_generator" \
configure \
-DGRID:STRING="$viewer_grid" \
-DVIEWER_CHANNEL:STRING="$viewer_channel" \
-DVIEWER_LOGIN_CHANNEL:STRING="$login_channel" \
-DINSTALL_PROPRIETARY:BOOL=ON \
@ -114,11 +115,15 @@ then
if [ -x "$top/../buildscripts/hg/bin/build.sh" ]
then
exec "$top/../buildscripts/hg/bin/build.sh" "$top"
elif [ -r "$top/README" ]
then
cat "$top/README"
exit 1
else
cat <<EOF
This script, if called in a development environment, requires that the branch
independent build script repository be checked out next to this repository.
This repository is located at http://hg.lindenlab.com/parabuild/buildscripts
This repository is located at http://hg.secondlife.com/buildscripts
EOF
exit 1
fi
@ -223,7 +228,7 @@ do
fi
else
begin_section "Build$variant"
build "$variant" "$build_dir" > "$build_log" 2>&1
build "$variant" "$build_dir" >> "$build_log" 2>&1
begin_section Tests
grep --line-buffered "^##teamcity" "$build_log"
end_section Tests
@ -284,6 +289,7 @@ then
succeeded=$build_coverity
else
upload_item installer "$package" binary/octet-stream
upload_item quicklink "$package" binary/octet-stream
# Upload crash reporter files.
case "$last_built_variant" in

View File

@ -20,6 +20,7 @@ Aimee Trescothick
SNOW-227
SNOW-570
SNOW-572
SNOW-575
VWR-3321
VWR-3336
VWR-3903
@ -33,6 +34,7 @@ Aimee Trescothick
VWR-6550
VWR-6583
VWR-6482
VWR-6918
VWR-7109
VWR-7383
VWR-7800
@ -59,16 +61,26 @@ Aimee Trescothick
Alejandro Rosenthal
VWR-1184
Aleric Inglewood
SNOW-522
SNOW-626
SNOW-756
SNOW-764
VWR-10001
VWR-10759
VWR-10837
VWR-12691
VWR-12984
VWR-13996
VWR-14426
SNOW-84
SNOW-477
SNOW-766
STORM-163
Ales Beaumont
VWR-9352
SNOW-240
Alexandrea Fride
STORM-255
Alissa Sabre
VWR-81
VWR-83
@ -117,6 +129,7 @@ Alissa Sabre
VWR-12617
VWR-12620
VWR-12789
SNOW-322
Angus Boyd
VWR-592
Ann Congrejo
@ -133,6 +146,7 @@ Asuka Neely
Balp Allen
VWR-4157
Be Holder
SNOW-322
SNOW-397
Benja Kepler
VWR-746
@ -163,8 +177,11 @@ Boroondas Gupte
SNOW-610
SNOW-624
SNOW-737
STORM-318
VWR-233
VWR-20583
VWR-20891
VWR-23455
WEB-262
Bulli Schumann
CT-218
@ -194,6 +211,8 @@ Catherine Pfeffer
Celierra Darling
VWR-1274
VWR-6975
Cypren Christenson
STORM-417
Dale Glass
VWR-120
VWR-560
@ -336,6 +355,7 @@ Joghert LeSabre
VWR-64
Jonathan Yap
VWR-17801
STORM-616
Kage Pixel
VWR-11
Ken March
@ -373,6 +393,8 @@ Malwina Dollinger
CT-138
march Korda
SVC-1020
Marine Kelley
STORM-281
Matthew Dowd
VWR-1344
VWR-1651
@ -381,6 +403,7 @@ Matthew Dowd
VWR-1761
VWR-2681
McCabe Maxsted
SNOW-387
VWR-1318
VWR-4065
VWR-4826
@ -393,6 +416,7 @@ McCabe Maxsted
VWR-8689
VWR-9007
Michelle2 Zenovka
STORM-477
VWR-2652
VWR-2662
VWR-2834
@ -520,6 +544,7 @@ Pf Shan
CT-230
CT-231
CT-321
SNOW-422
princess niven
VWR-5733
CT-85
@ -541,14 +566,19 @@ Ringo Tuxing
Robin Cornelius
SNOW-108
SNOW-204
SNOW-287
SNOW-484
SNOW-504
SNOW-506
SNOW-507
SNOW-511
SNOW-512
SNOW-514
SNOW-520
SNOW-585
SNOW-599
SNOW-747
STORM-422
VWR-2488
VWR-9557
VWR-11128
@ -557,6 +587,7 @@ Robin Cornelius
VWR-12758
VWR-12763
VWR-12995
VWR-20911
Ryozu Kojima
VWR-53
VWR-287
@ -571,6 +602,8 @@ Salahzar Stenvaag
CT-321
Sammy Frederix
VWR-6186
Satomi Ahn
STORM-501
Scrippy Scofield
VWR-3748
Seg Baphomet
@ -629,6 +662,7 @@ Strife Onizuka
VWR-183
VWR-2265
VWR-4111
SNOW-691
Tayra Dagostino
SNOW-517
SNOW-543
@ -646,12 +680,19 @@ Teardrops Fall
VWR-5366
Techwolf Lupindo
SNOW-92
SNOW-592
SNOW-649
SNOW-650
SNOW-651
SNOW-654
SNOW-687
SNOW-680
SNOW-681
SNOW-685
SNOW-690
SNOW-746
VWR-12385
VWR-20893
tenebrous pau
VWR-247
Tharax Ferraris
@ -661,8 +702,9 @@ Thickbrick Sleaford
SNOW-390
SNOW-421
SNOW-462
SNOW-635
SNOW-586
SNOW-592
SNOW-635
SNOW-743
VWR-7109
VWR-9287
@ -673,6 +715,8 @@ Thraxis Epsilon
VWR-383
tiamat bingyi
CT-246
Tofu Buzzard
STORM-546
TraductoresAnonimos Alter
CT-324
Tue Torok
@ -718,8 +762,22 @@ Whoops Babii
VWR-8298
Wilton Lundquist
VWR-7682
WolfPup Lowenhar
SNOW-622
SNOW-772
STORM-102
STORM-103
STORM-143
STORM-535
STORM-544
STORM-654
VWR-20741
VWR-20933
Zai Lynch
VWR-19505
Wolfpup Lowenhar
STORM-255
STORM-256
Zarkonnen Decosta
VWR-253
Zi Ree

View File

@ -370,6 +370,14 @@
</map>
<!-- Server to client -->
<key>DisplayNameUpdate</key>
<map>
<key>flavor</key>
<string>llsd</string>
<key>trusted-sender</key>
<boolean>true</boolean>
</map>
<key>ParcelVoiceInfo</key>
<map>
<key>flavor</key>
@ -426,6 +434,22 @@
<boolean>true</boolean>
</map>
<key>SetDisplayNameReply</key>
<map>
<key>flavor</key>
<string>llsd</string>
<key>trusted-sender</key>
<boolean>true</boolean>
</map>
<key>SimConsoleResponse</key>
<map>
<key>flavor</key>
<string>llsd</string>
<key>trusted-sender</key>
<boolean>true</boolean>
</map>
<key>DirLandReply</key>
<map>
<key>flavor</key>

View File

@ -123,6 +123,8 @@ if (SERVER)
endif (WINDOWS)
endif (SERVER)
# Define after the custom viewer and server targets are created so individual
# apps can add themselves as dependencies
add_subdirectory(${INTEGRATION_TESTS_PREFIX}integration_tests)
if (LL_TESTS)
# Define after the custom viewer and server targets are created so
# individual apps can add themselves as dependencies
add_subdirectory(${INTEGRATION_TESTS_PREFIX}integration_tests)
endif (LL_TESTS)

View File

@ -26,6 +26,7 @@ set(cmake_SOURCE_FILES
FindBerkeleyDB.cmake
FindCARes.cmake
FindELFIO.cmake
FindFMOD.cmake
FindGooglePerfTools.cmake
FindMono.cmake
FindMT.cmake

View File

@ -55,9 +55,10 @@ if(WINDOWS)
set(release_files ${release_files} libtcmalloc_minimal.dll)
endif(USE_GOOGLE_PERFTOOLS)
if (FMOD_SDK_DIR)
set(fmod_files fmod.dll)
endif (FMOD_SDK_DIR)
if (FMOD)
set(debug_files ${debug_files} fmod.dll)
set(release_files ${release_files} fmod.dll)
endif (FMOD)
#*******************************
# LLKDU
@ -237,9 +238,9 @@ elseif(LINUX)
libssl.so.0.9.7
)
if (FMOD_SDK_DIR)
set(fmod_files "libfmod-3.75.so")
endif (FMOD_SDK_DIR)
if (FMOD)
set(release_files ${release_files} "libfmod-3.75.so")
endif (FMOD)
#*******************************
# LLKDU
@ -333,30 +334,6 @@ copy_if_different(
)
set(third_party_targets ${third_party_targets} ${out_targets})
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)
#*******************************
# LLKDU
set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu")

View File

@ -1,64 +1,26 @@
# -*- cmake -*-
include(Linking)
if(INSTALL_PROPRIETARY)
include(Prebuilt)
use_prebuilt_binary(fmod)
endif(INSTALL_PROPRIETARY)
find_library(FMOD_LIBRARY_RELEASE
NAMES fmod fmodvc fmod-3.75
PATHS
${ARCH_PREBUILT_DIRS_RELEASE}
)
find_library(FMOD_LIBRARY_DEBUG
NAMES fmod fmodvc fmod-3.75
PATHS
${ARCH_PREBUILT_DIRS_DEBUG}
)
if (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
set(FMOD_LIBRARY
debug ${FMOD_LIBRARY_DEBUG}
optimized ${FMOD_LIBRARY_RELEASE})
elseif (FMOD_LIBRARY_RELEASE)
set(FMOD_LIBRARY ${FMOD_LIBRARY_RELEASE})
endif (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
if (NOT FMOD_LIBRARY)
set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
if (FMOD_SDK_DIR)
find_library(FMOD_LIBRARY
NAMES fmodvc fmod-3.75 fmod
PATHS
${FMOD_SDK_DIR}/api/lib
${FMOD_SDK_DIR}/api
${FMOD_SDK_DIR}/lib
${FMOD_SDK_DIR}
)
endif (FMOD_SDK_DIR)
endif (NOT FMOD_LIBRARY)
find_path(FMOD_INCLUDE_DIR fmod.h
${LIBS_PREBUILT_DIR}/include
${FMOD_SDK_DIR}/api/inc
${FMOD_SDK_DIR}/inc
${FMOD_SDK_DIR}
)
if (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
set(FMOD ON CACHE BOOL "Use closed source FMOD sound library.")
else (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
set(FMOD_LIBRARY "")
set(FMOD_INCLUDE_DIR "")
if (FMOD)
message(STATUS "No support for FMOD audio (need to set FMOD_SDK_DIR?)")
endif (FMOD)
set(FMOD OFF CACHE BOOL "Use closed source FMOD sound library.")
endif (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
set(FMOD ON CACHE BOOL "Use FMOD sound library.")
if (FMOD)
message(STATUS "Building with FMOD audio support")
if (STANDALONE)
set(FMOD_FIND_REQUIRED ON)
include(FindFMOD)
else (STANDALONE)
if (INSTALL_PROPRIETARY)
include(Prebuilt)
use_prebuilt_binary(fmod)
endif (INSTALL_PROPRIETARY)
if (WINDOWS)
set(FMOD_LIBRARY fmod)
elseif (DARWIN)
set(FMOD_LIBRARY fmod)
elseif (LINUX)
set(FMOD_LIBRARY fmod-3.75)
endif (WINDOWS)
SET(FMOD_LIBRARIES ${FMOD_LIBRARY})
set(FMOD_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
endif (STANDALONE)
endif (FMOD)

View File

@ -0,0 +1,44 @@
# -*- cmake -*-
# - Find FMOD
# Find the FMOD includes and library
# This module defines
# FMOD_INCLUDE_DIR, where to find fmod.h and fmod_errors.h
# FMOD_LIBRARIES, the libraries needed to use FMOD.
# FMOD, If false, do not try to use FMOD.
# also defined, but not for general use are
# FMOD_LIBRARY, where to find the FMOD library.
FIND_PATH(FMOD_INCLUDE_DIR fmod.h PATH_SUFFIXES fmod)
SET(FMOD_NAMES ${FMOD_NAMES} fmod fmodvc fmod-3.75)
FIND_LIBRARY(FMOD_LIBRARY
NAMES ${FMOD_NAMES}
PATH_SUFFIXES fmod
)
IF (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
SET(FMOD_LIBRARIES ${FMOD_LIBRARY})
SET(FMOD_FOUND "YES")
ELSE (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
SET(FMOD_FOUND "NO")
ENDIF (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
IF (FMOD_FOUND)
IF (NOT FMOD_FIND_QUIETLY)
MESSAGE(STATUS "Found FMOD: ${FMOD_LIBRARIES}")
ENDIF (NOT FMOD_FIND_QUIETLY)
ELSE (FMOD_FOUND)
IF (FMOD_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find FMOD library")
ENDIF (FMOD_FIND_REQUIRED)
ENDIF (FMOD_FOUND)
# Deprecated declarations.
SET (NATIVE_FMOD_INCLUDE_PATH ${FMOD_INCLUDE_DIR} )
GET_FILENAME_COMPONENT (NATIVE_FMOD_LIB_PATH ${FMOD_LIBRARY} PATH)
MARK_AS_ADVANCED(
FMOD_LIBRARY
FMOD_INCLUDE_DIR
)

View File

@ -0,0 +1,40 @@
# -*- cmake -*-
# - Find Google BreakPad
# Find the Google BreakPad includes and library
# This module defines
# BREAKPAD_EXCEPTION_HANDLER_INCLUDE_DIR, where to find exception_handler.h, etc.
# BREAKPAD_EXCEPTION_HANDLER_LIBRARIES, the libraries needed to use Google BreakPad.
# BREAKPAD_EXCEPTION_HANDLER_FOUND, If false, do not try to use Google BreakPad.
# also defined, but not for general use are
# BREAKPAD_EXCEPTION_HANDLER_LIBRARY, where to find the Google BreakPad library.
FIND_PATH(BREAKPAD_EXCEPTION_HANDLER_INCLUDE_DIR google_breakpad/exception_handler.h)
SET(BREAKPAD_EXCEPTION_HANDLER_NAMES ${BREAKPAD_EXCEPTION_HANDLER_NAMES} breakpad_client)
FIND_LIBRARY(BREAKPAD_EXCEPTION_HANDLER_LIBRARY
NAMES ${BREAKPAD_EXCEPTION_HANDLER_NAMES}
)
IF (BREAKPAD_EXCEPTION_HANDLER_LIBRARY AND BREAKPAD_EXCEPTION_HANDLER_INCLUDE_DIR)
SET(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES ${BREAKPAD_EXCEPTION_HANDLER_LIBRARY})
SET(BREAKPAD_EXCEPTION_HANDLER_FOUND "YES")
ELSE (BREAKPAD_EXCEPTION_HANDLER_LIBRARY AND BREAKPAD_EXCEPTION_HANDLER_INCLUDE_DIR)
SET(BREAKPAD_EXCEPTION_HANDLER_FOUND "NO")
ENDIF (BREAKPAD_EXCEPTION_HANDLER_LIBRARY AND BREAKPAD_EXCEPTION_HANDLER_INCLUDE_DIR)
IF (BREAKPAD_EXCEPTION_HANDLER_FOUND)
IF (NOT BREAKPAD_EXCEPTION_HANDLER_FIND_QUIETLY)
MESSAGE(STATUS "Found Google BreakPad: ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES}")
ENDIF (NOT BREAKPAD_EXCEPTION_HANDLER_FIND_QUIETLY)
ELSE (BREAKPAD_EXCEPTION_HANDLER_FOUND)
IF (BREAKPAD_EXCEPTION_HANDLER_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find Google BreakPad library")
ENDIF (BREAKPAD_EXCEPTION_HANDLER_FIND_REQUIRED)
ENDIF (BREAKPAD_EXCEPTION_HANDLER_FOUND)
MARK_AS_ADVANCED(
BREAKPAD_EXCEPTION_HANDLER_LIBRARY
BREAKPAD_EXCEPTION_HANDLER_INCLUDE_DIR
)

View File

@ -2,8 +2,8 @@
include(Prebuilt)
if (STANDALONE)
MESSAGE(FATAL_ERROR "*TODO standalone support for google breakad is unimplemented")
# *TODO - implement this include(FindGoogleBreakpad)
set(BREAKPAD_EXCEPTION_HANDLER_FIND_REQUIRED ON)
include(FindGoogleBreakpad)
else (STANDALONE)
use_prebuilt_binary(google_breakpad)
if (DARWIN)

View File

@ -256,6 +256,10 @@ MACRO(SET_TEST_PATH LISTVAR)
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.
set(${LISTVAR} ${SHARED_LIB_STAGING_DIR} /usr/lib)
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)

View File

@ -1,28 +1,28 @@
# -*- cmake -*-
include(Prebuilt)
if (STANDALONE)
include(FindPkgConfig)
pkg_check_modules(PULSEAUDIO REQUIRED libpulse-mainloop-glib)
elseif (LINUX)
use_prebuilt_binary(pulseaudio)
set(PULSEAUDIO_FOUND ON FORCE BOOL)
set(PULSEAUDIO_INCLUDE_DIRS
${LIBS_PREBUILT_DIR}/include
)
# We don't need to explicitly link against pulseaudio itself, because
# the viewer probes for the system's copy at runtime.
set(PULSEAUDIO_LIBRARIES
# none needed!
)
endif (STANDALONE)
if (PULSEAUDIO_FOUND)
set(PULSEAUDIO ON CACHE BOOL "Build with PulseAudio support, if available.")
endif (PULSEAUDIO_FOUND)
set(PULSEAUDIO ON CACHE BOOL "Build with PulseAudio support, if available.")
if (PULSEAUDIO)
add_definitions(-DLL_PULSEAUDIO_ENABLED=1)
if (STANDALONE)
include(FindPkgConfig)
pkg_check_modules(PULSEAUDIO libpulse)
elseif (LINUX)
use_prebuilt_binary(pulseaudio)
set(PULSEAUDIO_FOUND ON FORCE BOOL)
set(PULSEAUDIO_INCLUDE_DIRS
${LIBS_PREBUILT_DIR}/include
)
# We don't need to explicitly link against pulseaudio itself, because
# the viewer probes for the system's copy at runtime.
set(PULSEAUDIO_LIBRARIES
# none needed!
)
endif (STANDALONE)
endif (PULSEAUDIO)
if (PULSEAUDIO_FOUND)
add_definitions(-DLL_PULSEAUDIO_ENABLED=1)
endif (PULSEAUDIO_FOUND)

View File

@ -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]

View File

@ -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)

View File

@ -54,19 +54,20 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(LINUX ON BOOl FORCE)
# If someone has specified a word size, use that to determine the
# architecture. Otherwise, let the architecture specify the word size.
# architecture. Otherwise, let the compiler specify the word size.
# Using uname will break under chroots and other cross arch compiles. RC
if (WORD_SIZE EQUAL 32)
set(ARCH i686)
elseif (WORD_SIZE EQUAL 64)
set(ARCH x86_64)
else (WORD_SIZE EQUAL 32)
execute_process(COMMAND uname -m COMMAND sed s/i.86/i686/
OUTPUT_VARIABLE ARCH OUTPUT_STRIP_TRAILING_WHITESPACE)
if (ARCH STREQUAL x86_64)
set(WORD_SIZE 64)
else (ARCH STREQUAL x86_64)
if(CMAKE_SIZEOF_VOID_P MATCHES 4)
set(ARCH i686)
set(WORD_SIZE 32)
endif (ARCH STREQUAL x86_64)
else(CMAKE_SIZEOF_VOID_P MATCHES 4)
set(ARCH x86_64)
set(WORD_SIZE 64)
endif(CMAKE_SIZEOF_VOID_P MATCHES 4)
endif (WORD_SIZE EQUAL 32)
set(LL_ARCH ${ARCH}_linux)
@ -86,7 +87,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if (NOT CMAKE_OSX_DEPLOYMENT_TARGET)
# NOTE: setting -isysroot is NOT adequate: http://lists.apple.com/archives/Xcode-users/2007/Oct/msg00696.html
# see http://public.kitware.com/Bug/view.php?id=9959 + poppy
set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.4u.sdk)
set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.5.sdk)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.4)
endif (NOT CMAKE_OSX_DEPLOYMENT_TARGET)

View File

@ -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)

View File

@ -24,7 +24,7 @@ myprog somearg otherarg
$LicenseInfo:firstyear=2009&license=viewerlgpl$
Second Life Viewer Source Code
Copyright (C) 2010, Linden Research, Inc.
Copyright (C) 2009-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

View File

@ -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)
{
@ -185,10 +185,9 @@ void export_test_floaters()
// Build a floater and output new attributes
LLXMLNodePtr output_node = new LLXMLNode();
LLFloater* floater = new LLFloater(LLSD());
LLUICtrlFactory::getInstance()->buildFloater(floater,
filename,
// FALSE, // don't open floater
output_node);
floater->buildFromFile( filename,
// FALSE, // don't open floater
output_node);
std::string out_filename = xui_dir + filename;
std::string::size_type extension_pos = out_filename.rfind(".xml");
out_filename.resize(extension_pos);

View File

@ -78,7 +78,7 @@ void LLWidgetReg::initClass(bool register_widgets)
LLDefaultChildRegistry::Register<LLMultiSlider> multi_slider_bar("multi_slider_bar");
LLDefaultChildRegistry::Register<LLMultiSliderCtrl> multi_slider("multi_slider");
LLDefaultChildRegistry::Register<LLPanel> panel("panel", &LLPanel::fromXML);
LLDefaultChildRegistry::Register<LLLayoutStack> layout_stack("layout_stack", &LLLayoutStack::fromXML);
LLDefaultChildRegistry::Register<LLLayoutStack> layout_stack("layout_stack");
LLDefaultChildRegistry::Register<LLProgressBar> progress_bar("progress_bar");
LLDefaultChildRegistry::Register<LLRadioGroup> radio_group("radio_group");
LLDefaultChildRegistry::Register<LLSearchEditor> search_editor("search_editor");

View File

@ -4,7 +4,7 @@
$LicenseInfo:firstyear=2006&license=viewerlgpl$
Second Life Viewer Source Code
Copyright (C) 2010, Linden Research, Inc.
Copyright (C) 2006-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

View File

@ -36,14 +36,6 @@ import os.path
import re
import time
#import sys # *TODO: remove. only used in testing.
#import pprint # *TODO: remove. only used in testing.
try:
set = set
except NameError:
from sets import Set as set
from indra.base import llsd
from indra.base import config
@ -195,8 +187,6 @@ class NamedQuery(object):
style. It also has to look for %:name% and :name% and
ready them for use in LIKE statements"""
if sql:
#print >>sys.stderr, "sql:",sql
# This first sub is to properly escape any % signs that
# are meant to be literally passed through to mysql in the
# query. It leaves any %'s that are used for
@ -408,7 +398,6 @@ class NamedQuery(object):
# build the query from the options available and the params
base_query = []
base_query.append(self._base_query)
#print >>sys.stderr, "base_query:",base_query
for opt, extra_where in self._options.items():
if type(extra_where) in (dict, list, tuple):
if opt in params:
@ -418,7 +407,6 @@ class NamedQuery(object):
base_query.append(extra_where)
if self._query_suffix:
base_query.append(self._query_suffix)
#print >>sys.stderr, "base_query:",base_query
full_query = '\n'.join(base_query)
# Go through the query and rewrite all of the ones with the

View File

@ -446,8 +446,14 @@ def uuid1(node=None, clock_seq=None):
def uuid3(namespace, name):
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
import md5
hash = md5.md5(namespace.bytes + name).digest()
try:
# Python 2.6
from hashlib import md5
except ImportError:
# Python 2.5 and earlier
from md5 import new as md5
hash = md5(namespace.bytes + name).digest()
return UUID(bytes=hash[:16], version=3)
def uuid4():

View File

@ -216,7 +216,7 @@ 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;
}

View File

@ -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

View File

@ -97,6 +97,7 @@ void LLAudioEngine::setDefaults()
}
mMasterGain = 1.f;
mInternalGain = 0.f;
mNextWindUpdate = 0.f;
mStreamingAudioImpl = NULL;
@ -247,15 +248,6 @@ void LLAudioEngine::idle(F32 max_decode_time)
// Primarily does position updating, cleanup of unused audio sources.
// Also does regeneration of the current priority of each audio source.
if (getMuted())
{
setInternalGain(0.f);
}
else
{
setInternalGain(getMasterGain());
}
S32 i;
for (i = 0; i < MAX_BUFFERS; i++)
{
@ -284,6 +276,12 @@ void LLAudioEngine::idle(F32 max_decode_time)
continue;
}
if (sourcep->isMuted())
{
++iter;
continue;
}
if (!sourcep->getChannel() && sourcep->getCurrentBuffer())
{
// We could potentially play this sound if its priority is high enough.
@ -336,9 +334,9 @@ void LLAudioEngine::idle(F32 max_decode_time)
// attached to each channel, since only those with active channels
// can have anything interesting happen with their queue? (Maybe not true)
LLAudioSource *sourcep = iter->second;
if (!sourcep->mQueuedDatap)
if (!sourcep->mQueuedDatap || sourcep->isMuted())
{
// Nothing queued, so we don't care.
// Muted, or nothing queued, so we don't care.
continue;
}
@ -418,6 +416,10 @@ void LLAudioEngine::idle(F32 max_decode_time)
for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
{
LLAudioSource *sourcep = iter->second;
if (sourcep->isMuted())
{
continue;
}
if (sourcep->isSyncMaster())
{
if (sourcep->getPriority() > max_sm_priority)
@ -691,15 +693,23 @@ bool LLAudioEngine::isWindEnabled()
void LLAudioEngine::setMuted(bool muted)
{
mMuted = muted;
if (muted != mMuted)
{
mMuted = muted;
setMasterGain(mMasterGain);
}
enableWind(!mMuted);
}
void LLAudioEngine::setMasterGain(const F32 gain)
{
mMasterGain = gain;
setInternalGain(gain);
F32 internal_gain = getMuted() ? 0.f : gain;
if (internal_gain != mInternalGain)
{
mInternalGain = internal_gain;
setInternalGain(mInternalGain);
}
}
F32 LLAudioEngine::getMasterGain()
@ -1243,13 +1253,14 @@ LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32
mOwnerID(owner_id),
mPriority(0.f),
mGain(gain),
mType(type),
mSourceMuted(false),
mAmbient(false),
mLoop(false),
mSyncMaster(false),
mSyncSlave(false),
mQueueSounds(false),
mPlayedOnce(false),
mType(type),
mChannelp(NULL),
mCurrentDatap(NULL),
mQueuedDatap(NULL)
@ -1301,6 +1312,10 @@ void LLAudioSource::updatePriority()
{
mPriority = 1.f;
}
else if (isMuted())
{
mPriority = 0.f;
}
else
{
// Priority is based on distance
@ -1349,25 +1364,33 @@ bool LLAudioSource::setupChannel()
bool LLAudioSource::play(const LLUUID &audio_uuid)
{
// Special abuse of play(); don't play a sound, but kill it.
if (audio_uuid.isNull())
{
if (getChannel())
{
getChannel()->setSource(NULL);
setChannel(NULL);
addAudioData(NULL, true);
if (!isMuted())
{
mCurrentDatap = NULL;
}
}
return false;
}
// Reset our age timeout if someone attempts to play the source.
mAgeTimer.reset();
LLAudioData *adp = gAudiop->getAudioData(audio_uuid);
bool has_buffer = gAudiop->updateBufferForData(adp, audio_uuid);
addAudioData(adp);
if (isMuted())
{
return false;
}
bool has_buffer = gAudiop->updateBufferForData(adp, audio_uuid);
if (!has_buffer)
{
// Don't bother trying to set up a channel or anything, we don't have an audio buffer.
@ -1392,10 +1415,11 @@ bool LLAudioSource::play(const LLUUID &audio_uuid)
}
bool LLAudioSource::isDone()
bool LLAudioSource::isDone() const
{
const F32 MAX_AGE = 60.f;
const F32 MAX_UNPLAYED_AGE = 15.f;
const F32 MAX_MUTED_AGE = 11.f;
if (isLoop())
{
@ -1403,7 +1427,6 @@ bool LLAudioSource::isDone()
return false;
}
if (hasPendingPreloads())
{
return false;
@ -1420,10 +1443,10 @@ bool LLAudioSource::isDone()
// This is a single-play source
if (!mChannelp)
{
if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce)
if ((elapsed > (mSourceMuted ? MAX_MUTED_AGE : MAX_UNPLAYED_AGE)) || mPlayedOnce)
{
// We don't have a channel assigned, and it's been
// over 5 seconds since we tried to play it. Don't bother.
// over 15 seconds since we tried to play it. Don't bother.
//llinfos << "No channel assigned, source is done" << llendl;
return true;
}
@ -1449,7 +1472,7 @@ bool LLAudioSource::isDone()
if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce)
{
// The sound isn't playing back after 5 seconds or we're already done playing it, kill it.
// The sound isn't playing back after 15 seconds or we're already done playing it, kill it.
return true;
}

View File

@ -118,8 +118,8 @@ public:
// Use these for temporarily muting the audio system.
// Does not change buffers, initialization, etc. but
// stops playing new sounds.
virtual void setMuted(bool muted);
virtual bool getMuted() const { return mMuted; }
void setMuted(bool muted);
bool getMuted() const { return mMuted; }
#ifdef USE_PLUGIN_MEDIA
LLPluginClassMedia* initializeMedia(const std::string& media_type);
#endif
@ -239,6 +239,7 @@ protected:
LLAudioBuffer *mBuffers[MAX_BUFFERS];
F32 mMasterGain;
F32 mInternalGain; // Actual gain set; either mMasterGain or 0 when mMuted is true.
F32 mSecondaryGain[AUDIO_TYPE_COUNT];
F32 mNextWindUpdate;
@ -303,7 +304,8 @@ public:
virtual void setGain(const F32 gain) { mGain = llclamp(gain, 0.f, 1.f); }
const LLUUID &getID() const { return mID; }
bool isDone();
bool isDone() const;
bool isMuted() const { return mSourceMuted; }
LLAudioData *getCurrentData();
LLAudioData *getQueuedData();
@ -325,6 +327,7 @@ protected:
LLUUID mOwnerID; // owner of the object playing the sound
F32 mPriority;
F32 mGain;
bool mSourceMuted;
bool mAmbient;
bool mLoop;
bool mSyncMaster;

View File

@ -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)))

View File

@ -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;

View File

@ -33,6 +33,7 @@ set(llcommon_SOURCE_FILES
llapp.cpp
llapr.cpp
llassettype.cpp
llavatarname.cpp
llbase32.cpp
llbase64.cpp
llcommon.cpp
@ -69,6 +70,7 @@ set(llcommon_SOURCE_FILES
llmemorystream.cpp
llmemtype.cpp
llmetrics.cpp
llmetricperformancetester.cpp
llmortician.cpp
lloptioninterface.cpp
llptrto.cpp
@ -115,6 +117,7 @@ set(llcommon_HEADER_FILES
llallocator.h
llallocator_heap_profile.h
llagentconstants.h
llavatarname.h
llapp.h
llapr.h
llassettype.h
@ -157,6 +160,7 @@ set(llcommon_HEADER_FILES
lleventemitter.h
llextendedstatus.h
llfasttimer.h
llfasttimer_class.h
llfile.h
llfindlocale.h
llfixedbuffer.h
@ -183,6 +187,7 @@ set(llcommon_HEADER_FILES
llmemorystream.h
llmemtype.h
llmetrics.h
llmetricperformancetester.h
llmortician.h
llnametable.h
lloptioninterface.h
@ -251,8 +256,15 @@ set_source_files_properties(${llcommon_HEADER_FILES}
list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})
if(LLCOMMON_LINK_SHARED)
add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
ll_stage_sharedlib(llcommon)
add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
if(NOT WORD_SIZE EQUAL 32)
if(WINDOWS)
add_definitions(/FIXED:NO)
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
add_definitions(-fPIC)
endif(WINDOWS)
endif(NOT WORD_SIZE EQUAL 32)
ll_stage_sharedlib(llcommon)
else(LLCOMMON_LINK_SHARED)
add_library (llcommon ${llcommon_SOURCE_FILES})
endif(LLCOMMON_LINK_SHARED)

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,113 @@
/**
* @file llavatarname.cpp
* @brief Represents name-related data for an avatar, such as the
* username/SLID ("bobsmith123" or "james.linden") and the display
* name ("James Cook")
*
* $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 "llavatarname.h"
#include "lldate.h"
#include "llsd.h"
// Store these in pre-built std::strings to avoid memory allocations in
// LLSD map lookups
static const std::string USERNAME("username");
static const std::string DISPLAY_NAME("display_name");
static const std::string LEGACY_FIRST_NAME("legacy_first_name");
static const std::string LEGACY_LAST_NAME("legacy_last_name");
static const std::string IS_DISPLAY_NAME_DEFAULT("is_display_name_default");
static const std::string DISPLAY_NAME_EXPIRES("display_name_expires");
static const std::string DISPLAY_NAME_NEXT_UPDATE("display_name_next_update");
LLAvatarName::LLAvatarName()
: mUsername(),
mDisplayName(),
mLegacyFirstName(),
mLegacyLastName(),
mIsDisplayNameDefault(false),
mIsDummy(false),
mExpires(F64_MAX),
mNextUpdate(0.0)
{ }
bool LLAvatarName::operator<(const LLAvatarName& rhs) const
{
if (mUsername == rhs.mUsername)
return mDisplayName < rhs.mDisplayName;
else
return mUsername < rhs.mUsername;
}
LLSD LLAvatarName::asLLSD() const
{
LLSD sd;
sd[USERNAME] = mUsername;
sd[DISPLAY_NAME] = mDisplayName;
sd[LEGACY_FIRST_NAME] = mLegacyFirstName;
sd[LEGACY_LAST_NAME] = mLegacyLastName;
sd[IS_DISPLAY_NAME_DEFAULT] = mIsDisplayNameDefault;
sd[DISPLAY_NAME_EXPIRES] = LLDate(mExpires);
sd[DISPLAY_NAME_NEXT_UPDATE] = LLDate(mNextUpdate);
return sd;
}
void LLAvatarName::fromLLSD(const LLSD& sd)
{
mUsername = sd[USERNAME].asString();
mDisplayName = sd[DISPLAY_NAME].asString();
mLegacyFirstName = sd[LEGACY_FIRST_NAME].asString();
mLegacyLastName = sd[LEGACY_LAST_NAME].asString();
mIsDisplayNameDefault = sd[IS_DISPLAY_NAME_DEFAULT].asBoolean();
LLDate expires = sd[DISPLAY_NAME_EXPIRES];
mExpires = expires.secondsSinceEpoch();
LLDate next_update = sd[DISPLAY_NAME_NEXT_UPDATE];
mNextUpdate = next_update.secondsSinceEpoch();
}
std::string LLAvatarName::getCompleteName() const
{
std::string name;
if (!mUsername.empty())
{
name = mDisplayName + " (" + mUsername + ")";
}
else
{
// ...display names are off, legacy name is in mDisplayName
name = mDisplayName;
}
return name;
}
std::string LLAvatarName::getLegacyName() const
{
std::string name;
name.reserve( mLegacyFirstName.size() + 1 + mLegacyLastName.size() );
name = mLegacyFirstName;
name += " ";
name += mLegacyLastName;
return name;
}

View File

@ -0,0 +1,95 @@
/**
* @file llavatarname.h
* @brief Represents name-related data for an avatar, such as the
* username/SLID ("bobsmith123" or "james.linden") and the display
* name ("James Cook")
*
* $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 LLAVATARNAME_H
#define LLAVATARNAME_H
#include <string>
class LLSD;
class LL_COMMON_API LLAvatarName
{
public:
LLAvatarName();
bool operator<(const LLAvatarName& rhs) const;
LLSD asLLSD() const;
void fromLLSD(const LLSD& sd);
// For normal names, returns "James Linden (james.linden)"
// When display names are disabled returns just "James Linden"
std::string getCompleteName() const;
// Returns "James Linden" or "bobsmith123 Resident" for backwards
// compatibility with systems like voice and muting
// *TODO: Eliminate this in favor of username only
std::string getLegacyName() const;
// "bobsmith123" or "james.linden", US-ASCII only
std::string mUsername;
// "Jose' Sanchez" or "James Linden", UTF-8 encoded Unicode
// Contains data whether or not user has explicitly set
// a display name; may duplicate their username.
std::string mDisplayName;
// For "James Linden", "James"
// For "bobsmith123", "bobsmith123"
// Used to communicate with legacy systems like voice and muting which
// rely on old-style names.
// *TODO: Eliminate this in favor of username only
std::string mLegacyFirstName;
// For "James Linden", "Linden"
// For "bobsmith123", "Resident"
// see above for rationale
std::string mLegacyLastName;
// If true, both display name and SLID were generated from
// a legacy first and last name, like "James Linden (james.linden)"
bool mIsDisplayNameDefault;
// 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;
// Names can change, so need to keep track of when name was
// last checked.
// Unix time-from-epoch seconds for efficiency
F64 mExpires;
// You can only change your name every N hours, so record
// when the next update is allowed
// Unix time-from-epoch seconds
F64 mNextUpdate;
};
#endif

View File

@ -28,7 +28,6 @@
#ifndef LL_LLCHAT_H
#define LL_LLCHAT_H
#include "llstring.h"
#include "lluuid.h"
#include "v3math.h"
@ -71,7 +70,7 @@ typedef enum e_chat_style
class LLChat
{
public:
LLChat(const std::string& text = LLStringUtil::null)
LLChat(const std::string& text = std::string())
: mText(text),
mFromName(),
mFromID(),

View File

@ -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); }

View File

@ -78,7 +78,9 @@ protected:
virtual Index notFound() const
{
// default is to assert
llassert(false);
// don't assert -- makes it impossible to work on mesh-development and viewer-development simultaneously
// -- davep 2010.10.29
//llassert(false);
return Index(-1);
}
void addEntry(Index index, Entry *entry)

View File

@ -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;
@ -469,9 +470,9 @@ void LLFastTimer::NamedTimer::accumulateTimings()
int hidx = cur_frame % HISTORY_NUM;
timerp->mCountHistory[hidx] = timerp->mTotalTimeCounter;
timerp->mCountAverage = (timerp->mCountAverage * cur_frame + timerp->mTotalTimeCounter) / (cur_frame+1);
timerp->mCountAverage = ((U64)timerp->mCountAverage * cur_frame + timerp->mTotalTimeCounter) / (cur_frame+1);
timerp->mCallHistory[hidx] = timerp->getFrameState().mCalls;
timerp->mCallAverage = (timerp->mCallAverage * cur_frame + timerp->getFrameState().mCalls) / (cur_frame+1);
timerp->mCallAverage = ((U64)timerp->mCallAverage * cur_frame + timerp->getFrameState().mCalls) / (cur_frame+1);
}
}
}

View File

@ -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;

View File

@ -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()
{
}

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -731,14 +731,17 @@ void LLStringOps::setupDatetimeInfo (bool daylight)
nowT = time (NULL);
tmpT = localtime (&nowT);
localT = mktime (tmpT);
tmpT = gmtime (&nowT);
gmtT = mktime (tmpT);
tmpT = localtime (&nowT);
localT = mktime (tmpT);
sLocalTimeOffset = (long) (gmtT - localT);
if (tmpT->tm_isdst)
{
sLocalTimeOffset -= 60 * 60; // 1 hour
}
sPacificDaylightTime = daylight;
sPacificTimeOffset = (sPacificDaylightTime? 7 : 8 ) * 60 * 60;

View File

@ -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

View File

@ -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);
};

View File

@ -28,10 +28,14 @@
#define LL_LLVERSIONVIEWER_H
const S32 LL_VERSION_MAJOR = 2;
const S32 LL_VERSION_MINOR = 2;
const S32 LL_VERSION_MINOR = 4;
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.snowglobe.viewer";
#endif
#endif

View File

@ -52,7 +52,6 @@ enum LLRoleChangeType
//
// KNOWN HOLES: use these for any single bit powers you need
// bit 0x1 << 41
// bit 0x1 << 46
// bit 0x1 << 49 and above
@ -103,6 +102,8 @@ const U64 GP_LAND_ALLOW_FLY = 0x1 << 24; // Bypass Fly Restriction
const U64 GP_LAND_ALLOW_CREATE = 0x1 << 25; // Bypass Create/Edit Objects Restriction
const U64 GP_LAND_ALLOW_LANDMARK = 0x1 << 26; // Bypass Landmark Restriction
const U64 GP_LAND_ALLOW_SET_HOME = 0x1 << 28; // Bypass Set Home Point Restriction
const U64 GP_LAND_ALLOW_HOLD_EVENT = 0x1LL << 41; // Allowed to hold events on group-owned land
// Parcel Access
const U64 GP_LAND_MANAGE_ALLOWED = 0x1 << 29; // Manage Allowed List

View File

@ -113,8 +113,8 @@ enum EObjectPropertiesExtraID
enum EAddPosition
{
ADD_TOP,
ADD_SORTED,
ADD_BOTTOM
ADD_BOTTOM,
ADD_DEFAULT
};
enum LLGroupChange

View File

@ -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;
}

View File

@ -30,6 +30,8 @@
#include "lldir.h"
#include "llimagej2c.h"
#include "llmemtype.h"
#include "lltimer.h"
#include "llmath.h"
typedef LLImageJ2CImpl* (*CreateLLImageJ2CFunction)();
typedef void (*DestroyLLImageJ2CFunction)(LLImageJ2CImpl*);
@ -51,6 +53,10 @@ LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl();
void fallbackDestroyLLImageJ2CImpl(LLImageJ2CImpl* impl);
const char* fallbackEngineInfoLLImageJ2CImpl();
// Test data gathering handle
LLImageCompressionTester* LLImageJ2C::sTesterp = NULL ;
const std::string sTesterName("ImageCompressionTester");
//static
//Loads the required "create", "destroy" and "engineinfo" functions needed
void LLImageJ2C::openDSO()
@ -71,8 +77,8 @@ void LLImageJ2C::openDSO()
#endif
dso_path = gDirUtilp->findFile(dso_name,
gDirUtilp->getAppRODataDir(),
gDirUtilp->getExecutableDir());
gDirUtilp->getAppRODataDir(),
gDirUtilp->getExecutableDir());
j2cimpl_dso_handle = NULL;
j2cimpl_dso_memory_pool = NULL;
@ -102,7 +108,7 @@ void LLImageJ2C::openDSO()
//so lets check for a destruction function
rv = apr_dso_sym((apr_dso_handle_sym_t*)&dest_func,
j2cimpl_dso_handle,
"destroyLLImageJ2CKDU");
"destroyLLImageJ2CKDU");
if ( rv == APR_SUCCESS )
{
//we've loaded the destroy function ok
@ -195,6 +201,17 @@ LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C),
{ // 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
@ -280,6 +297,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 +336,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 +363,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 +371,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 +590,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
//----------------------------------------------------------------------------------------------

View File

@ -29,8 +29,11 @@
#include "llimage.h"
#include "llassettype.h"
#include "llmetricperformancetester.h"
class LLImageJ2CImpl;
class LLImageCompressionTester ;
class LLImageJ2C : public LLImageFormatted
{
protected:
@ -72,11 +75,12 @@ public:
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 +94,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 +125,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

View File

@ -108,6 +108,9 @@ std::string build_transfer_message_to_source(
std::ostringstream ostr;
if(dest_id.isNull())
{
// *NOTE: Do not change these strings! The viewer matches
// them in llviewermessage.cpp to perform localization.
// If you need to make changes, add a new, localizable message. JC
ostr << "You paid L$" << amount;
switch(transaction_type)
{
@ -154,6 +157,9 @@ std::string build_transfer_message_to_destination(
return description;
}
std::ostringstream ostr;
// *NOTE: Do not change these strings! The viewer matches
// them in llviewermessage.cpp to perform localization.
// If you need to make changes, add a new, localizable message. JC
ostr << source_name << " paid you L$" << amount;
append_reason(ostr, transaction_type, description);
ostr << ".";

View File

@ -25,6 +25,7 @@ set(llmessage_SOURCE_FILES
llares.cpp
llareslistener.cpp
llassetstorage.cpp
llavatarnamecache.cpp
llblowfishcipher.cpp
llbuffer.cpp
llbufferstream.cpp
@ -110,6 +111,7 @@ set(llmessage_HEADER_FILES
llares.h
llareslistener.h
llassetstorage.h
llavatarnamecache.h
llblowfishcipher.h
llbuffer.h
llbufferstream.h
@ -248,6 +250,7 @@ if (LL_TESTS)
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py"
)
LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llxfer_file "" "${test_libs}")

View File

@ -0,0 +1,812 @@
/**
* @file llavatarnamecache.cpp
* @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names
* ("James Cook") from avatar UUIDs.
*
* $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 "llavatarnamecache.h"
#include "llcachename.h" // we wrap this system
#include "llframetimer.h"
#include "llhttpclient.h"
#include "llsd.h"
#include "llsdserialize.h"
#include <boost/tokenizer.hpp>
#include <map>
#include <set>
namespace LLAvatarNameCache
{
use_display_name_signal_t mUseDisplayNamesSignal;
// Manual override for display names - can disable even if the region
// supports it.
bool sUseDisplayNames = true;
// Cache starts in a paused state until we can determine if the
// current region supports display names.
bool sRunning = false;
// Base lookup URL for name service.
// On simulator, loaded from indra.xml
// On viewer, usually a simulator capability (at People API team's request)
// Includes the trailing slash, like "http://pdp60.lindenlab.com:8000/agents/"
std::string sNameLookupURL;
// accumulated agent IDs for next query against service
typedef std::set<LLUUID> ask_queue_t;
ask_queue_t sAskQueue;
// agent IDs that have been requested, but with no reply
// maps agent ID to frame time request was made
typedef std::map<LLUUID, F64> pending_queue_t;
pending_queue_t sPendingQueue;
// Callbacks to fire when we received a name.
// May have multiple callbacks for a single ID, which are
// represented as multiple slots bound to the signal.
// Avoid copying signals via pointers.
typedef std::map<LLUUID, callback_signal_t*> signal_map_t;
signal_map_t sSignalMap;
// names we know about
typedef std::map<LLUUID, LLAvatarName> cache_t;
cache_t sCache;
// Send bulk lookup requests a few times a second at most
// only need per-frame timing resolution
LLFrameTimer sRequestTimer;
// Periodically clean out expired entries from the cache
//LLFrameTimer sEraseExpiredTimer;
//-----------------------------------------------------------------------
// Internal methods
//-----------------------------------------------------------------------
// Handle name response off network.
// Optionally skip adding to cache, used when this is a fallback to the
// legacy name system.
void processName(const LLUUID& agent_id,
const LLAvatarName& av_name,
bool add_to_cache);
void requestNamesViaCapability();
// Legacy name system callback
void legacyNameCallback(const LLUUID& agent_id,
const std::string& full_name,
bool is_group);
void requestNamesViaLegacy();
// Fill in an LLAvatarName with the legacy name data
void buildLegacyName(const std::string& full_name,
LLAvatarName* av_name);
// Do a single callback to a given slot
void fireSignal(const LLUUID& agent_id,
const callback_slot_t& slot,
const LLAvatarName& av_name);
// Is a request in-flight over the network?
bool isRequestPending(const LLUUID& agent_id);
// Erase expired names from cache
void eraseExpired();
bool expirationFromCacheControl(LLSD headers, F64 *expires);
}
/* Sample response:
<?xml version="1.0"?>
<llsd>
<map>
<key>agents</key>
<array>
<map>
<key>display_name_next_update</key>
<date>2010-04-16T21:34:02+00:00Z</date>
<key>display_name_expires</key>
<date>2010-04-16T21:32:26.142178+00:00Z</date>
<key>display_name</key>
<string>MickBot390 LLQABot</string>
<key>sl_id</key>
<string>mickbot390.llqabot</string>
<key>id</key>
<string>0012809d-7d2d-4c24-9609-af1230a37715</string>
<key>is_display_name_default</key>
<boolean>false</boolean>
</map>
<map>
<key>display_name_next_update</key>
<date>2010-04-16T21:34:02+00:00Z</date>
<key>display_name_expires</key>
<date>2010-04-16T21:32:26.142178+00:00Z</date>
<key>display_name</key>
<string>Bjork Gudmundsdottir</string>
<key>sl_id</key>
<string>sardonyx.linden</string>
<key>id</key>
<string>3941037e-78ab-45f0-b421-bd6e77c1804d</string>
<key>is_display_name_default</key>
<boolean>true</boolean>
</map>
</array>
</map>
</llsd>
*/
class LLAvatarNameResponder : public LLHTTPClient::Responder
{
private:
// need to store agent ids that are part of this request in case of
// an error, so we can flag them as unavailable
std::vector<LLUUID> mAgentIDs;
// Need the headers to look up Expires: and Retry-After:
LLSD mHeaders;
public:
LLAvatarNameResponder(const std::vector<LLUUID>& agent_ids)
: mAgentIDs(agent_ids),
mHeaders()
{ }
/*virtual*/ void completedHeader(U32 status, const std::string& reason,
const LLSD& headers)
{
mHeaders = headers;
}
/*virtual*/ void result(const LLSD& content)
{
// Pull expiration out of headers if available
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mHeaders);
LLSD agents = content["agents"];
LLSD::array_const_iterator it = agents.beginArray();
for ( ; it != agents.endArray(); ++it)
{
const LLSD& row = *it;
LLUUID agent_id = row["id"].asUUID();
LLAvatarName av_name;
av_name.fromLLSD(row);
// Use expiration time from header
av_name.mExpires = expires;
// Some avatars don't have explicit display names set
if (av_name.mDisplayName.empty())
{
av_name.mDisplayName = av_name.mUsername;
}
// 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)
{
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;
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);
}
}
}
/*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
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
const F64 DEFAULT_DELAY = 120.0; // 2 mintues
return now + DEFAULT_DELAY;
}
};
void LLAvatarNameCache::processName(const LLUUID& agent_id,
const LLAvatarName& av_name,
bool add_to_cache)
{
if (add_to_cache)
{
sCache[agent_id] = av_name;
}
sPendingQueue.erase(agent_id);
// signal everyone waiting on this name
signal_map_t::iterator sig_it = sSignalMap.find(agent_id);
if (sig_it != sSignalMap.end())
{
callback_signal_t* signal = sig_it->second;
(*signal)(agent_id, av_name);
sSignalMap.erase(agent_id);
delete signal;
signal = NULL;
}
}
void LLAvatarNameCache::requestNamesViaCapability()
{
F64 now = LLFrameTimer::getTotalSeconds();
// URL format is like:
// http://pdp60.lindenlab.com:8000/agents/?ids=3941037e-78ab-45f0-b421-bd6e77c1804d&ids=0012809d-7d2d-4c24-9609-af1230a37715&ids=0019aaba-24af-4f0a-aa72-6457953cf7f0
//
// Apache can handle URLs of 4096 chars, but let's be conservative
const U32 NAME_URL_MAX = 4096;
const U32 NAME_URL_SEND_THRESHOLD = 3000;
std::string url;
url.reserve(NAME_URL_MAX);
std::vector<LLUUID> agent_ids;
agent_ids.reserve(128);
ask_queue_t::const_iterator it = sAskQueue.begin();
for ( ; it != sAskQueue.end(); ++it)
{
const LLUUID& agent_id = *it;
if (url.empty())
{
// ...starting new request
url += sNameLookupURL;
url += "?ids=";
}
else
{
// ...continuing existing request
url += "&ids=";
}
url += agent_id.asString();
agent_ids.push_back(agent_id);
// mark request as pending
sPendingQueue[agent_id] = now;
if (url.size() > NAME_URL_SEND_THRESHOLD)
{
//llinfos << "requestNames " << url << llendl;
LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids));
url.clear();
agent_ids.clear();
}
}
if (!url.empty())
{
//llinfos << "requestNames " << url << llendl;
LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids));
url.clear();
agent_ids.clear();
}
// We've moved all asks to the pending request queue
sAskQueue.clear();
}
void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id,
const std::string& full_name,
bool is_group)
{
// 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;
buildLegacyName(full_name, &av_name);
// Don't add to cache, the data already exists in the legacy name system
// cache and we don't want or need duplicate storage, because keeping the
// two copies in sync is complex.
processName(agent_id, av_name, false);
}
void LLAvatarNameCache::requestNamesViaLegacy()
{
F64 now = LLFrameTimer::getTotalSeconds();
std::string full_name;
ask_queue_t::const_iterator it = sAskQueue.begin();
for (; it != sAskQueue.end(); ++it)
{
const LLUUID& agent_id = *it;
// Mark as pending first, just in case the callback is immediately
// invoked below. This should never happen in practice.
sPendingQueue[agent_id] = now;
gCacheName->get(agent_id, false, // legacy compatibility
boost::bind(&LLAvatarNameCache::legacyNameCallback,
_1, _2, _3));
}
// We've either answered immediately or moved all asks to the
// pending queue
sAskQueue.clear();
}
void LLAvatarNameCache::initClass(bool running)
{
sRunning = running;
}
void LLAvatarNameCache::cleanupClass()
{
}
void LLAvatarNameCache::importFile(std::istream& istr)
{
LLSD data;
S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr);
if (parse_count < 1) return;
// by convention LLSD storage is a map
// we only store one entry in the map
LLSD agents = data["agents"];
LLUUID agent_id;
LLAvatarName av_name;
LLSD::map_const_iterator it = agents.beginMap();
for ( ; it != agents.endMap(); ++it)
{
agent_id.set(it->first);
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;
}
void LLAvatarNameCache::exportFile(std::ostream& ostr)
{
LLSD agents;
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)
{
// key must be a string
agents[agent_id.asString()] = av_name.asLLSD();
}
}
LLSD data;
data["agents"] = agents;
LLSDSerialize::toPrettyXML(data, ostr);
}
void LLAvatarNameCache::setNameLookupURL(const std::string& name_lookup_url)
{
sNameLookupURL = name_lookup_url;
}
bool LLAvatarNameCache::hasNameLookupURL()
{
return !sNameLookupURL.empty();
}
void LLAvatarNameCache::idle()
{
// By convention, start running at first idle() call
sRunning = true;
// *TODO: Possibly re-enabled this based on People API load measurements
// 100 ms is the threshold for "user speed" operations, so we can
// stall for about that long to batch up requests.
//const F32 SECS_BETWEEN_REQUESTS = 0.1f;
//if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS))
//{
// 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())
{
return;
}
if (useDisplayNames())
{
requestNamesViaCapability();
}
else
{
// ...fall back to legacy name cache system
requestNamesViaLegacy();
}
}
bool LLAvatarNameCache::isRequestPending(const LLUUID& agent_id)
{
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;
}
return false;
}
void LLAvatarNameCache::eraseExpired()
{
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);
}
}
}
void LLAvatarNameCache::buildLegacyName(const std::string& full_name,
LLAvatarName* av_name)
{
llassert(av_name);
av_name->mUsername = "";
av_name->mDisplayName = full_name;
av_name->mIsDisplayNameDefault = true;
av_name->mIsDummy = true;
av_name->mExpires = F64_MAX;
}
// fills in av_name if it has it in the cache, even if expired (can check expiry time)
// returns bool specifying if av_name was filled, false otherwise
bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
{
if (sRunning)
{
// ...only do immediate lookups when cache is running
if (useDisplayNames())
{
// ...use display names cache
std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id);
if (it != sCache.end())
{
*av_name = it->second;
// re-request name if entry is expired
if (av_name->mExpires < LLFrameTimer::getTotalSeconds())
{
if (!isRequestPending(agent_id))
{
sAskQueue.insert(agent_id);
}
}
return true;
}
}
else
{
// ...use legacy names cache
std::string full_name;
if (gCacheName->getFullName(agent_id, full_name))
{
buildLegacyName(full_name, av_name);
return true;
}
}
}
if (!isRequestPending(agent_id))
{
sAskQueue.insert(agent_id);
}
return false;
}
void LLAvatarNameCache::fireSignal(const LLUUID& agent_id,
const callback_slot_t& slot,
const LLAvatarName& av_name)
{
callback_signal_t signal;
signal.connect(slot);
signal(agent_id, av_name);
}
void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot)
{
if (sRunning)
{
// ...only do immediate lookups when cache is running
if (useDisplayNames())
{
// ...use new cache
std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id);
if (it != sCache.end())
{
const LLAvatarName& av_name = it->second;
if (av_name.mExpires > LLFrameTimer::getTotalSeconds())
{
// ...name already exists in cache, fire callback now
fireSignal(agent_id, slot, av_name);
return;
}
}
}
else
{
// ...use old name system
std::string full_name;
if (gCacheName->getFullName(agent_id, full_name))
{
LLAvatarName av_name;
buildLegacyName(full_name, &av_name);
fireSignal(agent_id, slot, av_name);
return;
}
}
}
// schedule a request
if (!isRequestPending(agent_id))
{
sAskQueue.insert(agent_id);
}
// always store additional callback, even if request is pending
signal_map_t::iterator sig_it = sSignalMap.find(agent_id);
if (sig_it == sSignalMap.end())
{
// ...new callback for this id
callback_signal_t* signal = new callback_signal_t();
signal->connect(slot);
sSignalMap[agent_id] = signal;
}
else
{
// ...existing callback, bind additional slot
callback_signal_t* signal = sig_it->second;
signal->connect(slot);
}
}
void LLAvatarNameCache::setUseDisplayNames(bool use)
{
if (use != sUseDisplayNames)
{
sUseDisplayNames = use;
// flush our cache
sCache.clear();
mUseDisplayNamesSignal();
}
}
bool LLAvatarNameCache::useDisplayNames()
{
// Must be both manually set on and able to look up names.
return sUseDisplayNames && !sNameLookupURL.empty();
}
void LLAvatarNameCache::erase(const LLUUID& agent_id)
{
sCache.erase(agent_id);
}
void LLAvatarNameCache::fetch(const LLUUID& agent_id)
{
// re-request, even if request is already pending
sAskQueue.insert(agent_id);
}
void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_name)
{
// *TODO: update timestamp if zero?
sCache[agent_id] = av_name;
}
F64 LLAvatarNameCache::nameExpirationFromHeaders(LLSD headers)
{
F64 expires = 0.0;
if (expirationFromCacheControl(headers, &expires))
{
return expires;
}
else
{
// With no expiration info, default to an hour
const F64 DEFAULT_EXPIRES = 60.0 * 60.0;
F64 now = LLFrameTimer::getTotalSeconds();
return now + DEFAULT_EXPIRES;
}
}
bool LLAvatarNameCache::expirationFromCacheControl(LLSD headers, F64 *expires)
{
// Allow the header to override the default
LLSD cache_control_header = headers["cache-control"];
if (cache_control_header.isDefined())
{
S32 max_age = 0;
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;
}
}
return false;
}
void LLAvatarNameCache::addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb)
{
mUseDisplayNamesSignal.connect(cb);
}
static const std::string MAX_AGE("max-age");
static const boost::char_separator<char> EQUALS_SEPARATOR("=");
static const boost::char_separator<char> COMMA_SEPARATOR(",");
bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
{
// Split the string on "," to get a list of directives
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
tokenizer directives(cache_control, COMMA_SEPARATOR);
tokenizer::iterator token_it = directives.begin();
for ( ; token_it != directives.end(); ++token_it)
{
// Tokens may have leading or trailing whitespace
std::string token = *token_it;
LLStringUtil::trim(token);
if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
{
// ...this token starts with max-age, so let's chop it up by "="
tokenizer subtokens(token, EQUALS_SEPARATOR);
tokenizer::iterator subtoken_it = subtokens.begin();
// Must have a token
if (subtoken_it == subtokens.end()) return false;
std::string subtoken = *subtoken_it;
// Must exactly equal "max-age"
LLStringUtil::trim(subtoken);
if (subtoken != MAX_AGE) return false;
// Must have another token
++subtoken_it;
if (subtoken_it == subtokens.end()) return false;
subtoken = *subtoken_it;
// Must be a valid integer
// *NOTE: atoi() returns 0 for invalid values, so we have to
// check the string first.
// *TODO: Do servers ever send "0000" for zero? We don't handle it
LLStringUtil::trim(subtoken);
if (subtoken == "0")
{
*max_age = 0;
return true;
}
S32 val = atoi( subtoken.c_str() );
if (val > 0 && val < S32_MAX)
{
*max_age = val;
return true;
}
return false;
}
}
return false;
}

View File

@ -0,0 +1,103 @@
/**
* @file llavatarnamecache.h
* @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names
* ("James Cook") from avatar UUIDs.
*
* $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 LLAVATARNAMECACHE_H
#define LLAVATARNAMECACHE_H
#include "llavatarname.h" // for convenience
#include <boost/signals2.hpp>
class LLSD;
class LLUUID;
namespace LLAvatarNameCache
{
typedef boost::signals2::signal<void (void)> use_display_name_signal_t;
// Until the cache is set running, immediate lookups will fail and
// async lookups will be queued. This allows us to block requests
// until we know if the first region supports display names.
void initClass(bool running);
void cleanupClass();
void importFile(std::istream& istr);
void exportFile(std::ostream& ostr);
// On the viewer, usually a simulator capabilitity
// If empty, name cache will fall back to using legacy name
// lookup system
void setNameLookupURL(const std::string& name_lookup_url);
// Do we have a valid lookup URL, hence are we trying to use the
// new display name lookup system?
bool hasNameLookupURL();
// Periodically makes a batch request for display names not already in
// cache. Call once per frame.
void idle();
// If name is in cache, returns true and fills in provided LLAvatarName
// otherwise returns false
bool get(const LLUUID& agent_id, LLAvatarName *av_name);
// Callback types for get() below
typedef boost::signals2::signal<
void (const LLUUID& agent_id, const LLAvatarName& av_name)>
callback_signal_t;
typedef callback_signal_t::slot_type callback_slot_t;
// Fetches name information and calls callback.
// If name information is in cache, callback will be called immediately.
void get(const LLUUID& agent_id, callback_slot_t slot);
// Allow display names to be explicitly disabled for testing.
void setUseDisplayNames(bool use);
bool useDisplayNames();
void erase(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);
void insert(const LLUUID& agent_id, const LLAvatarName& av_name);
// Compute name expiration time from HTTP Cache-Control header,
// or return default value, in seconds from epoch.
F64 nameExpirationFromHeaders(LLSD headers);
void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb);
}
// Parse a cache-control header to get the max-age delta-seconds.
// Returns true if header has max-age param and it parses correctly.
// Exported here to ease unit testing.
bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
#endif

View File

@ -38,6 +38,8 @@
#include "message.h"
#include "llmemtype.h"
#include <boost/regex.hpp>
// llsd serialization constants
static const std::string AGENTS("agents");
static const std::string GROUPS("groups");
@ -69,6 +71,8 @@ public:
public:
bool mIsGroup;
U32 mCreateTime; // unix time_t
// IDEVO TODO collapse names to one field, which will eliminate
// many string compares on "Resident"
std::string mFirstName;
std::string mLastName;
std::string mGroupName;
@ -214,7 +218,9 @@ public:
Impl(LLMessageSystem* msg);
~Impl();
BOOL getName(const LLUUID& id, std::string& first, std::string& last);
boost::signals2::connection addPending(const LLUUID& id, const LLCacheNameCallback& callback);
void addPending(const LLUUID& id, const LLHost& host);
@ -300,89 +306,10 @@ boost::signals2::connection LLCacheName::addObserver(const LLCacheNameCallback&
return impl.mSignal.connect(callback);
}
void LLCacheName::importFile(LLFILE* fp)
{
S32 count = 0;
const S32 BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE]; /*Flawfinder: ignore*/
// *NOTE: These buffer sizes are hardcoded into sscanf() below
char id_string[MAX_STRING]; /*Flawfinder: ignore*/
char firstname[MAX_STRING]; /*Flawfinder: ignore*/
char lastname[MAX_STRING]; /*Flawfinder: ignore*/
U32 create_time;
// This is OK if the first line is actually a name. We just don't load it.
char* valid = fgets(buffer, BUFFER_SIZE, fp);
if (!valid) return;
// *NOTE: This buffer size is hardcoded into sscanf() below
char version_string[BUFFER_SIZE]; /*Flawfinder: ignore*/
S32 version = 0;
S32 match = sscanf( /* Flawfinder: ignore */
buffer,
"%1023s %d",
version_string, &version);
if ( match != 2
|| strcmp(version_string, "version")
|| version != CN_FILE_VERSION)
{
llwarns << "Ignoring old cache name file format" << llendl;
return;
}
// We'll expire entries more than a week old
U32 now = (U32)time(NULL);
const U32 SECS_PER_DAY = 60 * 60 * 24;
U32 delete_before_time = now - (7 * SECS_PER_DAY);
while(!feof(fp))
{
valid = fgets(buffer, BUFFER_SIZE, fp);
if (!valid) break;
match = sscanf( /* Flawfinder: ignore */
buffer,
"%254s %u %254s %254s",
id_string,
&create_time,
firstname,
lastname);
if (4 != match) continue;
LLUUID id(id_string);
if (id.isNull()) continue;
// undo trivial XOR
S32 i;
for (i = 0; i < UUID_BYTES; i++)
{
id.mData[i] ^= 0x33;
}
// Don't load entries that are more than a week old
if (create_time < delete_before_time) continue;
LLCacheNameEntry* entry = new LLCacheNameEntry();
entry->mIsGroup = false;
entry->mCreateTime = create_time;
entry->mFirstName = firstname;
entry->mLastName = lastname;
impl.mCache[id] = entry;
std::string fullname = entry->mFirstName + " " + entry->mLastName;
impl.mReverseCache[fullname] = id;
count++;
}
llinfos << "LLCacheName loaded " << count << " names" << llendl;
}
bool LLCacheName::importFile(std::istream& istr)
{
LLSD data;
if(LLSDSerialize::fromXML(data, istr) < 1)
if(LLSDSerialize::fromXMLDocument(data, istr) < 1)
return false;
// We'll expire entries more than a week old
@ -408,7 +335,7 @@ bool LLCacheName::importFile(std::istream& istr)
entry->mFirstName = agent[FIRST].asString();
entry->mLastName = agent[LAST].asString();
impl.mCache[id] = entry;
std::string fullname = entry->mFirstName + " " + entry->mLastName;
std::string fullname = buildFullName(entry->mFirstName, entry->mLastName);
impl.mReverseCache[fullname] = id;
++count;
@ -457,6 +384,7 @@ void LLCacheName::exportFile(std::ostream& ostr)
// store it
LLUUID id = iter->first;
std::string id_str = id.asString();
// IDEVO TODO: Should we store SLIDs with last name "Resident" or not?
if(!entry->mFirstName.empty() && !entry->mLastName.empty())
{
data[AGENTS][id_str][FIRST] = entry->mFirstName;
@ -474,7 +402,7 @@ void LLCacheName::exportFile(std::ostream& ostr)
}
BOOL LLCacheName::getName(const LLUUID& id, std::string& first, std::string& last)
BOOL LLCacheName::Impl::getName(const LLUUID& id, std::string& first, std::string& last)
{
if(id.isNull())
{
@ -483,7 +411,7 @@ BOOL LLCacheName::getName(const LLUUID& id, std::string& first, std::string& las
return TRUE;
}
LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
LLCacheNameEntry* entry = get_ptr_in_map(mCache, id );
if (entry)
{
first = entry->mFirstName;
@ -494,16 +422,17 @@ BOOL LLCacheName::getName(const LLUUID& id, std::string& first, std::string& las
{
first = sCacheName["waiting"];
last.clear();
if (!impl.isRequestPending(id))
if (!isRequestPending(id))
{
impl.mAskNameQueue.insert(id);
mAskNameQueue.insert(id);
}
return FALSE;
}
}
// static
void LLCacheName::LocalizeCacheName(std::string key, std::string value)
void LLCacheName::localizeCacheName(std::string key, std::string value)
{
if (key!="" && value!= "" )
sCacheName[key]=value;
@ -514,11 +443,13 @@ void LLCacheName::LocalizeCacheName(std::string key, std::string value)
BOOL LLCacheName::getFullName(const LLUUID& id, std::string& fullname)
{
std::string first_name, last_name;
BOOL res = getName(id, first_name, last_name);
fullname = first_name + " " + last_name;
BOOL res = impl.getName(id, first_name, last_name);
fullname = buildFullName(first_name, last_name);
return res;
}
BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group)
{
if(id.isNull())
@ -555,13 +486,13 @@ BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group)
BOOL LLCacheName::getUUID(const std::string& first, const std::string& last, LLUUID& id)
{
std::string fullname = first + " " + last;
return getUUID(fullname, id);
std::string full_name = buildFullName(first, last);
return getUUID(full_name, id);
}
BOOL LLCacheName::getUUID(const std::string& fullname, LLUUID& id)
BOOL LLCacheName::getUUID(const std::string& full_name, LLUUID& id)
{
ReverseCache::iterator iter = impl.mReverseCache.find(fullname);
ReverseCache::iterator iter = impl.mReverseCache.find(full_name);
if (iter != impl.mReverseCache.end())
{
id = iter->second;
@ -573,6 +504,89 @@ BOOL LLCacheName::getUUID(const std::string& fullname, LLUUID& id)
}
}
//static
std::string LLCacheName::buildFullName(const std::string& first, const std::string& last)
{
std::string fullname = first;
if (!last.empty()
&& last != "Resident")
{
fullname += ' ';
fullname += last;
}
return fullname;
}
//static
std::string LLCacheName::cleanFullName(const std::string& full_name)
{
return full_name.substr(0, full_name.find(" Resident"));
}
//static
std::string LLCacheName::buildUsername(const std::string& full_name)
{
// rare, but handle hard-coded error names returned from server
if (full_name == "(\?\?\?) (\?\?\?)")
{
return "(\?\?\?)";
}
std::string::size_type index = full_name.find(' ');
if (index != std::string::npos)
{
std::string username;
username = full_name.substr(0, index);
std::string lastname = full_name.substr(index+1);
if (lastname != "Resident")
{
username = username + "." + lastname;
}
LLStringUtil::toLower(username);
return username;
}
// if the input wasn't a correctly formatted legacy name just return it unchanged
return full_name;
}
//static
std::string LLCacheName::buildLegacyName(const std::string& complete_name)
{
// regexp doesn't play nice with unicode, chop off the display name
S32 open_paren = complete_name.rfind(" (");
if (open_paren == std::string::npos)
{
return complete_name;
}
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;
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 (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;
}
// This is a little bit kludgy. LLCacheNameCallback is a slot instead of a function pointer.
// The reason it is a slot is so that the legacy get() function below can bind an old callback
// and pass it as a slot. The reason it isn't a boost::function is so that trackable behavior
@ -580,7 +594,7 @@ BOOL LLCacheName::getUUID(const std::string& fullname, LLUUID& id)
// we call it immediately. -Steve
// NOTE: Even though passing first and last name is a bit of extra overhead, it eliminates the
// potential need for any parsing should any code need to handle first and last name independently.
boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, const LLCacheNameCallback& callback)
boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback)
{
boost::signals2::connection res;
@ -588,7 +602,7 @@ boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, co
{
LLCacheNameSignal signal;
signal.connect(callback);
signal(id, sCacheName["nobody"], "", is_group);
signal(id, sCacheName["nobody"], is_group);
return res;
}
@ -600,11 +614,13 @@ boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, co
// id found in map therefore we can call the callback immediately.
if (entry->mIsGroup)
{
signal(id, entry->mGroupName, "", entry->mIsGroup);
signal(id, entry->mGroupName, entry->mIsGroup);
}
else
{
signal(id, entry->mFirstName, entry->mLastName, entry->mIsGroup);
std::string fullname =
buildFullName(entry->mFirstName, entry->mLastName);
signal(id, fullname, entry->mIsGroup);
}
}
else
@ -626,9 +642,15 @@ boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, co
return res;
}
boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, old_callback_t callback, void* user_data)
boost::signals2::connection LLCacheName::getGroup(const LLUUID& group_id,
const LLCacheNameCallback& callback)
{
return get(id, is_group, boost::bind(callback, _1, _2, _3, _4, user_data));
return get(group_id, true, callback);
}
boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data)
{
return get(id, is_group, boost::bind(callback, _1, _2, _3, user_data));
}
void LLCacheName::processPending()
@ -700,7 +722,7 @@ void LLCacheName::dump()
{
llinfos
<< iter->first << " = "
<< entry->mFirstName << " " << entry->mLastName
<< buildFullName(entry->mFirstName, entry->mLastName)
<< " @ " << entry->mCreateTime
<< llendl;
}
@ -719,12 +741,24 @@ void LLCacheName::dumpStats()
<< llendl;
}
void LLCacheName::clear()
{
for_each(impl.mCache.begin(), impl.mCache.end(), DeletePairedPointer());
impl.mCache.clear();
}
//static
std::string LLCacheName::getDefaultName()
{
return sCacheName["waiting"];
}
//static
std::string LLCacheName::getDefaultLastName()
{
return "Resident";
}
void LLCacheName::Impl::processPendingAsks()
{
LLMemType mt_ppa(LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS);
@ -746,11 +780,13 @@ void LLCacheName::Impl::processPendingReplies()
if (!entry->mIsGroup)
{
(reply->mSignal)(reply->mID, entry->mFirstName, entry->mLastName, FALSE);
std::string fullname =
LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
(reply->mSignal)(reply->mID, fullname, false);
}
else
{
(reply->mSignal)(reply->mID, entry->mGroupName, "", TRUE);
(reply->mSignal)(reply->mID, entry->mGroupName, true);
}
}
@ -921,13 +957,27 @@ void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool isGroup)
if (!isGroup)
{
mSignal(id, entry->mFirstName, entry->mLastName, FALSE);
std::string fullname = entry->mFirstName + " " + entry->mLastName;
mReverseCache[fullname] = id;
// NOTE: Very occasionally the server sends down a full name
// in the first name field with an empty last name, for example,
// first = "Ladanie1 Resident", last = "".
// I cannot reproduce this, nor can I find a bug in the server code.
// Ensure "Resident" does not appear via cleanFullName, because
// buildFullName only checks last name. JC
std::string full_name;
if (entry->mLastName.empty())
{
full_name = cleanFullName(entry->mFirstName);
}
else
{
full_name = LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
}
mSignal(id, full_name, false);
mReverseCache[full_name] = id;
}
else
{
mSignal(id, entry->mGroupName, "", TRUE);
mSignal(id, entry->mGroupName, true);
mReverseCache[entry->mGroupName] = id;
}
}
@ -956,4 +1006,3 @@ void LLCacheName::Impl::handleUUIDGroupNameReply(LLMessageSystem* msg, void** us
{
((LLCacheName::Impl*)userData)->processUUIDReply(msg, true);
}

View File

@ -36,13 +36,12 @@ class LLUUID;
typedef boost::signals2::signal<void (const LLUUID& id,
const std::string& first_name,
const std::string& last_name,
BOOL is_group)> LLCacheNameSignal;
const std::string& name,
bool is_group)> LLCacheNameSignal;
typedef LLCacheNameSignal::slot_type LLCacheNameCallback;
// Old callback with user data for compatability
typedef void (*old_callback_t)(const LLUUID&, const std::string&, const std::string&, BOOL, void*);
typedef void (*old_callback_t)(const LLUUID&, const std::string&, bool, void*);
// Here's the theory:
// If you request a name that isn't in the cache, it returns "waiting"
@ -65,24 +64,37 @@ public:
boost::signals2::connection addObserver(const LLCacheNameCallback& callback);
// janky old format. Remove after a while. Phoenix. 2008-01-30
void importFile(LLFILE* fp);
// storing cache on disk; for viewer, in name.cache
bool importFile(std::istream& istr);
void exportFile(std::ostream& ostr);
// If available, copies the first and last name into the strings provided.
// first must be at least DB_FIRST_NAME_BUF_SIZE characters.
// last must be at least DB_LAST_NAME_BUF_SIZE characters.
// If available, copies name ("bobsmith123" or "James Linden") into string
// If not available, copies the string "waiting".
// Returns TRUE iff available.
BOOL getName(const LLUUID& id, std::string& first, std::string& last);
BOOL getFullName(const LLUUID& id, std::string& fullname);
BOOL getFullName(const LLUUID& id, std::string& full_name);
// Reverse lookup of UUID from name
BOOL getUUID(const std::string& first, const std::string& last, LLUUID& id);
BOOL getUUID(const std::string& fullname, LLUUID& id);
// IDEVO Temporary code
// Clean up new-style "bobsmith123 Resident" names to "bobsmith123" for display
static std::string buildFullName(const std::string& first, const std::string& last);
// Clean up legacy "bobsmith123 Resident" to "bobsmith123"
// If name does not contain "Resident" returns it unchanged.
static std::string cleanFullName(const std::string& full_name);
// Converts a standard legacy name to a username
// "bobsmith123 Resident" -> "bobsmith"
// "Random Linden" -> "random.linden"
static std::string buildUsername(const std::string& name);
// Converts a complete display name to a legacy name
// if possible, otherwise returns the input
// "Alias (random.linden)" -> "Random Linden"
// "Something random" -> "Something random"
static std::string buildLegacyName(const std::string& name);
// If available, this method copies the group name into the string
// provided. The caller must allocate at least
@ -94,10 +106,15 @@ public:
// If the data is currently available, may call the callback immediatly
// otherwise, will request the data, and will call the callback when
// available. There is no garuntee the callback will ever be called.
boost::signals2::connection get(const LLUUID& id, BOOL is_group, const LLCacheNameCallback& callback);
boost::signals2::connection get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback);
// Convenience method for looking up a group name, so you can
// tell the difference between avatar lookup and group lookup
// in global searches
boost::signals2::connection getGroup(const LLUUID& group_id, const LLCacheNameCallback& callback);
// LEGACY
boost::signals2::connection get(const LLUUID& id, BOOL is_group, old_callback_t callback, void* user_data);
boost::signals2::connection get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data);
// This method needs to be called from time to time to send out
// requests.
void processPending();
@ -108,9 +125,15 @@ public:
// Debugging
void dump(); // Dumps the contents of the cache
void dumpStats(); // Dumps the sizes of the cache and associated queues.
void clear(); // Deletes all entries from the cache
static std::string getDefaultName();
static void LocalizeCacheName(std::string key, std::string value);
// Returns "Resident", the default last name for SLID-based accounts
// that have no last name.
static std::string getDefaultLastName();
static void localizeCacheName(std::string key, std::string value);
static std::map<std::string, std::string> sCacheName;
private:

View File

@ -55,7 +55,7 @@ public:
LLMeanCollisionData(LLMeanCollisionData *mcd)
: mVictim(mcd->mVictim), mPerp(mcd->mPerp), mTime(mcd->mTime), mType(mcd->mType), mMag(mcd->mMag),
mFirstName(mcd->mFirstName), mLastName(mcd->mLastName)
mFullName(mcd->mFullName)
{
}
@ -89,8 +89,7 @@ public:
time_t mTime;
EMeanCollisionType mType;
F32 mMag;
std::string mFirstName;
std::string mLastName;
std::string mFullName;
};

View File

@ -0,0 +1,102 @@
/**
* @file llavatarnamecache_test.cpp
* @author James Cook
* @brief LLAvatarNameCache test cases.
*
* $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 "../llavatarnamecache.h"
#include "../test/lltut.h"
namespace tut
{
struct avatarnamecache_data
{
};
typedef test_group<avatarnamecache_data> avatarnamecache_test;
typedef avatarnamecache_test::object avatarnamecache_object;
tut::avatarnamecache_test avatarnamecache_testcase("LLAvatarNameCache");
template<> template<>
void avatarnamecache_object::test<1>()
{
bool valid = false;
S32 max_age = 0;
valid = max_age_from_cache_control("max-age=3600", &max_age);
ensure("typical input valid", valid);
ensure_equals("typical input parsed", max_age, 3600);
valid = max_age_from_cache_control(
" max-age=600 , no-cache,private=\"stuff\" ", &max_age);
ensure("complex input valid", valid);
ensure_equals("complex input parsed", max_age, 600);
valid = max_age_from_cache_control(
"no-cache, max-age = 123 ", &max_age);
ensure("complex input 2 valid", valid);
ensure_equals("complex input 2 parsed", max_age, 123);
}
template<> template<>
void avatarnamecache_object::test<2>()
{
bool valid = false;
S32 max_age = -1;
valid = max_age_from_cache_control("", &max_age);
ensure("empty input returns invalid", !valid);
ensure_equals("empty input doesn't change val", max_age, -1);
valid = max_age_from_cache_control("no-cache", &max_age);
ensure("no max-age field returns invalid", !valid);
valid = max_age_from_cache_control("max", &max_age);
ensure("just 'max' returns invalid", !valid);
valid = max_age_from_cache_control("max-age", &max_age);
ensure("partial max-age is invalid", !valid);
valid = max_age_from_cache_control("max-age=", &max_age);
ensure("longer partial max-age is invalid", !valid);
valid = max_age_from_cache_control("max-age=FOO", &max_age);
ensure("invalid integer max-age is invalid", !valid);
valid = max_age_from_cache_control("max-age 234", &max_age);
ensure("space separated max-age is invalid", !valid);
valid = max_age_from_cache_control("max-age=0", &max_age);
ensure("zero max-age is valid", valid);
// *TODO: Handle "0000" as zero
//valid = max_age_from_cache_control("max-age=0000", &max_age);
//ensure("multi-zero max-age is valid", valid);
valid = max_age_from_cache_control("max-age=-123", &max_age);
ensure("less than zero max-age is invalid", !valid);
}
}

View File

@ -51,6 +51,14 @@ set(llplugin_HEADER_FILES
set_source_files_properties(${llplugin_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
if(NOT WORD_SIZE EQUAL 32)
if(WINDOWS)
add_definitions(/FIXED:NO)
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
add_definitions(-fPIC)
endif(WINDOWS)
endif(NOT WORD_SIZE EQUAL 32)
list(APPEND llplugin_SOURCE_FILES ${llplugin_HEADER_FILES})
add_library (llplugin ${llplugin_SOURCE_FILES})

View File

@ -74,6 +74,7 @@ bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::s
// Queue up the media init message -- it will be sent after all the currently queued messages.
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "init");
message.setValue("target", mTarget);
sendMessage(message);
mPlugin->init(launcher_filename, plugin_filename, debug);
@ -143,7 +144,7 @@ void LLPluginClassMedia::reset()
mProgressPercent = 0;
mClickURL.clear();
mClickTarget.clear();
mClickTargetType = TARGET_NONE;
mClickUUID.clear();
// media_time class
mCurrentTime = 0.0f;
@ -669,6 +670,18 @@ F64 LLPluginClassMedia::getCPUUsage()
return result;
}
void LLPluginClassMedia::sendPickFileResponse(const std::string &file)
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file_response");
message.setValue("file", file);
if(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::cut()
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_cut");
@ -715,24 +728,9 @@ void LLPluginClassMedia::setJavascriptEnabled(const bool enabled)
sendMessage(message);
}
LLPluginClassMedia::ETargetType getTargetTypeFromLLQtWebkit(int target_type)
void LLPluginClassMedia::setTarget(const std::string &target)
{
// convert a LinkTargetType value from llqtwebkit to an ETargetType
// so that we don't expose the llqtwebkit header in viewer code
switch (target_type)
{
case LLQtWebKit::LTT_TARGET_NONE:
return LLPluginClassMedia::TARGET_NONE;
case LLQtWebKit::LTT_TARGET_BLANK:
return LLPluginClassMedia::TARGET_BLANK;
case LLQtWebKit::LTT_TARGET_EXTERNAL:
return LLPluginClassMedia::TARGET_EXTERNAL;
default:
return LLPluginClassMedia::TARGET_OTHER;
}
mTarget = target;
}
/* virtual */
@ -945,6 +943,10 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
mMediaName = message.getValue("name");
mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_NAME_CHANGED);
}
else if(message_name == "pick_file")
{
mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_PICK_FILE_REQUEST);
}
else
{
LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL;
@ -987,15 +989,13 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
{
mClickURL = message.getValue("uri");
mClickTarget = message.getValue("target");
U32 target_type = message.getValueU32("target_type");
mClickTargetType = ::getTargetTypeFromLLQtWebkit(target_type);
mClickUUID = message.getValue("uuid");
mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_HREF);
}
else if(message_name == "click_nofollow")
{
mClickURL = message.getValue("uri");
mClickTarget.clear();
mClickTargetType = TARGET_NONE;
mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_NOFOLLOW);
}
else if(message_name == "cookie_set")
@ -1005,6 +1005,20 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
mOwner->handleCookieSet(this, message.getValue("cookie"));
}
}
else if(message_name == "close_request")
{
mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLOSE_REQUEST);
}
else if(message_name == "geometry_change")
{
mClickUUID = message.getValue("uuid");
mGeometryX = message.getValueS32("x");
mGeometryY = message.getValueS32("y");
mGeometryWidth = message.getValueS32("width");
mGeometryHeight = message.getValueS32("height");
mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_GEOMETRY_CHANGE);
}
else
{
LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL;
@ -1159,6 +1173,25 @@ void LLPluginClassMedia::setBrowserUserAgent(const std::string& user_agent)
sendMessage(message);
}
void LLPluginClassMedia::proxyWindowOpened(const std::string &target, const std::string &uuid)
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "proxy_window_opened");
message.setValue("target", target);
message.setValue("uuid", uuid);
sendMessage(message);
}
void LLPluginClassMedia::proxyWindowClosed(const std::string &uuid)
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "proxy_window_closed");
message.setValue("uuid", uuid);
sendMessage(message);
}
void LLPluginClassMedia::crashPlugin()
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "crash");

View File

@ -156,6 +156,8 @@ public:
void setLowPrioritySizeLimit(int size);
F64 getCPUUsage();
void sendPickFileResponse(const std::string &file);
// Valid after a MEDIA_EVENT_CURSOR_CHANGED event
std::string getCursorName() const { return mCursorName; };
@ -176,7 +178,8 @@ public:
void setLanguageCode(const std::string &language_code);
void setPluginsEnabled(const bool enabled);
void setJavascriptEnabled(const bool enabled);
void setTarget(const std::string &target);
///////////////////////////////////
// media browser class functions
bool pluginSupportsMediaBrowser(void);
@ -193,6 +196,8 @@ public:
void browse_back();
void set_status_redirect(int code, const std::string &url);
void setBrowserUserAgent(const std::string& user_agent);
void proxyWindowOpened(const std::string &target, const std::string &uuid);
void proxyWindowClosed(const std::string &uuid);
// This is valid after MEDIA_EVENT_NAVIGATE_BEGIN or MEDIA_EVENT_NAVIGATE_COMPLETE
std::string getNavigateURI() const { return mNavigateURI; };
@ -218,16 +223,14 @@ public:
// This is valid after MEDIA_EVENT_CLICK_LINK_HREF
std::string getClickTarget() const { return mClickTarget; };
typedef enum
{
TARGET_NONE, // empty href target string
TARGET_BLANK, // target to open link in user's preferred browser
TARGET_EXTERNAL, // target to open link in external browser
TARGET_OTHER // nonempty and unsupported target type
}ETargetType;
// This is valid after MEDIA_EVENT_CLICK_LINK_HREF
ETargetType getClickTargetType() const { return mClickTargetType; };
// This is valid during MEDIA_EVENT_CLICK_LINK_HREF and MEDIA_EVENT_GEOMETRY_CHANGE
std::string getClickUUID() const { return mClickUUID; };
// These are valid during MEDIA_EVENT_GEOMETRY_CHANGE
S32 getGeometryX() const { return mGeometryX; };
S32 getGeometryY() const { return mGeometryY; };
S32 getGeometryWidth() const { return mGeometryWidth; };
S32 getGeometryHeight() const { return mGeometryHeight; };
std::string getMediaName() const { return mMediaName; };
std::string getMediaDescription() const { return mMediaDescription; };
@ -347,6 +350,8 @@ protected:
LLColor4 mBackgroundColor;
std::string mTarget;
/////////////////////////////////////////
// media_browser class
std::string mNavigateURI;
@ -359,7 +364,11 @@ protected:
std::string mLocation;
std::string mClickURL;
std::string mClickTarget;
ETargetType mClickTargetType;
std::string mClickUUID;
S32 mGeometryX;
S32 mGeometryY;
S32 mGeometryWidth;
S32 mGeometryHeight;
/////////////////////////////////////////
// media_time class

View File

@ -54,6 +54,9 @@ public:
MEDIA_EVENT_LOCATION_CHANGED, // browser location (URL) has changed (maybe due to internal navagation/frames/etc)
MEDIA_EVENT_CLICK_LINK_HREF, // I'm not entirely sure what the semantics of these two are
MEDIA_EVENT_CLICK_LINK_NOFOLLOW,
MEDIA_EVENT_CLOSE_REQUEST, // The plugin requested its window be closed (currently hooked up to javascript window.close in webkit)
MEDIA_EVENT_PICK_FILE_REQUEST, // The plugin wants the user to pick a file
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

View File

@ -79,4 +79,6 @@ if (DARWIN)
)
endif (DARWIN)
ll_deploy_sharedlibs_command(SLPlugin)
if (LL_TESTS)
ll_deploy_sharedlibs_command(SLPlugin)
endif (LL_TESTS)

View File

@ -281,7 +281,7 @@ int main(int argc, char **argv)
}
// Check for a change in this process's frontmost window.
if(FrontWindow() != front_window)
if(GetFrontWindowOfClass(kAllWindowClasses, true) != front_window)
{
ProcessSerialNumber self = { 0, kCurrentProcess };
ProcessSerialNumber parent = { 0, kNoProcess };
@ -307,7 +307,7 @@ int main(int argc, char **argv)
}
}
if((FrontWindow() != NULL) && (front_window == NULL))
if((GetFrontWindowOfClass(kAllWindowClasses, true) != NULL) && (front_window == NULL))
{
// Opening the first window
@ -319,7 +319,7 @@ int main(int argc, char **argv)
if(layer_group)
{
SetWindowGroup(FrontWindow(), layer_group);
SetWindowGroup(GetFrontWindowOfClass(kAllWindowClasses, true), layer_group);
}
if(parent_is_front_process)
@ -328,9 +328,9 @@ int main(int argc, char **argv)
(void) SetFrontProcess( &self );
}
ActivateWindow(FrontWindow(), true);
ActivateWindow(GetFrontWindowOfClass(kAllWindowClasses, true), true);
}
else if((FrontWindow() == NULL) && (front_window != NULL))
else if((GetFrontWindowOfClass(kAllWindowClasses, true) == NULL) && (front_window != NULL))
{
// Closing the last window
@ -350,7 +350,7 @@ int main(int argc, char **argv)
window_hack_state = 2;
}
front_window = FrontWindow();
front_window = GetFrontWindowOfClass(kAllWindowClasses, true);
}
}

View File

@ -977,37 +977,43 @@ LLFontGL::VAlign LLFontGL::vAlignFromName(const std::string& name)
//static
LLFontGL* LLFontGL::getFontMonospace()
{
return getFont(LLFontDescriptor("Monospace","Monospace",0));
static LLFontGL* fontp = getFont(LLFontDescriptor("Monospace","Monospace",0));
return fontp;
}
//static
LLFontGL* LLFontGL::getFontSansSerifSmall()
{
return getFont(LLFontDescriptor("SansSerif","Small",0));
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",0));
return fontp;
}
//static
LLFontGL* LLFontGL::getFontSansSerif()
{
return getFont(LLFontDescriptor("SansSerif","Medium",0));
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Medium",0));
return fontp;
}
//static
LLFontGL* LLFontGL::getFontSansSerifBig()
{
return getFont(LLFontDescriptor("SansSerif","Large",0));
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Large",0));
return fontp;
}
//static
LLFontGL* LLFontGL::getFontSansSerifHuge()
{
return getFont(LLFontDescriptor("SansSerif","Huge",0));
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Large",0));
return fontp;
}
//static
LLFontGL* LLFontGL::getFontSansSerifBold()
{
return getFont(LLFontDescriptor("SansSerif","Medium",BOLD));
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Medium",BOLD));
return fontp;
}
//static

View File

@ -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();

View File

@ -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();
};
/*

View File

@ -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

View File

@ -368,6 +368,18 @@ void LLImageGL::restoreGL()
}
}
//static
void LLImageGL::dirtyTexOptions()
{
for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
iter != sImageList.end(); iter++)
{
LLImageGL* glimage = *iter;
glimage->mTexOptionsDirty = true;
stop_glerror();
}
}
//----------------------------------------------------------------------------
//for server side use only.
@ -1057,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);

View File

@ -64,6 +64,7 @@ public:
// Save off / restore GL textures
static void destroyGL(BOOL save_state = TRUE);
static void restoreGL();
static void dirtyTexOptions();
// Sometimes called externally for textures not using LLImageGL (should go away...)
static S32 updateBoundTexMem(const S32 mem, const S32 ncomponents, S32 category) ;

View File

@ -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);
}

View File

@ -384,8 +384,6 @@ void LLRenderTarget::flush(BOOL fetch_depth)
}
else
{
#if !LL_DARWIN
stop_glerror();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
@ -429,7 +427,6 @@ void LLRenderTarget::flush(BOOL fetch_depth)
}
}
}
#endif
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
@ -438,7 +435,6 @@ void LLRenderTarget::flush(BOOL fetch_depth)
void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1,
S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter)
{
#if !LL_DARWIN
gGL.flush();
if (!source.mFBO || !mFBO)
{
@ -477,14 +473,12 @@ void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0,
stop_glerror();
}
}
#endif
}
//static
void LLRenderTarget::copyContentsToFramebuffer(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1,
S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter)
{
#if !LL_DARWIN
if (!source.mFBO)
{
llerrs << "Cannot copy framebuffer contents for non FBO render targets." << llendl;
@ -501,7 +495,6 @@ void LLRenderTarget::copyContentsToFramebuffer(LLRenderTarget& source, S32 srcX0
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
stop_glerror();
}
#endif
}
BOOL LLRenderTarget::isComplete() const
@ -646,7 +639,6 @@ void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, BOOL depth
void LLMultisampleBuffer::addColorAttachment(U32 color_fmt)
{
#if !LL_DARWIN
if (color_fmt == 0)
{
return;
@ -687,12 +679,10 @@ void LLMultisampleBuffer::addColorAttachment(U32 color_fmt)
}
mTex.push_back(tex);
#endif
}
void LLMultisampleBuffer::allocateDepth()
{
#if !LL_DARWIN
glGenRenderbuffersEXT(1, (GLuint* ) &mDepth);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, mDepth);
if (mStencil)
@ -703,6 +693,5 @@ void LLMultisampleBuffer::allocateDepth()
{
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, mSamples, GL_DEPTH_COMPONENT16_ARB, mResX, mResY);
}
#endif
}

View File

@ -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)

View File

@ -158,6 +158,7 @@ set(llui_HEADER_FILES
llnotifications.h
llnotificationslistener.h
llnotificationsutil.h
llnotificationtemplate.h
llpanel.h
llprogressbar.h
llradiogroup.h

View File

@ -83,7 +83,7 @@ LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
mSingleExpansion = false;
mFitParent = false;
LLUICtrlFactory::getInstance()->buildPanel(this, "accordion_parent.xml");
buildFromFile( "accordion_parent.xml");
}
//---------------------------------------------------------------------------------

View File

@ -141,6 +141,7 @@ LLAccordionCtrlTab::LLAccordionCtrlTabHeader::LLAccordionCtrlTabHeader(
textboxParams.use_ellipses = true;
textboxParams.bg_visible = false;
textboxParams.mouse_opaque = false;
textboxParams.parse_urls = false;
mHeaderTextbox = LLUICtrlFactory::create<LLTextBox>(textboxParams);
addChild(mHeaderTextbox);
}
@ -455,8 +456,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
@ -508,10 +508,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
{
@ -521,57 +520,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);
}
}
@ -775,8 +768,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)
@ -829,15 +821,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()
{

View File

@ -85,10 +85,10 @@ public:
Optional<bool> selection_enabled;
Optional<S32> padding_left;
Optional<S32> padding_right;
Optional<S32> padding_top;
Optional<S32> padding_bottom;
Optional<S32> padding_left,
padding_right,
padding_top,
padding_bottom;
Params();
};
@ -170,7 +170,7 @@ public:
virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks );
virtual bool addChild(LLView* child, S32 tab_group);
virtual bool addChild(LLView* child, S32 tab_group = 0 );
bool isExpanded() const { return mDisplayChildren; }

View File

@ -50,9 +50,7 @@ template class LLCheckBoxCtrl* LLView::getChild<class LLCheckBoxCtrl>(
const std::string& name, BOOL recurse) const;
LLCheckBoxCtrl::Params::Params()
: text_enabled_color("text_enabled_color"),
text_disabled_color("text_disabled_color"),
initial_value("initial_value", false),
: initial_value("initial_value", false),
label_text("label_text"),
check_button("check_button"),
radio_style("radio_style")
@ -61,8 +59,8 @@ LLCheckBoxCtrl::Params::Params()
LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
: LLUICtrl(p),
mTextEnabledColor(p.text_enabled_color()),
mTextDisabledColor(p.text_disabled_color()),
mTextEnabledColor(p.label_text.text_color()),
mTextDisabledColor(p.label_text.text_readonly_color()),
mFont(p.font())
{
mViewModel->setValue(LLSD(p.initial_value));
@ -89,7 +87,6 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
{
tbparams.font(p.font);
}
tbparams.text_color( p.enabled() ? p.text_enabled_color() : p.text_disabled_color() );
mLabel = LLUICtrlFactory::create<LLTextBox> (tbparams);
addChild(mLabel);

View File

@ -52,8 +52,6 @@ public:
struct Params
: public LLInitParam::Block<Params, LLUICtrl::Params>
{
Optional<LLUIColor> text_enabled_color;
Optional<LLUIColor> text_disabled_color;
Optional<bool> initial_value; // override LLUICtrl initial_value
Optional<LLTextBox::Params> label_text;

View File

@ -52,8 +52,6 @@
#include "lltooltip.h"
// Globals
S32 LLCOMBOBOX_HEIGHT = 0;
S32 LLCOMBOBOX_WIDTH = 0;
S32 MAX_COMBO_WIDTH = 500;
static LLDefaultChildRegistry::Register<LLComboBox> register_combo_box("combo_box");
@ -139,8 +137,8 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p)
// Grab the mouse-up event and make sure the button state is correct
mList->setMouseUpCallback(boost::bind(&LLComboBox::onListMouseUp, this));
for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items().begin();
it != p.items().end();
for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items.begin();
it != p.items.end();
++it)
{
LLScrollListItem::Params item_params = *it;
@ -486,7 +484,7 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p)
LLLineEditor::Params params = p.combo_editor;
params.rect(text_entry_rect);
params.default_text(LLStringUtil::null);
params.max_length_bytes(mMaxChars);
params.max_length.bytes(mMaxChars);
params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2));
params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1));
params.commit_on_focus_lost(false);
@ -705,10 +703,10 @@ void LLComboBox::onItemSelected(const LLSD& data)
setLabel(getSelectedItemLabel());
if (mAllowTextEntry)
{
gFocusMgr.setKeyboardFocus(mTextEntry);
mTextEntry->selectAll();
}
{
gFocusMgr.setKeyboardFocus(mTextEntry);
mTextEntry->selectAll();
}
}
// hiding the list reasserts the old value stored in the text editor/dropdown button
hideList();

View File

@ -43,9 +43,6 @@
class LLFontGL;
class LLViewBorder;
extern S32 LLCOMBOBOX_HEIGHT;
extern S32 LLCOMBOBOX_WIDTH;
class LLComboBox
: public LLUICtrl, public LLCtrlListInterface
{
@ -224,8 +221,8 @@ private:
commit_callback_t mPrearrangeCallback;
commit_callback_t mTextEntryCallback;
commit_callback_t mSelectionCallback;
boost::signals2::connection mTopLostSignalConnection;
S32 mLastSelectedIndex;
boost::signals2::connection mTopLostSignalConnection;
S32 mLastSelectedIndex;
};
// A combo box with icons for the list of items.

View File

@ -36,6 +36,7 @@
#include "lluictrlfactory.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "lldir.h"
#include "lldraghandle.h"
#include "llfloaterreg.h"
#include "llfocusmgr.h"
@ -60,6 +61,9 @@
// use this to control "jumping" behavior when Ctrl-Tabbing
const S32 TABBED_FLOATER_OFFSET = 0;
// static
F32 LLFloater::sActiveFloaterTransparency = 0.0f;
F32 LLFloater::sInactiveFloaterTransparency = 0.0f;
std::string LLFloater::sButtonNames[BUTTON_COUNT] =
{
@ -199,6 +203,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));
sActiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency");
}
ctrl = LLUI::sSettingGroups["config"]->getControl("InactiveFloaterTransparency").get();
if (ctrl)
{
ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency));
sInactiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency");
}
}
// defaults for floater param block pulled from widgets/floater.xml
@ -346,6 +365,18 @@ void LLFloater::layoutDragHandle()
updateTitleButtons();
}
// static
void LLFloater::updateActiveFloaterTransparency()
{
sActiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency");
}
// static
void LLFloater::updateInactiveFloaterTransparency()
{
sInactiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency");
}
void LLFloater::addResizeCtrls()
{
// Resize bars (sides)
@ -1621,7 +1652,8 @@ void LLFloater::onClickCloseBtn()
// virtual
void LLFloater::draw()
{
F32 alpha = getDrawContext().mAlpha;
mCurrentTransparency = hasFocus() ? sActiveFloaterTransparency : sInactiveFloaterTransparency;
// draw background
if( isBackgroundVisible() )
{
@ -1652,12 +1684,12 @@ void LLFloater::draw()
if (image)
{
// We're using images for this floater's backgrounds
image->draw(getLocalRect(), overlay_color % alpha);
image->draw(getLocalRect(), overlay_color % mCurrentTransparency);
}
else
{
// We're not using images, use old-school flat colors
gl_rect_2d( left, top, right, bottom, color % alpha );
gl_rect_2d( left, top, right, bottom, color % mCurrentTransparency );
// draw highlight on title bar to indicate focus. RDW
if(hasFocus()
@ -1669,7 +1701,7 @@ void LLFloater::draw()
const LLFontGL* font = LLFontGL::getFontSansSerif();
LLRect r = getRect();
gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1,
titlebar_focus_color % alpha, 0, TRUE);
titlebar_focus_color % mCurrentTransparency, 0, TRUE);
}
}
}
@ -1719,7 +1751,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;
@ -1736,7 +1767,7 @@ void LLFloater::drawShadow(LLPanel* panel)
shadow_color.mV[VALPHA] *= 0.5f;
}
gl_drop_shadow(left, top, right, bottom,
shadow_color % alpha,
shadow_color % mCurrentTransparency,
llround(shadow_offset));
}
@ -2829,7 +2860,8 @@ LLFastTimer::DeclareTimer POST_BUILD("Floater Post Build");
bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node)
{
Params params(LLUICtrlFactory::getDefaultParams<LLFloater>());
LLXUIParser::instance().readXUI(node, params, filename); // *TODO: Error checking
LLXUIParser parser;
parser.readXUI(node, params, filename); // *TODO: Error checking
if (output_node)
{
@ -2837,8 +2869,7 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str
setupParamsForExport(output_params, parent);
Params default_params(LLUICtrlFactory::getDefaultParams<LLFloater>());
output_node->setName(node->getName()->mString);
LLXUIParser::instance().writeXUI(
output_node, output_params, &default_params);
parser.writeXUI(output_node, output_params, &default_params);
}
// Default floater position to top-left corner of screen
@ -2933,3 +2964,64 @@ bool LLFloater::isVisible(const LLFloater* floater)
{
return floater && floater->getVisible();
}
static LLFastTimer::DeclareTimer FTM_BUILD_FLOATERS("Build Floaters");
bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_node)
{
LLFastTimer timer(FTM_BUILD_FLOATERS);
LLXMLNodePtr root;
//if exporting, only load the language being exported,
//instead of layering localized version on top of english
if (output_node)
{
if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root))
{
llwarns << "Couldn't parse floater from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl;
return false;
}
}
else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
{
llwarns << "Couldn't parse floater from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl;
return false;
}
// root must be called floater
if( !(root->hasName("floater") || root->hasName("multi_floater")) )
{
llwarns << "Root node should be named floater in: " << filename << llendl;
return false;
}
bool res = true;
lldebugs << "Building floater " << filename << llendl;
LLUICtrlFactory::instance().pushFileName(filename);
{
if (!getFactoryMap().empty())
{
LLPanel::sFactoryStack.push_front(&getFactoryMap());
}
// for local registry callbacks; define in constructor, referenced in XUI or postBuild
getCommitCallbackRegistrar().pushScope();
getEnableCallbackRegistrar().pushScope();
res = initFloaterXML(root, getParent(), filename, output_node);
setXMLFilename(filename);
getCommitCallbackRegistrar().popScope();
getEnableCallbackRegistrar().popScope();
if (!getFactoryMap().empty())
{
LLPanel::sFactoryStack.pop_front();
}
}
LLUICtrlFactory::instance().popFileName();
return res;
}

View File

@ -141,6 +141,7 @@ public:
// Don't export top/left for rect, only height/width
static void setupParamsForExport(Params& p, LLView* parent);
bool buildFromFile(const std::string &filename, LLXMLNodePtr output_node = NULL);
boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb );
@ -340,6 +341,9 @@ private:
void addDragHandle();
void layoutDragHandle(); // repair layout
static void updateActiveFloaterTransparency();
static void updateInactiveFloaterTransparency();
public:
// Called when floater is opened, passes mKey
// Public so external views or floaters can watch for this floater opening
@ -407,6 +411,11 @@ private:
bool mDocked;
bool mTornOff;
F32 mCurrentTransparency;
static F32 sActiveFloaterTransparency;
static F32 sInactiveFloaterTransparency;
static LLMultiFloater* sHostp;
static BOOL sQuitting;
static std::string sButtonNames[BUTTON_COUNT];

View File

@ -121,7 +121,7 @@ LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key)
res = build_func(key);
bool success = LLUICtrlFactory::getInstance()->buildFloater(res, xui_file, NULL);
bool success = res->buildFromFile(xui_file, NULL);
if (!success)
{
llwarns << "Failed to build floater type: '" << name << "'." << llendl;

View File

@ -35,95 +35,66 @@
#include "llresizebar.h"
#include "llcriticaldamp.h"
static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack", &LLLayoutStack::fromXML);
static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack");
static LLLayoutStack::LayoutStackRegistry::Register<LLLayoutPanel> register_layout_panel("layout_panel");
//
// LLLayoutStack
// LLLayoutPanel
//
struct LLLayoutStack::LayoutPanel
{
LayoutPanel(LLPanel* panelp, ELayoutOrientation orientation, S32 min_width, S32 min_height, S32 max_width, S32 max_height, BOOL auto_resize, BOOL user_resize) : mPanel(panelp),
mMinWidth(min_width),
mMinHeight(min_height),
mMaxWidth(max_width),
mMaxHeight(max_height),
mAutoResize(auto_resize),
mUserResize(user_resize),
mOrientation(orientation),
LLLayoutPanel::LLLayoutPanel(const Params& p)
: LLPanel(p),
mMinDim(p.min_dim),
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)
{
LLResizeBar::Side side = (orientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM;
LLRect resize_bar_rect = panelp->getRect();
// panels initialized as hidden should not start out partially visible
if (!getVisible())
{
mVisibleAmt = 0.f;
}
}
S32 min_dim;
if (orientation == HORIZONTAL)
void LLLayoutPanel::initFromParams(const Params& p)
{
min_dim = mMinHeight;
}
else
{
min_dim = mMinWidth;
}
LLResizeBar::Params p;
p.name("resize");
p.resizing_view(mPanel);
p.min_size(min_dim);
p.side(side);
p.snapping_enabled(false);
mResizeBar = LLUICtrlFactory::create<LLResizeBar>(p);
// panels initialized as hidden should not start out partially visible
if (!mPanel->getVisible())
{
mVisibleAmt = 0.f;
}
LLPanel::initFromParams(p);
setFollowsNone();
}
~LayoutPanel()
LLLayoutPanel::~LLLayoutPanel()
{
// probably not necessary, but...
delete mResizeBar;
mResizeBar = NULL;
}
F32 getCollapseFactor()
F32 LLLayoutPanel::getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation)
{
if (mOrientation == HORIZONTAL)
if (orientation == LLLayoutStack::HORIZONTAL)
{
F32 collapse_amt =
clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinWidth / (F32)llmax(1, mPanel->getRect().getWidth()));
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)mMinHeight / (F32)llmax(1, mPanel->getRect().getHeight())));
clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinDim / (F32)llmax(1, getRect().getHeight())));
return mVisibleAmt * collapse_amt;
}
}
LLPanel* mPanel;
S32 mMinWidth;
S32 mMinHeight;
// mMaxWidth & mMaxHeight are added to make configurable max width of the nearby chat bar. EXT-5589
// they are not processed by LLLayoutStack but they can be if necessary
S32 mMaxWidth;
S32 mMaxHeight;
BOOL mAutoResize;
BOOL mUserResize;
BOOL mCollapsed;
LLResizeBar* mResizeBar;
ELayoutOrientation mOrientation;
F32 mVisibleAmt;
F32 mCollapseAmt;
};
//
// LLLayoutStack
//
LLLayoutStack::Params::Params()
: orientation("orientation", std::string("vertical")),
: orientation("orientation"),
animate("animate", true),
clip("clip", true),
border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0))
@ -157,18 +128,18 @@ void LLLayoutStack::draw()
for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
// clip to layout rectangle, not bounding rectangle
LLRect clip_rect = (*panel_it)->mPanel->getRect();
LLRect clip_rect = (*panel_it)->getRect();
// scale clipping rectangle by visible amount
if (mOrientation == HORIZONTAL)
{
clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->getCollapseFactor());
clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->getCollapseFactor(mOrientation));
}
else
{
clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->getCollapseFactor());
clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->getCollapseFactor(mOrientation));
}
LLPanel* panelp = (*panel_it)->mPanel;
LLPanel* panelp = (*panel_it);
LLLocalClipRect clip(clip_rect, mClip);
// only force drawing invisible children if visible amount is non-zero
@ -179,7 +150,7 @@ void LLLayoutStack::draw()
void LLLayoutStack::removeChild(LLView* view)
{
LayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast<LLPanel*>(view));
LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast<LLPanel*>(view));
if (embedded_panelp)
{
@ -200,149 +171,16 @@ BOOL LLLayoutStack::postBuild()
return TRUE;
}
static void get_attribute_s32_and_write(LLXMLNodePtr node,
const char* name,
S32 *value,
S32 default_value,
LLXMLNodePtr output_child)
{
BOOL has_attr = node->getAttributeS32(name, *value);
if (has_attr && *value != default_value && output_child)
{
// create an attribute child node
LLXMLNodePtr child_attr = output_child->createChild(name, TRUE);
child_attr->setIntValue(*value);
}
}
static void get_attribute_bool_and_write(LLXMLNodePtr node,
const char* name,
BOOL *value,
BOOL default_value,
LLXMLNodePtr output_child)
{
BOOL has_attr = node->getAttributeBOOL(name, *value);
if (has_attr && *value != default_value && output_child)
{
LLXMLNodePtr child_attr = output_child->createChild(name, TRUE);
child_attr->setBoolValue(*value);
}
}
//static
LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)
{
LLLayoutStack::Params p(LLUICtrlFactory::getDefaultParams<LLLayoutStack>());
LLXUIParser::instance().readXUI(node, p, LLUICtrlFactory::getInstance()->getCurFileName());
// Export must happen before setupParams() mungles rectangles and before
// this item gets added to parent (otherwise screws up last_child_rect
// logic). JC
if (output_node)
{
Params output_params(p);
setupParamsForExport(output_params, parent);
LLLayoutStack::Params default_params(LLUICtrlFactory::getDefaultParams<LLLayoutStack>());
output_node->setName(node->getName()->mString);
LLXUIParser::instance().writeXUI(
output_node, output_params, &default_params);
}
p.from_xui = true;
applyXUILayout(p, parent);
LLLayoutStack* layout_stackp = LLUICtrlFactory::create<LLLayoutStack>(p);
if (parent && layout_stackp)
{
S32 tab_group = p.tab_group.isProvided() ? p.tab_group() : parent->getLastTabGroup();
parent->addChild(layout_stackp, tab_group);
}
for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling())
{
const S32 DEFAULT_MIN_WIDTH = 0;
const S32 DEFAULT_MIN_HEIGHT = 0;
const S32 DEFAULT_MAX_WIDTH = S32_MAX;
const S32 DEFAULT_MAX_HEIGHT = S32_MAX;
const BOOL DEFAULT_AUTO_RESIZE = TRUE;
S32 min_width = DEFAULT_MIN_WIDTH;
S32 min_height = DEFAULT_MIN_HEIGHT;
S32 max_width = DEFAULT_MAX_WIDTH;
S32 max_height = DEFAULT_MAX_HEIGHT;
BOOL auto_resize = DEFAULT_AUTO_RESIZE;
LLXMLNodePtr output_child;
if (output_node)
bool LLLayoutStack::addChild(LLView* child, S32 tab_group)
{
output_child = output_node->createChild("", FALSE);
}
// Layout stack allows child nodes to acquire additional attributes,
// such as "min_width" in: <button label="Foo" min_width="100"/>
// If these attributes exist and have non-default values, write them
// to the output node.
get_attribute_s32_and_write(child_node, "min_width", &min_width,
DEFAULT_MIN_WIDTH, output_child);
get_attribute_s32_and_write(child_node, "min_height", &min_height,
DEFAULT_MIN_HEIGHT, output_child);
get_attribute_s32_and_write(child_node, "max_width", &max_width,
DEFAULT_MAX_WIDTH, output_child);
get_attribute_s32_and_write(child_node, "max_height", &max_height,
DEFAULT_MAX_HEIGHT, output_child);
get_attribute_bool_and_write(child_node, "auto_resize", &auto_resize,
DEFAULT_AUTO_RESIZE, output_child);
if (child_node->hasName("layout_panel"))
{
BOOL user_resize = TRUE;
get_attribute_bool_and_write(child_node, "user_resize", &user_resize,
TRUE, output_child);
LLPanel* panelp = (LLPanel*)LLPanel::fromXML(child_node, layout_stackp, output_child);
LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child);
if (panelp)
{
panelp->setFollowsNone();
layout_stackp->addPanel(panelp, min_width, min_height, max_width, max_height, auto_resize, user_resize);
mPanels.push_back(panelp);
}
return LLView::addChild(child, tab_group);
}
else
{
BOOL user_resize = FALSE;
get_attribute_bool_and_write(child_node, "user_resize", &user_resize,
FALSE, output_child);
LLPanel::Params p;
p.mouse_opaque(false);
LLPanel* panelp = LLUICtrlFactory::create<LLPanel>(p);
LLView* new_child = LLUICtrlFactory::getInstance()->createFromXML(child_node, panelp, LLStringUtil::null, LLPanel::child_registry_t::instance(), output_child);
if (new_child)
{
// put child in new embedded panel
layout_stackp->addPanel(panelp, min_width, min_height, max_width, max_height, auto_resize, user_resize);
// resize panel to contain widget and move widget to be contained in panel
panelp->setRect(new_child->getRect());
new_child->setOrigin(0, 0);
}
else
{
panelp->die();
}
}
if (output_child && !output_child->mChildren && output_child->mAttributes.empty() && output_child->getValue().empty())
{
output_node->deleteChild(output_child);
}
}
if (!layout_stackp->postBuild())
{
delete layout_stackp;
return NULL;
}
return layout_stackp;
}
S32 LLLayoutStack::getDefaultHeight(S32 cur_height)
{
@ -370,8 +208,8 @@ S32 LLLayoutStack::getDefaultWidth(S32 cur_width)
void LLLayoutStack::movePanel(LLPanel* panel_to_move, LLPanel* target_panel, bool move_to_front)
{
LayoutPanel* embedded_panel_to_move = findEmbeddedPanel(panel_to_move);
LayoutPanel* embedded_target_panel = move_to_front ? *mPanels.begin() : findEmbeddedPanel(target_panel);
LLLayoutPanel* embedded_panel_to_move = findEmbeddedPanel(panel_to_move);
LLLayoutPanel* embedded_target_panel = move_to_front ? *mPanels.begin() : findEmbeddedPanel(target_panel);
if (!embedded_panel_to_move || !embedded_target_panel || embedded_panel_to_move == embedded_target_panel)
{
@ -384,34 +222,14 @@ void LLLayoutStack::movePanel(LLPanel* panel_to_move, LLPanel* target_panel, boo
mPanels.insert(it, embedded_panel_to_move);
}
void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, S32 max_width, S32 max_height, BOOL auto_resize, BOOL user_resize, EAnimate animate, S32 index)
void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate)
{
addChild(panel);
// panel starts off invisible (collapsed)
if (animate == ANIMATE)
{
panel->setVisible(FALSE);
}
LayoutPanel* embedded_panel = new LayoutPanel(panel, mOrientation, min_width, min_height, max_width, max_height, auto_resize, user_resize);
mPanels.insert(mPanels.begin() + llclamp(index, 0, (S32)mPanels.size()), embedded_panel);
if (panel->getParent() != this)
{
addChild(panel);
}
addChild(embedded_panel->mResizeBar);
// bring all resize bars to the front so that they are clickable even over the panels
// with a bit of overlap
for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
LLResizeBar* resize_barp = (*panel_it)->mResizeBar;
sendChildToFront(resize_barp);
}
// start expanding panel animation
if (animate == ANIMATE)
{
panel->mVisibleAmt = 0.f;
panel->setVisible(TRUE);
}
}
@ -423,7 +241,7 @@ void LLLayoutStack::removePanel(LLPanel* panel)
void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed)
{
LayoutPanel* panel_container = findEmbeddedPanel(panel);
LLLayoutPanel* panel_container = findEmbeddedPanel(panel);
if (!panel_container) return;
panel_container->mCollapsed = collapsed;
@ -431,7 +249,7 @@ void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed)
void LLLayoutStack::updatePanelAutoResize(const std::string& panel_name, BOOL auto_resize)
{
LayoutPanel* panel = findEmbeddedPanelByName(panel_name);
LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name);
if (panel)
{
@ -441,7 +259,7 @@ void LLLayoutStack::updatePanelAutoResize(const std::string& panel_name, BOOL au
void LLLayoutStack::setPanelUserResize(const std::string& panel_name, BOOL user_resize)
{
LayoutPanel* panel = findEmbeddedPanelByName(panel_name);
LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name);
if (panel)
{
@ -449,27 +267,25 @@ void LLLayoutStack::setPanelUserResize(const std::string& panel_name, BOOL user_
}
}
bool LLLayoutStack::getPanelMinSize(const std::string& panel_name, S32* min_widthp, S32* min_heightp)
bool LLLayoutStack::getPanelMinSize(const std::string& panel_name, S32* min_dimp)
{
LayoutPanel* panel = findEmbeddedPanelByName(panel_name);
LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name);
if (panel)
{
if (min_widthp) *min_widthp = panel->mMinWidth;
if (min_heightp) *min_heightp = panel->mMinHeight;
if (min_dimp) *min_dimp = panel->mMinDim;
}
return NULL != panel;
}
bool LLLayoutStack::getPanelMaxSize(const std::string& panel_name, S32* max_widthp, S32* max_heightp)
bool LLLayoutStack::getPanelMaxSize(const std::string& panel_name, S32* max_dimp)
{
LayoutPanel* panel = findEmbeddedPanelByName(panel_name);
LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name);
if (panel)
{
if (max_widthp) *max_widthp = panel->mMaxWidth;
if (max_heightp) *max_heightp = panel->mMaxHeight;
if (max_dimp) *max_dimp = panel->mMaxDim;
}
return NULL != panel;
@ -481,6 +297,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
LLFastTimer ft(FTM_UPDATE_LAYOUT);
static LLUICachedControl<S32> resize_bar_overlap ("UIResizeBarOverlap", 0);
calcMinExtents();
createResizeBars();
// calculate current extents
S32 total_width = 0;
@ -492,7 +309,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
e_panel_list_t::iterator panel_it;
for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
LLPanel* panelp = (*panel_it)->mPanel;
LLPanel* panelp = (*panel_it);
if (panelp->getVisible())
{
if (mAnimate)
@ -542,11 +359,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
if (mOrientation == HORIZONTAL)
{
// enforce minimize size constraint by default
if (panelp->getRect().getWidth() < (*panel_it)->mMinWidth)
if (panelp->getRect().getWidth() < (*panel_it)->mMinDim)
{
panelp->reshape((*panel_it)->mMinWidth, panelp->getRect().getHeight());
panelp->reshape((*panel_it)->mMinDim, panelp->getRect().getHeight());
}
total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor());
total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor(mOrientation));
// want n-1 panel gaps for n panels
if (panel_it != mPanels.begin())
{
@ -556,11 +373,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
else //VERTICAL
{
// enforce minimize size constraint by default
if (panelp->getRect().getHeight() < (*panel_it)->mMinHeight)
if (panelp->getRect().getHeight() < (*panel_it)->mMinDim)
{
panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->mMinHeight);
panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->mMinDim);
}
total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor());
total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor(mOrientation));
if (panel_it != mPanels.begin())
{
total_height += mPanelSpacing;
@ -574,7 +391,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
// panels that are not fully visible do not count towards shrink headroom
if ((*panel_it)->getCollapseFactor() < 1.f)
if ((*panel_it)->getCollapseFactor(mOrientation) < 1.f)
{
continue;
}
@ -587,11 +404,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
{
if (mOrientation == HORIZONTAL)
{
shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth;
shrink_headroom_total += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim;
}
else //VERTICAL
{
shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight;
shrink_headroom_total += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim;
}
}
else
@ -599,13 +416,13 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
num_resizable_panels++;
if (mOrientation == HORIZONTAL)
{
shrink_headroom_available += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth;
shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth;
shrink_headroom_available += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim;
shrink_headroom_total += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim;
}
else //VERTICAL
{
shrink_headroom_available += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight;
shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight;
shrink_headroom_available += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim;
shrink_headroom_total += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim;
}
}
}
@ -628,17 +445,25 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
LLPanel* panelp = (*panel_it)->mPanel;
LLPanel* panelp = (*panel_it);
S32 cur_width = panelp->getRect().getWidth();
S32 cur_height = panelp->getRect().getHeight();
S32 new_width = llmax((*panel_it)->mMinWidth, cur_width);
S32 new_height = llmax((*panel_it)->mMinHeight, cur_height);
S32 new_width = cur_width;
S32 new_height = cur_height;
if (mOrientation == HORIZONTAL)
{
new_width = llmax((*panel_it)->mMinDim, new_width);
}
else
{
new_height = llmax((*panel_it)->mMinDim, new_height);
}
S32 delta_size = 0;
// if panel can automatically resize (not animating, and resize flag set)...
if ((*panel_it)->getCollapseFactor() == 1.f
if ((*panel_it)->getCollapseFactor(mOrientation) == 1.f
&& (force_resize || (*panel_it)->mAutoResize)
&& !(*panel_it)->mResizeBar->hasMouseCapture())
{
@ -649,8 +474,8 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
{
// shrink proportionally to amount over minimum
// so we can do this in one pass
delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - (*panel_it)->mMinWidth) / (F32)shrink_headroom_available)) : 0;
shrink_headroom_available -= (cur_width - (*panel_it)->mMinWidth);
delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - (*panel_it)->mMinDim) / (F32)shrink_headroom_available)) : 0;
shrink_headroom_available -= (cur_width - (*panel_it)->mMinDim);
}
else
{
@ -659,7 +484,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
num_resizable_panels--;
}
pixels_to_distribute -= delta_size;
new_width = llmax((*panel_it)->mMinWidth, cur_width + delta_size);
new_width = llmax((*panel_it)->mMinDim, cur_width + delta_size);
}
else
{
@ -672,8 +497,8 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
{
// shrink proportionally to amount over minimum
// so we can do this in one pass
delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - (*panel_it)->mMinHeight) / (F32)shrink_headroom_available)) : 0;
shrink_headroom_available -= (cur_height - (*panel_it)->mMinHeight);
delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - (*panel_it)->mMinDim) / (F32)shrink_headroom_available)) : 0;
shrink_headroom_available -= (cur_height - (*panel_it)->mMinDim);
}
else
{
@ -681,7 +506,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
num_resizable_panels--;
}
pixels_to_distribute -= delta_size;
new_height = llmax((*panel_it)->mMinHeight, cur_height + delta_size);
new_height = llmax((*panel_it)->mMinDim, cur_height + delta_size);
}
else
{
@ -722,11 +547,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
if (mOrientation == HORIZONTAL)
{
cur_x += llround(new_width * (*panel_it)->getCollapseFactor()) + mPanelSpacing;
cur_x += llround(new_width * (*panel_it)->getCollapseFactor(mOrientation)) + mPanelSpacing;
}
else //VERTICAL
{
cur_y -= llround(new_height * (*panel_it)->getCollapseFactor()) + mPanelSpacing;
cur_y -= llround(new_height * (*panel_it)->getCollapseFactor(mOrientation)) + mPanelSpacing;
}
}
@ -734,19 +559,19 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
LLResizeBar* last_resize_bar = NULL;
for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
LLPanel* panelp = (*panel_it)->mPanel;
LLPanel* panelp = (*panel_it);
if (mOrientation == HORIZONTAL)
{
(*panel_it)->mResizeBar->setResizeLimits(
(*panel_it)->mMinWidth,
(*panel_it)->mMinWidth + shrink_headroom_total);
(*panel_it)->mMinDim,
(*panel_it)->mMinDim + shrink_headroom_total);
}
else //VERTICAL
{
(*panel_it)->mResizeBar->setResizeLimits(
(*panel_it)->mMinHeight,
(*panel_it)->mMinHeight + shrink_headroom_total);
(*panel_it)->mMinDim,
(*panel_it)->mMinDim + shrink_headroom_total);
}
// toggle resize bars based on panel visibility, resizability, etc
@ -782,14 +607,14 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
} // end LLLayoutStack::updateLayout
LLLayoutStack::LayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
{
if (!panelp) return NULL;
e_panel_list_t::const_iterator panel_it;
for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
if ((*panel_it)->mPanel == panelp)
if ((*panel_it) == panelp)
{
return *panel_it;
}
@ -797,15 +622,15 @@ LLLayoutStack::LayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) co
return NULL;
}
LLLayoutStack::LayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const
LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const
{
LayoutPanel* result = NULL;
LLLayoutPanel* result = NULL;
for (e_panel_list_t::const_iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
LayoutPanel* p = *panel_it;
LLLayoutPanel* p = *panel_it;
if (p->mPanel->getName() == name)
if (p->getName() == name)
{
result = p;
break;
@ -826,9 +651,7 @@ void LLLayoutStack::calcMinExtents()
{
if (mOrientation == HORIZONTAL)
{
mMinHeight = llmax( mMinHeight,
(*panel_it)->mMinHeight);
mMinWidth += (*panel_it)->mMinWidth;
mMinWidth += (*panel_it)->mMinDim;
if (panel_it != mPanels.begin())
{
mMinWidth += mPanelSpacing;
@ -836,9 +659,7 @@ void LLLayoutStack::calcMinExtents()
}
else //VERTICAL
{
mMinWidth = llmax( mMinWidth,
(*panel_it)->mMinWidth);
mMinHeight += (*panel_it)->mMinHeight;
mMinHeight += (*panel_it)->mMinDim;
if (panel_it != mPanels.begin())
{
mMinHeight += mPanelSpacing;
@ -847,6 +668,37 @@ void LLLayoutStack::calcMinExtents()
}
}
void LLLayoutStack::createResizeBars()
{
for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
LLLayoutPanel* lp = (*panel_it);
if (lp->mResizeBar == NULL)
{
LLResizeBar::Side side = (mOrientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM;
LLRect resize_bar_rect = getRect();
LLResizeBar::Params resize_params;
resize_params.name("resize");
resize_params.resizing_view(lp);
resize_params.min_size(lp->mMinDim);
resize_params.side(side);
resize_params.snapping_enabled(false);
LLResizeBar* resize_bar = LLUICtrlFactory::create<LLResizeBar>(resize_params);
lp->mResizeBar = resize_bar;
LLView::addChild(resize_bar, 0);
// bring all resize bars to the front so that they are clickable even over the panels
// with a bit of overlap
for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
LLResizeBar* resize_barp = (*panel_it)->mResizeBar;
sendChildToFront(resize_barp);
}
}
}
}
// update layout stack animations, etc. once per frame
// NOTE: we use this to size world view based on animating UI, *before* we draw the UI
// we might still need to call updateLayout during UI draw phase, in case UI elements

View File

@ -28,16 +28,21 @@
#ifndef LL_LLLAYOUTSTACK_H
#define LL_LLLAYOUTSTACK_H
#include "llview.h"
#include "llpanel.h"
class LLPanel;
class LLLayoutPanel;
class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack>
{
public:
struct LayoutStackRegistry : public LLChildRegistry<LayoutStackRegistry>
{};
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
Optional<std::string> orientation;
Mandatory<std::string> orientation;
Optional<S32> border_size;
Optional<bool> animate,
clip;
@ -45,6 +50,8 @@ public:
Params();
};
typedef LayoutStackRegistry child_registry_t;
typedef enum e_layout_orientation
{
HORIZONTAL,
@ -56,6 +63,7 @@ public:
/*virtual*/ void draw();
/*virtual*/ void removeChild(LLView*);
/*virtual*/ BOOL postBuild();
/*virtual*/ bool addChild(LLView* child, S32 tab_group = 0);
static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL);
@ -68,7 +76,7 @@ public:
ANIMATE
} EAnimate;
void addPanel(LLPanel* panel, S32 min_width, S32 min_height, S32 max_width, S32 max_height, BOOL auto_resize, BOOL user_resize, EAnimate animate = NO_ANIMATE, S32 index = S32_MAX);
void addPanel(LLLayoutPanel* panel, EAnimate animate = NO_ANIMATE);
void removePanel(LLPanel* panel);
void collapsePanel(LLPanel* panel, BOOL collapsed = TRUE);
S32 getNumPanels() { return mPanels.size(); }
@ -82,20 +90,18 @@ public:
void setPanelUserResize(const std::string& panel_name, BOOL user_resize);
/**
* Gets minimal width and/or height of the specified by name panel.
* Gets minimal dimension along layout_stack axis of the specified by name panel.
*
* If it is necessary to get only the one dimension pass NULL for another one.
* @returns true if specified by panel_name internal panel exists, false otherwise.
*/
bool getPanelMinSize(const std::string& panel_name, S32* min_widthp, S32* min_heightp);
bool getPanelMinSize(const std::string& panel_name, S32* min_dimp);
/**
* Gets maximal width and/or height of the specified by name panel.
* Gets maximal dimension along layout_stack axis of the specified by name panel.
*
* If it is necessary to get only the one dimension pass NULL for another one.
* @returns true if specified by panel_name internal panel exists, false otherwise.
*/
bool getPanelMaxSize(const std::string& panel_name, S32* max_width, S32* max_height);
bool getPanelMaxSize(const std::string& panel_name, S32* max_dim);
void updateLayout(BOOL force_resize = FALSE);
@ -110,19 +116,18 @@ protected:
friend class LLUICtrlFactory;
private:
struct LayoutPanel;
void createResizeBars();
void calcMinExtents();
S32 getDefaultHeight(S32 cur_height);
S32 getDefaultWidth(S32 cur_width);
const ELayoutOrientation mOrientation;
typedef std::vector<LayoutPanel*> e_panel_list_t;
typedef std::vector<LLLayoutPanel*> e_panel_list_t;
e_panel_list_t mPanels;
LayoutPanel* findEmbeddedPanel(LLPanel* panelp) const;
LayoutPanel* findEmbeddedPanelByName(const std::string& name) const;
LLLayoutPanel* findEmbeddedPanel(LLPanel* panelp) const;
LLLayoutPanel* findEmbeddedPanelByName(const std::string& name) const;
S32 mMinWidth; // calculated by calcMinExtents
S32 mMinHeight; // calculated by calcMinExtents
@ -134,4 +139,49 @@ private:
bool mClip;
}; // end class LLLayoutStack
class LLLayoutPanel : public LLPanel
{
friend class LLLayoutStack;
friend class LLUICtrlFactory;
public:
struct Params : public LLInitParam::Block<Params, LLPanel::Params>
{
Optional<S32> min_dim,
max_dim;
Optional<bool> user_resize,
auto_resize;
Params()
: min_dim("min_dim", 0),
max_dim("max_dim", 0),
user_resize("user_resize", true),
auto_resize("auto_resize", true)
{
addSynonym(min_dim, "min_width");
addSynonym(min_dim, "min_height");
addSynonym(max_dim, "max_width");
addSynonym(max_dim, "max_height");
}
};
~LLLayoutPanel();
void initFromParams(const Params& p);
protected:
LLLayoutPanel(const Params& p) ;
F32 getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation);
S32 mMinDim;
S32 mMaxDim;
BOOL mAutoResize;
BOOL mUserResize;
BOOL mCollapsed;
class LLResizeBar* mResizeBar;
F32 mVisibleAmt;
F32 mCollapseAmt;
};
#endif

View File

@ -78,7 +78,7 @@ template class LLLineEditor* LLView::getChild<class LLLineEditor>(
//
LLLineEditor::Params::Params()
: max_length_bytes("max_length", 254),
: max_length(""),
keystroke_callback("keystroke_callback"),
prevalidate_callback("prevalidate_callback"),
background_image("background_image"),
@ -108,7 +108,8 @@ LLLineEditor::Params::Params()
LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
: LLUICtrl(p),
mMaxLengthBytes(p.max_length_bytes),
mMaxLengthBytes(p.max_length.bytes),
mMaxLengthChars(p.max_length.chars),
mCursorPos( 0 ),
mScrollHPos( 0 ),
mTextPadLeft(p.text_pad_left),
@ -313,6 +314,12 @@ void LLLineEditor::setMaxTextLength(S32 max_text_length)
mMaxLengthBytes = max_len;
}
void LLLineEditor::setMaxTextChars(S32 max_text_chars)
{
S32 max_chars = llmax(0, max_text_chars);
mMaxLengthChars = max_chars;
}
void LLLineEditor::getTextPadding(S32 *left, S32 *right)
{
*left = mTextPadLeft;
@ -358,6 +365,16 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)
}
mText.assign(truncated_utf8);
if (mMaxLengthChars)
{
LLWString truncated_wstring = utf8str_to_wstring(truncated_utf8);
if (truncated_wstring.size() > (U32)mMaxLengthChars)
{
truncated_wstring = truncated_wstring.substr(0, mMaxLengthChars);
}
mText.assign(wstring_to_utf8str(truncated_wstring));
}
if (all_selected)
{
// ...keep whole thing selected
@ -798,6 +815,7 @@ void LLLineEditor::addChar(const llwchar uni_char)
}
S32 cur_bytes = mText.getString().size();
S32 new_bytes = wchar_utf8_length(new_c);
BOOL allow_char = TRUE;
@ -807,6 +825,14 @@ void LLLineEditor::addChar(const llwchar uni_char)
{
allow_char = FALSE;
}
else if (mMaxLengthChars)
{
S32 wide_chars = mText.getWString().size();
if ((wide_chars + 1) > mMaxLengthChars)
{
allow_char = FALSE;
}
}
if (allow_char)
{
@ -1107,7 +1133,19 @@ void LLLineEditor::pasteHelper(bool is_primary)
clean_string = clean_string.substr(0, wchars_that_fit);
LLUI::reportBadKeystroke();
}
if (mMaxLengthChars)
{
U32 available_chars = mMaxLengthChars - mText.getWString().size();
if (available_chars < clean_string.size())
{
clean_string = clean_string.substr(0, available_chars);
}
LLUI::reportBadKeystroke();
}
mText.insert(getCursor(), clean_string);
setCursor( getCursor() + (S32)clean_string.length() );
deselect();
@ -1266,12 +1304,12 @@ 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() )
{
mText.assign( *(--mCurrentHistoryLine) );
setCursor(llmin((S32)mText.length(), getCursor()));
setCursorToEnd();
}
else
{
@ -1281,14 +1319,14 @@ 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 )
{
mText.assign( *(++mCurrentHistoryLine) );
setCursor(llmin((S32)mText.length(), getCursor()));
setCursorToEnd();
}
else
{

View File

@ -59,11 +59,19 @@ public:
typedef boost::function<void (LLLineEditor* caller)> keystroke_callback_t;
struct MaxLength : public LLInitParam::Choice<MaxLength>
{
Alternative<S32> bytes, chars;
MaxLength() : bytes("max_length_bytes", 254),
chars("max_length_chars", 0)
{}
};
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
Optional<std::string> default_text;
Optional<S32> max_length_bytes;
Optional<MaxLength> max_length;
Optional<keystroke_callback_t> keystroke_callback;
Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_callback;
@ -214,6 +222,7 @@ public:
void setKeystrokeCallback(callback_t callback, void* user_data);
void setMaxTextLength(S32 max_text_length);
void setMaxTextChars(S32 max_text_chars);
// Manipulate left and right padding for text
void getTextPadding(S32 *left, S32 *right);
void setTextPadding(S32 left, S32 right);
@ -277,6 +286,7 @@ protected:
LLViewBorder* mBorder;
const LLFontGL* mGLFont;
S32 mMaxLengthBytes; // Max length of the UTF8 string in bytes
S32 mMaxLengthChars; // Maximum number of characters in the string
S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
S32 mScrollHPos; // Horizontal offset from the start of mText. Used for scrolling.
LLFrameTimer mScrollTimer;

View File

@ -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)
void LLMenuButton::hideMenu()
{
if(mMenuHandle.isDead()) return;
LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
if (menu)
{
mMenu->setVisible(FALSE);
}
else
{
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,77 @@ 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(true);
setForcePressedState(false);
mIsMenuShown = false;
}
else
{
menu->buildDrawLabels();
menu->arrangeAndClear();
menu->updateParent(LLMenuGL::sMenuContainer);
LLButton::draw();
updateMenuOrigin();
setForcePressedState(false);
LLMenuGL::showPopup(getParent(), menu, mX, mY);
setForcePressedState(true);
mIsMenuShown = true;
}
}
void LLMenuButton::updateMenuOrigin()
{
if (mMenuHandle.isDead()) return;
LLRect rect = getRect();
switch (mMenuPosition)
{
case MP_TOP_LEFT:
{
mX = rect.mLeft;
mY = rect.mTop + mMenuHandle.get()->getRect().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;
}
}

View File

@ -29,7 +29,7 @@
#include "llbutton.h"
class LLMenuGL;
class LLToggleableMenu;
class LLMenuButton
: public LLButton
@ -42,22 +42,41 @@ public:
Optional<std::string> menu_filename;
Params();
};
};
typedef enum e_menu_position
{
MP_TOP_LEFT,
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;
};

View File

@ -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)
{
if( (*cur_item_iter) == mFirstVisibleItem)
case SD_UP:
{
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())
case SD_DOWN:
{
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++)
{
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);
@ -2603,14 +2618,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 +2630,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;
}
}
}
@ -2681,14 +2718,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 +2730,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 +2931,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;

View File

@ -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;

View File

@ -101,8 +101,8 @@ LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
}
for (LLInitParam::ParamIterator<SliderParams>::const_iterator it = p.sliders().begin();
it != p.sliders().end();
for (LLInitParam::ParamIterator<SliderParams>::const_iterator it = p.sliders.begin();
it != p.sliders.end();
++it)
{
if (it->name.isProvided())

View File

@ -130,7 +130,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)
params.name("MultiSliderCtrl Editor");
params.rect(text_rect);
params.font(p.font);
params.max_length_bytes(MAX_STRING_LENGTH);
params.max_length.bytes(MAX_STRING_LENGTH);
params.commit_callback.function(LLMultiSliderCtrl::onEditorCommit);
params.prevalidate_callback(&LLTextValidate::validateFloat);
params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);

View File

@ -27,8 +27,11 @@
#include "linden_common.h"
#include "llnotifications.h"
#include "llnotificationtemplate.h"
#include "llavatarnamecache.h"
#include "llinstantmessage.h"
#include "llcachename.h"
#include "llxmlnode.h"
#include "lluictrl.h"
#include "lluictrlfactory.h"
@ -37,6 +40,8 @@
#include "lltrans.h"
#include "llnotificationslistener.h"
#include "llstring.h"
#include "llsdparam.h"
#include "llsdutil.h"
#include <algorithm>
#include <boost/regex.hpp>
@ -44,6 +49,58 @@
const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
void NotificationPriorityValues::declareValues()
{
declare("low", NOTIFICATION_PRIORITY_LOW);
declare("normal", NOTIFICATION_PRIORITY_NORMAL);
declare("high", NOTIFICATION_PRIORITY_HIGH);
declare("critical", NOTIFICATION_PRIORITY_CRITICAL);
}
LLNotificationForm::FormElementBase::FormElementBase()
: name("name")
{}
LLNotificationForm::FormIgnore::FormIgnore()
: text("text"),
control("control"),
invert_control("invert_control", false),
save_option("save_option", false)
{}
LLNotificationForm::FormButton::FormButton()
: index("index"),
text("text"),
ignore("ignore"),
is_default("default"),
type("type")
{
// set type here so it gets serialized
type = "button";
}
LLNotificationForm::FormInput::FormInput()
: type("type"),
max_length_chars("max_length_chars"),
width("width", 0),
value("value")
{}
LLNotificationForm::FormElement::FormElement()
: button("button"),
input("input")
{}
LLNotificationForm::FormElements::FormElements()
: elements("")
{}
LLNotificationForm::Params::Params()
: name("name"),
ignore("ignore"),
form_elements("")
{}
// Local channel for persistent notifications
// Stores only persistent notifications.
// Class users can use connectChanged() to process persistent notifications
@ -88,12 +145,7 @@ bool filterIgnoredNotifications(LLNotificationPtr notification)
LLNotificationFormPtr form = notification->getForm();
// Check to see if the user wants to ignore this alert
if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO)
{
return LLUI::sSettingGroups["ignores"]->getBOOL(notification->getName());
}
return true;
return !notification->getForm()->getIgnored();
}
bool handleIgnoredNotification(const LLSD& payload)
@ -135,63 +187,68 @@ namespace LLNotificationFilters
};
LLNotificationForm::LLNotificationForm()
: mFormData(LLSD::emptyArray()),
mIgnore(IGNORE_NO)
: mIgnore(IGNORE_NO)
{
}
LLNotificationForm::LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node)
: mFormData(LLSD::emptyArray()),
mIgnore(IGNORE_NO)
LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p)
: mIgnore(IGNORE_NO),
mInvertSetting(false) // ignore settings by default mean true=show, false=ignore
{
if (!xml_node->hasName("form"))
if (p.ignore.isProvided())
{
llwarns << "Bad xml node for form: " << xml_node->getName() << llendl;
}
LLXMLNodePtr child = xml_node->getFirstChild();
while(child)
{
child = LLNotifications::instance().checkForXMLTemplate(child);
mIgnoreMsg = p.ignore.text;
LLSD item_entry;
std::string element_name = child->getName()->mString;
if (element_name == "ignore" )
if (!p.ignore.save_option)
{
bool save_option = false;
child->getAttribute_bool("save_option", save_option);
if (!save_option)
{
mIgnore = IGNORE_WITH_DEFAULT_RESPONSE;
}
else
{
// remember last option chosen by user and automatically respond with that in the future
mIgnore = IGNORE_WITH_LAST_RESPONSE;
LLUI::sSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
}
child->getAttributeString("text", mIgnoreMsg);
BOOL show_notification = TRUE;
LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Ignore notification with this name", TRUE);
mIgnore = IGNORE_WITH_DEFAULT_RESPONSE;
}
else
{
// flatten xml form entry into single LLSD map with type==name
item_entry["type"] = element_name;
const LLXMLAttribList::iterator attrib_end = child->mAttributes.end();
for(LLXMLAttribList::iterator attrib_it = child->mAttributes.begin();
attrib_it != attrib_end;
++attrib_it)
{
item_entry[std::string(attrib_it->second->getName()->mString)] = attrib_it->second->getValue();
}
item_entry["value"] = child->getTextContents();
mFormData.append(item_entry);
// remember last option chosen by user and automatically respond with that in the future
mIgnore = IGNORE_WITH_LAST_RESPONSE;
LLUI::sSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
}
child = child->getNextSibling();
BOOL show_notification = TRUE;
if (p.ignore.control.isProvided())
{
mIgnoreSetting = LLUI::sSettingGroups["config"]->getControl(p.ignore.control);
mInvertSetting = p.ignore.invert_control;
}
else
{
LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", TRUE);
mIgnoreSetting = LLUI::sSettingGroups["ignores"]->getControl(name);
}
}
LLParamSDParser parser;
parser.writeSD(mFormData, p.form_elements);
mFormData = mFormData[""];
if (!mFormData.isArray())
{
// change existing contents to a one element array
LLSD new_llsd_array = LLSD::emptyArray();
new_llsd_array.append(mFormData);
mFormData = new_llsd_array;
}
for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray();
it != end_it;
++it)
{
// lift contents of form element up a level, since element type is already encoded in "type" param
if (it->isMap() && it->beginMap() != it->endMap())
{
*it = it->beginMap()->second;
}
}
LL_DEBUGS("Notifications") << name << LL_ENDL;
LL_DEBUGS("Notifications") << ll_pretty_print_sd(mFormData) << LL_ENDL;
}
LLNotificationForm::LLNotificationForm(const LLSD& sd)
@ -293,16 +350,64 @@ std::string LLNotificationForm::getDefaultOption()
return "";
}
LLNotificationTemplate::LLNotificationTemplate() :
mExpireSeconds(0),
mExpireOption(-1),
mURLOption(-1),
mURLOpenExternally(-1),
mPersist(false),
mUnique(false),
mPriority(NOTIFICATION_PRIORITY_NORMAL)
LLControlVariablePtr LLNotificationForm::getIgnoreSetting()
{
return mIgnoreSetting;
}
bool LLNotificationForm::getIgnored()
{
mForm = LLNotificationFormPtr(new LLNotificationForm());
bool show = true;
if (mIgnore != LLNotificationForm::IGNORE_NO
&& mIgnoreSetting)
{
show = mIgnoreSetting->getValue().asBoolean();
if (mInvertSetting) show = !show;
}
return !show;
}
void LLNotificationForm::setIgnored(bool ignored)
{
if (mIgnoreSetting)
{
if (mInvertSetting) ignored = !ignored;
mIgnoreSetting->setValue(!ignored);
}
}
LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Params& p)
: mName(p.name),
mType(p.type),
mMessage(p.value),
mLabel(p.label),
mIcon(p.icon),
mURL(p.url.value),
mExpireSeconds(p.duration),
mExpireOption(p.expire_option),
mURLOption(p.url.option),
mURLTarget(p.url.target),
mUnique(p.unique.isProvided()),
mPriority(p.priority),
mPersist(p.persist),
mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name())
{
if (p.sound.isProvided()
&& LLUI::sSettingGroups["config"]->controlExists(p.sound))
{
mSoundEffect = LLUUID(LLUI::sSettingGroups["config"]->getString(p.sound));
}
for(LLInitParam::ParamIterator<LLNotificationTemplate::UniquenessContext>::const_iterator it = p.unique.contexts.begin(),
end_it = p.unique.contexts.end();
it != end_it;
++it)
{
mUniqueContext.push_back(it->key);
}
mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form));
}
LLNotification::LLNotification(const LLNotification::Params& p) :
@ -499,7 +604,7 @@ void LLNotification::respond(const LLSD& response)
{
mResponder->handleRespond(asLLSD(), response);
}
else
else if (!mResponseFunctorName.empty())
{
// look up the functor
LLNotificationFunctorRegistry::ResponseFunctor functor =
@ -507,6 +612,11 @@ void LLNotification::respond(const LLSD& response)
// and then call it
functor(asLLSD(), response);
}
else
{
// no registered responder
return;
}
if (mTemporaryResponder && !isReusable())
{
@ -517,8 +627,7 @@ void LLNotification::respond(const LLSD& response)
if (mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO)
{
BOOL show_notification = mIgnored ? FALSE : TRUE;
LLUI::sSettingGroups["ignores"]->setBOOL(getName(), show_notification);
mForm->setIgnored(mIgnored);
if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
{
LLUI::sSettingGroups["ignores"]->setLLSD("Default" + getName(), response);
@ -528,6 +637,49 @@ void LLNotification::respond(const LLSD& response)
update();
}
void LLNotification::respondWithDefault()
{
respond(getResponseTemplate(WITH_DEFAULT_BUTTON));
}
const std::string& LLNotification::getName() const
{
return mTemplatep->mName;
}
const std::string& LLNotification::getIcon() const
{
return mTemplatep->mIcon;
}
bool LLNotification::isPersistent() const
{
return mTemplatep->mPersist;
}
std::string LLNotification::getType() const
{
return (mTemplatep ? mTemplatep->mType : "");
}
S32 LLNotification::getURLOption() const
{
return (mTemplatep ? mTemplatep->mURLOption : -1);
}
S32 LLNotification::getURLOpenExternally() const
{
return(mTemplatep? mTemplatep->mURLTarget == "_external": -1);
}
bool LLNotification::hasUniquenessConstraints() const
{
return (mTemplatep ? mTemplatep->mUnique : false);
}
void LLNotification::setIgnored(bool ignore)
{
mIgnored = ignore;
@ -607,6 +759,8 @@ void LLNotification::init(const std::string& template_name, const LLSD& form_ele
// apply substitution to form labels
mForm->formatElements(mSubstitutions);
mIgnored = mForm->getIgnored();
LLDate rightnow = LLDate::now();
if (mTemplatep->mExpireSeconds)
{
@ -910,12 +1064,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));
}
@ -1095,11 +1249,6 @@ bool LLNotifications::templateExists(const std::string& name)
return (mTemplates.count(name) != 0);
}
void LLNotifications::clearTemplates()
{
mTemplates.clear();
}
void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option)
{
LLNotificationPtr temp_notify(new LLNotification(params));
@ -1162,190 +1311,88 @@ void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
}
}
// private to this file
// returns true if the template request was invalid and there's nothing else we
// can do with this node, false if you should keep processing (it may have
// replaced the contents of the node referred to)
LLXMLNodePtr LLNotifications::checkForXMLTemplate(LLXMLNodePtr item)
void replaceFormText(LLNotificationForm::Params& form, const std::string& pattern, const std::string& replace)
{
if (item->hasName("usetemplate"))
if (form.ignore.isProvided() && form.ignore.text() == pattern)
{
std::string replacementName;
if (item->getAttributeString("name", replacementName))
form.ignore.text = replace;
}
for (LLInitParam::ParamIterator<LLNotificationForm::FormElement>::iterator it = form.form_elements.elements.begin(),
end_it = form.form_elements.elements.end();
it != end_it;
++it)
{
if (it->button.isChosen() && it->button.text() == pattern)
{
StringMap replacements;
for (LLXMLAttribList::const_iterator it=item->mAttributes.begin();
it != item->mAttributes.end(); ++it)
{
replacements[it->second->getName()->mString] = it->second->getValue();
}
if (mXmlTemplates.count(replacementName))
{
item=LLXMLNode::replaceNode(item, mXmlTemplates[replacementName]);
// walk the nodes looking for $(substitution) here and replace
replaceSubstitutionStrings(item, replacements);
}
else
{
llwarns << "XML template lookup failure on '" << replacementName << "' " << llendl;
}
it->button.text = replace;
}
}
return item;
}
bool LLNotifications::loadTemplates()
{
const std::string xml_filename = "notifications.xml";
std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename);
LLXMLNodePtr root;
BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
if (!success || root.isNull() || !root->hasName( "notifications" ))
{
llerrs << "Problem reading UI Notifications file: " << xml_filename << llendl;
llerrs << "Problem reading UI Notifications file: " << full_filename << llendl;
return false;
}
clearTemplates();
for (LLXMLNodePtr item = root->getFirstChild();
item.notNull(); item = item->getNextSibling())
LLNotificationTemplate::Notifications params;
LLXUIParser parser;
parser.readXUI(root, params, full_filename);
mTemplates.clear();
for(LLInitParam::ParamIterator<LLNotificationTemplate::GlobalString>::const_iterator it = params.strings.begin(), end_it = params.strings.end();
it != end_it;
++it)
{
// we do this FIRST so that item can be changed if we
// encounter a usetemplate -- we just replace the
// current xml node and keep processing
item = checkForXMLTemplate(item);
if (item->hasName("global"))
{
std::string global_name;
if (item->getAttributeString("name", global_name))
{
mGlobalStrings[global_name] = item->getTextContents();
}
continue;
}
if (item->hasName("template"))
{
// store an xml template; templates must have a single node (can contain
// other nodes)
std::string name;
item->getAttributeString("name", name);
LLXMLNodePtr ptr = item->getFirstChild();
mXmlTemplates[name] = ptr;
continue;
}
if (!item->hasName("notification"))
{
llwarns << "Unexpected entity " << item->getName()->mString <<
" found in " << xml_filename << llendl;
continue;
}
// now we know we have a notification entry, so let's build it
LLNotificationTemplatePtr pTemplate(new LLNotificationTemplate());
if (!item->getAttributeString("name", pTemplate->mName))
{
llwarns << "Unable to parse notification with no name" << llendl;
continue;
}
//llinfos << "Parsing " << pTemplate->mName << llendl;
pTemplate->mMessage = item->getTextContents();
pTemplate->mDefaultFunctor = pTemplate->mName;
item->getAttributeString("type", pTemplate->mType);
item->getAttributeString("icon", pTemplate->mIcon);
item->getAttributeString("label", pTemplate->mLabel);
item->getAttributeU32("duration", pTemplate->mExpireSeconds);
item->getAttributeU32("expireOption", pTemplate->mExpireOption);
std::string priority;
item->getAttributeString("priority", priority);
pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL;
if (!priority.empty())
{
if (priority == "low") pTemplate->mPriority = NOTIFICATION_PRIORITY_LOW;
if (priority == "normal") pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL;
if (priority == "high") pTemplate->mPriority = NOTIFICATION_PRIORITY_HIGH;
if (priority == "critical") pTemplate->mPriority = NOTIFICATION_PRIORITY_CRITICAL;
}
item->getAttributeString("functor", pTemplate->mDefaultFunctor);
BOOL persist = false;
item->getAttributeBOOL("persist", persist);
pTemplate->mPersist = persist;
std::string sound;
item->getAttributeString("sound", sound);
if (!sound.empty())
{
// test for bad sound effect name / missing effect
if (LLUI::sSettingGroups["config"]->controlExists(sound))
{
pTemplate->mSoundEffect =
LLUUID(LLUI::sSettingGroups["config"]->getString(sound));
}
else
{
llwarns << "Unknown sound effect control name " << sound
<< llendl;
}
}
for (LLXMLNodePtr child = item->getFirstChild();
!child.isNull(); child = child->getNextSibling())
{
child = checkForXMLTemplate(child);
// <url>
if (child->hasName("url"))
{
pTemplate->mURL = child->getTextContents();
child->getAttributeU32("option", pTemplate->mURLOption);
child->getAttributeU32("openexternally", pTemplate->mURLOpenExternally);
}
if (child->hasName("unique"))
{
pTemplate->mUnique = true;
for (LLXMLNodePtr formitem = child->getFirstChild();
!formitem.isNull(); formitem = formitem->getNextSibling())
{
if (formitem->hasName("context"))
{
std::string key;
formitem->getAttributeString("key", key);
pTemplate->mUniqueContext.push_back(key);
//llwarns << "adding " << key << " to unique context" << llendl;
}
else
{
llwarns << "'unique' has unrecognized subelement "
<< formitem->getName()->mString << llendl;
}
}
}
// <form>
if (child->hasName("form"))
{
pTemplate->mForm = LLNotificationFormPtr(new LLNotificationForm(pTemplate->mName, child));
}
}
addTemplate(pTemplate->mName, pTemplate);
mGlobalStrings[it->name] = it->value;
}
//std::ostringstream ostream;
//root->writeToOstream(ostream, "\n ");
//llwarns << ostream.str() << llendl;
std::map<std::string, LLNotificationForm::Params> form_templates;
for(LLInitParam::ParamIterator<LLNotificationTemplate::Template>::const_iterator it = params.templates.begin(), end_it = params.templates.end();
it != end_it;
++it)
{
form_templates[it->name] = it->form;
}
for(LLInitParam::ParamIterator<LLNotificationTemplate::Params>::iterator it = params.notifications.begin(), end_it = params.notifications.end();
it != end_it;
++it)
{
if (it->form_ref.form_template.isChosen())
{
// replace form contents from template
it->form_ref.form = form_templates[it->form_ref.form_template.name];
if(it->form_ref.form_template.yes_text.isProvided())
{
replaceFormText(it->form_ref.form, "$yestext", it->form_ref.form_template.yes_text);
}
if(it->form_ref.form_template.no_text.isProvided())
{
replaceFormText(it->form_ref.form, "$notext", it->form_ref.form_template.no_text);
}
if(it->form_ref.form_template.cancel_text.isProvided())
{
replaceFormText(it->form_ref.form, "$canceltext", it->form_ref.form_template.cancel_text);
}
if(it->form_ref.form_template.ignore_text.isProvided())
{
replaceFormText(it->form_ref.form, "$ignoretext", it->form_ref.form_template.ignore_text);
}
}
addTemplate(it->name, LLNotificationTemplatePtr(new LLNotificationTemplate(*it)));
}
return true;
}
@ -1396,6 +1443,8 @@ LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
void LLNotifications::add(const LLNotificationPtr pNotif)
{
if (pNotif == NULL) return;
// first see if we already have it -- if so, that's a problem
LLNotificationSet::iterator it=mItems.find(pNotif);
if (it != mItems.end())
@ -1408,6 +1457,8 @@ void LLNotifications::add(const LLNotificationPtr pNotif)
void LLNotifications::cancel(LLNotificationPtr pNotif)
{
if (pNotif == NULL) return;
LLNotificationSet::iterator it=mItems.find(pNotif);
if (it == mItems.end())
{
@ -1417,6 +1468,30 @@ void LLNotifications::cancel(LLNotificationPtr pNotif)
updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
}
void LLNotifications::cancelByName(const std::string& name)
{
std::vector<LLNotificationPtr> notifs_to_cancel;
for (LLNotificationSet::iterator it=mItems.begin(), end_it = mItems.end();
it != end_it;
++it)
{
LLNotificationPtr pNotif = *it;
if (pNotif->getName() == name)
{
notifs_to_cancel.push_back(pNotif);
}
}
for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
it != end_it;
++it)
{
LLNotificationPtr pNotif = *it;
pNotif->cancel();
updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
}
}
void LLNotifications::update(const LLNotificationPtr pNotif)
{
LLNotificationSet::iterator it=mItems.find(pNotif);
@ -1481,17 +1556,50 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
return s;
}
void LLPostponedNotification::onCachedNameReceived(const LLUUID& id, const std::string& first,
const std::string& last, bool is_group)
//static
void LLPostponedNotification::lookupName(LLPostponedNotification* thiz,
const LLUUID& id,
bool is_group)
{
mName = first + " " + last;
LLStringUtil::trim(mName);
if (mName.empty())
if (is_group)
{
llwarns << "Empty name received for Id: " << id << llendl;
mName = SYSTEM_FROM;
gCacheName->getGroup(id,
boost::bind(&LLPostponedNotification::onGroupNameCache,
thiz, _1, _2, _3));
}
else
{
LLAvatarNameCache::get(id,
boost::bind(&LLPostponedNotification::onAvatarNameCache,
thiz, _1, _2));
}
}
void LLPostponedNotification::onGroupNameCache(const LLUUID& id,
const std::string& full_name,
bool is_group)
{
finalizeName(full_name);
}
void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id,
const LLAvatarName& av_name)
{
std::string name = av_name.getCompleteName();
// from PE merge - we should figure out if this is the right thing to do
if (name.empty())
{
llwarns << "Empty name received for Id: " << agent_id << llendl;
name = SYSTEM_FROM;
}
finalizeName(name);
}
void LLPostponedNotification::finalizeName(const std::string& name)
{
mName = name;
modifyNotificationParams();
LLNotifications::instance().add(mParams);
cleanup();

View File

@ -98,9 +98,8 @@
#include "llinitparam.h"
#include "llnotificationslistener.h"
#include "llnotificationptr.h"
#include "llcachename.h"
class LLAvatarName;
typedef enum e_notification_priority
{
NOTIFICATION_PRIORITY_UNSPECIFIED,
@ -110,6 +109,11 @@ typedef enum e_notification_priority
NOTIFICATION_PRIORITY_CRITICAL
} ENotificationPriority;
struct NotificationPriorityValues : public LLInitParam::TypeValuesHelper<ENotificationPriority, NotificationPriorityValues>
{
static void declareValues();
};
class LLNotificationResponderInterface
{
public:
@ -157,6 +161,68 @@ class LLNotificationForm
LOG_CLASS(LLNotificationForm);
public:
struct FormElementBase : public LLInitParam::Block<FormElementBase>
{
Optional<std::string> name;
FormElementBase();
};
struct FormIgnore : public LLInitParam::Block<FormIgnore, FormElementBase>
{
Optional<std::string> text;
Optional<bool> save_option;
Optional<std::string> control;
Optional<bool> invert_control;
FormIgnore();
};
struct FormButton : public LLInitParam::Block<FormButton, FormElementBase>
{
Mandatory<S32> index;
Mandatory<std::string> text;
Optional<std::string> ignore;
Optional<bool> is_default;
Mandatory<std::string> type;
FormButton();
};
struct FormInput : public LLInitParam::Block<FormInput, FormElementBase>
{
Mandatory<std::string> type;
Optional<S32> width;
Optional<S32> max_length_chars;
Optional<std::string> value;
FormInput();
};
struct FormElement : public LLInitParam::Choice<FormElement>
{
Alternative<FormButton> button;
Alternative<FormInput> input;
FormElement();
};
struct FormElements : public LLInitParam::Block<FormElements>
{
Multiple<FormElement> elements;
FormElements();
};
struct Params : public LLInitParam::Block<Params>
{
Optional<std::string> name;
Optional<FormIgnore> ignore;
Optional<FormElements> form_elements;
Params();
};
typedef enum e_ignore_type
{
IGNORE_NO,
@ -167,8 +233,7 @@ public:
LLNotificationForm();
LLNotificationForm(const LLSD& sd);
LLNotificationForm(const std::string& name,
const LLPointer<class LLXMLNode> xml_node);
LLNotificationForm(const std::string& name, const Params& p);
LLSD asLLSD() const;
@ -181,92 +246,25 @@ public:
// appends form elements from another form serialized as LLSD
void append(const LLSD& sub_form);
std::string getDefaultOption();
LLPointer<class LLControlVariable> getIgnoreSetting();
bool getIgnored();
void setIgnored(bool ignored);
EIgnoreType getIgnoreType() { return mIgnore; }
std::string getIgnoreMessage() { return mIgnoreMsg; }
private:
LLSD mFormData;
EIgnoreType mIgnore;
std::string mIgnoreMsg;
LLSD mFormData;
EIgnoreType mIgnore;
std::string mIgnoreMsg;
LLPointer<class LLControlVariable> mIgnoreSetting;
bool mInvertSetting;
};
typedef boost::shared_ptr<LLNotificationForm> LLNotificationFormPtr;
// This is the class of object read from the XML file (notifications.xml,
// from the appropriate local language directory).
struct LLNotificationTemplate
{
LLNotificationTemplate();
// the name of the notification -- the key used to identify it
// Ideally, the key should follow variable naming rules
// (no spaces or punctuation).
std::string mName;
// The type of the notification
// used to control which queue it's stored in
std::string mType;
// The text used to display the notification. Replaceable parameters
// are enclosed in square brackets like this [].
std::string mMessage;
// The label for the notification; used for
// certain classes of notification (those with a window and a window title).
// Also used when a notification pops up underneath the current one.
// Replaceable parameters can be used in the label.
std::string mLabel;
// The name of the icon image. This should include an extension.
std::string mIcon;
// This is the Highlander bit -- "There Can Be Only One"
// An outstanding notification with this bit set
// is updated by an incoming notification with the same name,
// rather than creating a new entry in the queue.
// (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
// 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
// sender but still differentiating different offers. Example: Invitation to
// conference chat.
std::vector<std::string> mUniqueContext;
// If this notification expires automatically, this value will be
// nonzero, and indicates the number of seconds for which the notification
// will be valid (a teleport offer, for example, might be valid for
// 300 seconds).
U32 mExpireSeconds;
// if the offer expires, one of the options is chosen automatically
// based on its "value" parameter. This controls which one.
// If expireSeconds is specified, expireOption should also be specified.
U32 mExpireOption;
// if the notification contains a url, it's stored here (and replaced
// into the message where [_URL] is found)
std::string mURL;
// if there's a URL in the message, this controls which option visits
// that URL. Obsolete this and eliminate the buttons for affected
// messages when we allow clickable URLs in the UI
U32 mURLOption;
U32 mURLOpenExternally;
//This is a flag that tells if the url needs to open externally dispite
//what the user setting is.
// does this notification persist across sessions? if so, it will be
// serialized to disk on first receipt and read on startup
bool mPersist;
// This is the name of the default functor, if present, to be
// used for the notification's callback. It is optional, and used only if
// the notification is constructed without an identified functor.
std::string mDefaultFunctor;
// The form data associated with a given notification (buttons, text boxes, etc)
LLNotificationFormPtr mForm;
// default priority for notifications of this type
ENotificationPriority mPriority;
// UUID of the audio file to be played when this notification arrives
// this is loaded as a name, but looked up to get the UUID upon template load.
// If null, it wasn't specified.
LLUUID mSoundEffect;
};
struct LLNotificationTemplate;
// we want to keep a map of these by name, and it's best to manage them
// with smart pointers
@ -302,7 +300,7 @@ public:
// optional
Optional<LLSD> substitutions;
Optional<LLSD> payload;
Optional<ENotificationPriority> priority;
Optional<ENotificationPriority, NotificationPriorityValues> priority;
Optional<LLSD> form_elements;
Optional<LLDate> time_stamp;
Optional<LLNotificationContext*> context;
@ -445,6 +443,7 @@ public:
LLSD asLLSD();
void respond(const LLSD& sd);
void respondWithDefault();
void* getResponder() { return mResponderObj; }
@ -462,6 +461,13 @@ public:
return mRespondedTo;
}
bool isActive() const
{
return !isRespondedTo()
&& !isCancelled()
&& !isExpired();
}
const LLSD& getResponse() { return mResponse; }
bool isIgnored() const
@ -469,15 +475,11 @@ public:
return mIgnored;
}
const std::string& getName() const
{
return mTemplatep->mName;
}
const std::string& getName() const;
bool isPersistent() const
{
return mTemplatep->mPersist;
}
const std::string& getIcon() const;
bool isPersistent() const;
const LLUUID& id() const
{
@ -499,28 +501,12 @@ public:
return mTimestamp;
}
std::string getType() const
{
return (mTemplatep ? mTemplatep->mType : "");
}
std::string getType() const;
std::string getMessage() const;
std::string getLabel() const;
std::string getURL() const;
// {
// return (mTemplatep ? mTemplatep->mURL : "");
// }
S32 getURLOption() const
{
return (mTemplatep ? mTemplatep->mURLOption : -1);
}
S32 getURLOpenExternally() const
{
return(mTemplatep? mTemplatep->mURLOpenExternally : -1);
}
S32 getURLOption() const;
S32 getURLOpenExternally() const;
const LLNotificationFormPtr getForm();
@ -590,7 +576,7 @@ public:
std::string summarize() const;
bool hasUniquenessConstraints() const { return (mTemplatep ? mTemplatep->mUnique : false);}
bool hasUniquenessConstraints() const;
virtual ~LLNotification() {}
};
@ -872,7 +858,6 @@ public:
// load notification descriptions from file;
// OK to call more than once because it will reload
bool loadTemplates();
LLPointer<class LLXMLNode> checkForXMLTemplate(LLPointer<class LLXMLNode> item);
// Add a simple notification (from XUI)
void addFromCallback(const LLSD& name);
@ -894,6 +879,7 @@ public:
void add(const LLNotificationPtr pNotif);
void cancel(LLNotificationPtr pNotif);
void cancelByName(const std::string& name);
void update(const LLNotificationPtr pNotif);
LLNotificationPtr find(LLUUID uuid);
@ -917,8 +903,6 @@ public:
// test for existence
bool templateExists(const std::string& name);
// useful if you're reloading the file
void clearTemplates(); // erase all templates
void forceResponse(const LLNotification::Params& params, S32 option);
@ -956,9 +940,6 @@ private:
std::string mFileName;
typedef std::map<std::string, LLPointer<class LLXMLNode> > XMLTemplateMap;
XMLTemplateMap mXmlTemplates;
LLNotificationMap mUniqueNotifications;
typedef std::map<std::string, std::string> GlobalStringMap;
@ -994,17 +975,20 @@ public:
{
// upcast T to the base type to restrict T derivation from LLPostponedNotification
LLPostponedNotification* thiz = new T();
thiz->mParams = params;
gCacheName->get(id, is_group, boost::bind(
&LLPostponedNotification::onCachedNameReceived, thiz, _1, _2,
_3, _4));
// Avoid header file dependency on llcachename.h
lookupName(thiz, id, is_group);
}
private:
void onCachedNameReceived(const LLUUID& id, const std::string& first,
const std::string& last, bool is_group);
static void lookupName(LLPostponedNotification* thiz, const LLUUID& id, bool is_group);
// only used for groups
void onGroupNameCache(const LLUUID& id, const std::string& full_name, bool is_group);
// only used for avatars
void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
// used for both group and avatar names
void finalizeName(const std::string& name);
void cleanup()
{

View File

@ -29,6 +29,7 @@
#include "linden_common.h"
#include "llnotificationslistener.h"
#include "llnotifications.h"
#include "llnotificationtemplate.h"
#include "llsd.h"
#include "llui.h"
@ -182,7 +183,11 @@ void LLNotificationsListener::ignore(const LLSD& params) const
if (params["name"].isDefined())
{
// ["name"] was passed: ignore just that notification
LLUI::sSettingGroups["ignores"]->setBOOL(params["name"], ignore);
LLNotificationTemplatePtr templatep = mNotifications.getTemplate(params["name"]);
if (templatep)
{
templatep->mForm->setIgnored(ignore);
}
}
else
{

Some files were not shown because too many files have changed in this diff Show More