[SCM] A client for connecting to 3D metaverses such as Linden Labs Secondlife(tm) and OpenSim grids branch, upstream, updated. upstream/1.21.6-13-g1836a2b
Robin Cornelius
robin.cornelius at gmail.com
Tue Apr 14 11:12:15 UTC 2009
The following commit has been merged in the upstream branch:
commit ab7fdf09be4321f17fd55f6d6630b03489900280
Author: Robin Cornelius <robin.cornelius at gmail.com>
Date: Fri Mar 13 09:56:17 2009 +0000
Imported Upstream version 1.22.11
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..ad258d3
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,44 @@
+topic/debian/71_use_debian_alternatives_for_www_browser.diff -p1
+topic/debian/Allow_CXXFLAGS_to_specifiy_the_debug_flag.diff -p1
+topic/debian/VWR-8751-Build-with-a-shared-llmozlib2.diff -p1
+topic/debian/remove_as_needed.diff -p1
+topic/debian/trademark_compliance.diff -p1
+topic/debian/use_debian-included_fonts.diff -p1
+topic/debian/use_debian_channel.diff -p1
+topic/debian/viewericon.diff -p1
+topic/features/jira-backported/AvatarLoginList.diff -p1
+topic/features/jira-backported/VWR-11663_Save_Load_scripts.diff -p1
+topic/features/jira-backported/VWR-5082_set_bulk_inv_permissions.diff -p1
+topic/features/jira-backported/VWR-7531-Dynamic-Grid-List.diff -p1
+topic/features/jira-backported/avatar_list.diff -p1
+topic/features/jira-sent/24_always_test_vectorize.diff -p1
+topic/fixes/jira-backported/VWR-11103-FIx_local_ruler_on_linked_objects.diff -p1
+topic/fixes/jira-backported/VWR-11674_dont_download_muted_sounds.diff -p1
+topic/fixes/jira-backported/VWR-3766_llGetInventoryNumber_tooltip_missing_INVENTORY_ANIMATION.diff -p1
+topic/fixes/jira-backported/VWR5717_text_not_replaced_by_input_with_japanses_im.diff -p1
+topic/fixes/jira-backported/glh_linear.diff -p1
+topic/fixes/jira-sent/50_get_cpu_clock_count_for_more_than_just_i386.diff -p1
+topic/fixes/jira-sent/59_need_llpreprocessor_to_access_endian_macros.diff -p1
+topic/fixes/jira-sent/VWR-10759_delete_LLMediaImplGStreamer_mediaData_as_array.diff -p1
+topic/fixes/jira-sent/VWR-11128-Find_Python.diff -p1
+topic/fixes/jira-sent/VWR-11138_Develop_py_and_express_VS.diff -p1
+#topic/fixes/jira-sent/VWR-1815_top_corner_fix.diff -p1
+topic/fixes/jira-sent/VWR-8194_clamp_outline_for_broken_nvidia.diff -p1
+topic/fixes/jira-sent/VWR-9557-EnableBuildWithNvidiaOrMesaHeaders.diff -p1
+topic/fixes/jira-sent/correctly_find_dbus_headers_on_standalone.diff -p1
+topic/fixes/jira-sent/dont_depend_on_artwork_to_build.diff -p1
+topic/fixes/jira-sent/gcc_warning_on_llwindowsdl_var_not_set.diff -p1
+topic/fixes/jira-sent/lltemplatemessagereader_memcpy.diff -p1
+topic/fixes/jira-sent/no_format_or_string_literal.diff -p1
+topic/fixes/jira-sent/use_c_locale_and_dont_spam_me_because_i_am_not_american.diff -p1
+topic/fixes/new/allow_compilation_when_GST_DISABLE_GST_DEBUG_is_defined.diff -p1
+topic/fixes/new/delete_LLImageTGA_mColorMap_as_array.diff -p1
+topic/fixes/new/fix_cmake_install_target.diff -p1
+topic/fixes/thirdparty/0000_keep_statbars_from_overshooting.patch.diff -p1
+topic/fixes/thirdparty/0001_possible_crash_and_leak_llassetstorage.patch.diff -p1
+topic/fixes/thirdparty/0001_possible_crash_in_llviewerpartssim.diff -p1
+topic/fixes/thirdparty/1294_llworkerthread_when_terminating_program.patch.diff -p1
+topic/fixes/thirdparty/1857_apr_thread_mutex_nested.patch.diff -p1
+topic/fixes/thirdparty/2003_possible_crash_draganddrop.patch.diff -p1
+topic/fixes/thirdparty/2543_possible_crash_in_group_voting_propsals.patch.diff -p1
+topic/fixes/thirdparty/2683_possible_crash_update_speaker_list.patch.diff -p1
diff --git a/debian/patches/topic/debian/71_use_debian_alternatives_for_www_browser.diff b/debian/patches/topic/debian/71_use_debian_alternatives_for_www_browser.diff
new file mode 100644
index 0000000..2703617
--- /dev/null
+++ b/debian/patches/topic/debian/71_use_debian_alternatives_for_www_browser.diff
@@ -0,0 +1,39 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/debian/71_use_debian_alternatives_for_www_browser
+
+Fix the launch_url.sh script to use the debian system prefered browser choice.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/newview/linux_tools/launch_url.sh | 8 ++++++++
+ 1 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/indra/newview/linux_tools/launch_url.sh b/indra/newview/linux_tools/launch_url.sh
+index d2c8919..33cee0e 100755
+--- a/indra/newview/linux_tools/launch_url.sh
++++ b/indra/newview/linux_tools/launch_url.sh
+@@ -8,6 +8,7 @@
+ # On Unixoids we try, in order of decreasing priority:
+ # - $BROWSER if set (preferred)
+ # - kfmclient openURL
++# - gnome-open
+ # - x-www-browser
+ # - opera
+ # - firefox
+@@ -54,6 +55,13 @@ if which kfmclient >/dev/null; then
+ exit
+ fi
+
++# else gnome-open
++# (embodies gnome concept of 'preferred browser')
++if which gnome-open >/dev/null; then
++ gnome-open "$URL" &
++ exit
++fi
++
+ # else x-www-browser
+ # (Debianesque idea of a working X browser)
+ if which x-www-browser >/dev/null; then
+--
+tg: (f7a6cc1..) topic/debian/71_use_debian_alternatives_for_www_browser (depends on: master)
diff --git a/debian/patches/topic/debian/Allow_CXXFLAGS_to_specifiy_the_debug_flag.diff b/debian/patches/topic/debian/Allow_CXXFLAGS_to_specifiy_the_debug_flag.diff
new file mode 100644
index 0000000..4fc45de
--- /dev/null
+++ b/debian/patches/topic/debian/Allow_CXXFLAGS_to_specifiy_the_debug_flag.diff
@@ -0,0 +1,43 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/debian/Allow_CXXFLAGS_to_specifiy_the_debug_flag
+
+Modify 00-Common.cmake to allow custom CXXFLAGS to take effect
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/cmake/00-Common.cmake | 5 ++---
+ 1 files changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
+index bf2d2c3..0d1092b 100644
+--- a/indra/cmake/00-Common.cmake
++++ b/indra/cmake/00-Common.cmake
+@@ -11,7 +11,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "-D_DEBUG -DLL_DEBUG=1")
+ set(CMAKE_CXX_FLAGS_RELEASE
+ "-DLL_RELEASE=1 -DLL_RELEASE_FOR_DOWNLOAD=1 -DNDEBUG")
+ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
+- "-DLL_RELEASE=1 -DNDEBUG -DLL_RELEASE_WITH_DEBUG_INFO=1")
++ "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DLL_RELEASE=1 -DNDEBUG -DLL_RELEASE_WITH_DEBUG_INFO=1")
+
+
+ # Don't bother with a MinSizeRel build.
+@@ -123,7 +123,6 @@ if (LINUX)
+ -fno-math-errno
+ -fno-strict-aliasing
+ -fsigned-char
+- -g
+ -pthread
+ )
+
+@@ -158,7 +157,7 @@ if (LINUX)
+ endif (NOT STANDALONE)
+ endif (VIEWER)
+
+- set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG}")
++ set(CMAKE_CXX_FLAGS_DEBUG "-g -fno-inline ${CMAKE_CXX_FLAGS_DEBUG}")
+ set(CMAKE_CXX_FLAGS_RELEASE "-O2 ${CMAKE_CXX_FLAGS_RELEASE}")
+ endif (LINUX)
+
+--
+tg: (8278398..) topic/debian/Allow_CXXFLAGS_to_specifiy_the_debug_flag (depends on: master)
diff --git a/debian/patches/topic/debian/VWR-8751-Build-with-a-shared-llmozlib2.diff b/debian/patches/topic/debian/VWR-8751-Build-with-a-shared-llmozlib2.diff
new file mode 100644
index 0000000..c745cc0
--- /dev/null
+++ b/debian/patches/topic/debian/VWR-8751-Build-with-a-shared-llmozlib2.diff
@@ -0,0 +1,74 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/debian/VWR-8751-Build-with-a-shared-llmozlib2
+
+This allows us to use a dynamic libllmozlib library.
+Upstream are not currently interested in this.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/cmake/Mozlib.cmake | 26 +++++++++++++++++---------
+ indra/llmedia/llmediamanager.cpp | 2 ++
+ 2 files changed, 19 insertions(+), 9 deletions(-)
+
+diff --git a/indra/cmake/Mozlib.cmake b/indra/cmake/Mozlib.cmake
+index e9555df..b3002d3 100644
+--- a/indra/cmake/Mozlib.cmake
++++ b/indra/cmake/Mozlib.cmake
+@@ -3,8 +3,23 @@ include(Linking)
+ include(Prebuilt)
+
+ if (STANDALONE)
+- set(MOZLIB OFF CACHE BOOL
+- "Enable Mozilla support in the viewer (requires llmozlib library).")
++ FIND_PATH(MOZLIB_INCLUDE_DIR llmozlib2.h
++ /usr/local/include/
++ /usr/include/
++ )
++ SET(MOZLIB_NAMES ${MOZLIB_NAMES} llmozlib2)
++ FIND_LIBRARY(MOZLIB_LIBRARY
++ NAMES ${MOZLIB_NAMES}
++ PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64
++ )
++ IF (MOZLIB_LIBRARY AND MOZLIB_INCLUDE_DIR)
++ MESSAGE ( STATUS "Enabled LLMOZLIB2" )
++ set(MOZLIB ON)
++ ELSE (MOZLIB_LIBRARY AND MOZLIB_INCLUDE_DIR)
++ MESSAGE ( STATUS "NOT USING LLMOZLIB2" )
++ set(MOZLIB OFF)
++ ENDIF (MOZLIB_LIBRARY AND MOZLIB_INCLUDE_DIR)
++
+ else (STANDALONE)
+ use_prebuilt_binary(llmozlib)
+ set(MOZLIB ON CACHE BOOL
+@@ -18,13 +33,6 @@ if (MOZLIB)
+ link_directories(${CMAKE_SOURCE_DIR}/newview/app_settings/mozilla-runtime-linux-${ARCH})
+ set(MOZLIB_LIBRARIES
+ llmozlib2
+- mozjs
+- nspr4
+- plc4
+- plds4
+- xpcom
+- xul
+- profdirserviceprovider_s
+ )
+ elseif (WINDOWS)
+ if (MSVC71)
+diff --git a/indra/llmedia/llmediamanager.cpp b/indra/llmedia/llmediamanager.cpp
+index 6db8dbe..ea02268 100644
+--- a/indra/llmedia/llmediamanager.cpp
++++ b/indra/llmedia/llmediamanager.cpp
+@@ -150,8 +150,10 @@ LLMediaManager* LLMediaManager::getInstance()
+ // (static)
+ void LLMediaManager::setBrowserUserAgent(std::string user_agent)
+ {
++#if LL_LLMOZLIB_ENABLED
+ // *HACK: Breaks encapsulation model, as initClass does above. JC
+ LLMediaImplLLMozLib::setBrowserUserAgent(user_agent);
++#endif
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+--
+tg: (74b8ac2..) topic/debian/VWR-8751-Build-with-a-shared-llmozlib2 (depends on: master)
diff --git a/debian/patches/topic/debian/remove_as_needed.diff b/debian/patches/topic/debian/remove_as_needed.diff
new file mode 100644
index 0000000..1ee8e22
--- /dev/null
+++ b/debian/patches/topic/debian/remove_as_needed.diff
@@ -0,0 +1,39 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/debian/remove_as_needed
+
+This was needed to build on ubuntu, which i am keeping syncronised with as well.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/linux_crash_logger/CMakeLists.txt | 2 --
+ indra/newview/CMakeLists.txt | 1 -
+ 2 files changed, 0 insertions(+), 3 deletions(-)
+
+diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt
+index 6f6754e..0711791 100644
+--- a/indra/linux_crash_logger/CMakeLists.txt
++++ b/indra/linux_crash_logger/CMakeLists.txt
+@@ -38,8 +38,6 @@ list(APPEND linux_crash_logger_SOURCE_FILES
+ ${linux_crash_logger_HEADER_FILES}
+ )
+
+-list(APPEND CMAKE_EXE_LINKER_FLAGS -Wl,--as-needed)
+-
+ add_executable(linux-crash-logger ${linux_crash_logger_SOURCE_FILES})
+
+ target_link_libraries(linux-crash-logger
+diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
+index b95ba37..d1884ac 100644
+--- a/indra/newview/CMakeLists.txt
++++ b/indra/newview/CMakeLists.txt
+@@ -879,7 +879,6 @@ endif (DARWIN)
+ if (LINUX)
+ LIST(APPEND viewer_SOURCE_FILES llappviewerlinux.cpp)
+ LIST(APPEND viewer_SOURCE_FILES llappviewerlinux_api_dbus.cpp)
+- LIST(APPEND CMAKE_EXE_LINKER_FLAGS -Wl,--as-needed)
+
+ set(viewer_LIBRARIES
+ Xinerama
+--
+tg: (cec4d01..) topic/debian/remove_as_needed (depends on: master)
diff --git a/debian/patches/topic/debian/trademark_compliance.diff b/debian/patches/topic/debian/trademark_compliance.diff
new file mode 100644
index 0000000..f9f6a6d
--- /dev/null
+++ b/debian/patches/topic/debian/trademark_compliance.diff
@@ -0,0 +1,35 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/debian/trademark_compliance
+
+Remove instances of Secondlife and replace with Omvviewer
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/llappviewer.cpp | 4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
+index 09d1141..4acbf92 100644
+--- a/indra/newview/llappviewer.cpp
++++ b/indra/newview/llappviewer.cpp
+@@ -1876,7 +1876,7 @@ bool LLAppViewer::initConfiguration()
+ mYieldTime = gSavedSettings.getS32("YieldTime");
+
+ // XUI:translate
+- gSecondLife = "Second Life";
++ gSecondLife = "Open Metaverse Viewer";
+
+ // Read skin/branding settings if specified.
+ //if (! gDirUtilp->getSkinDir().empty() )
+@@ -2062,7 +2062,7 @@ bool LLAppViewer::initWindow()
+
+ // always start windowed
+ BOOL ignorePixelDepth = gSavedSettings.getBOOL("IgnorePixelDepth");
+- gViewerWindow = new LLViewerWindow(gWindowTitle, "Second Life",
++ gViewerWindow = new LLViewerWindow(gWindowTitle, "Open Metaverse Viewer",
+ gSavedSettings.getS32("WindowX"), gSavedSettings.getS32("WindowY"),
+ gSavedSettings.getS32("WindowWidth"), gSavedSettings.getS32("WindowHeight"),
+ FALSE, ignorePixelDepth);
+--
+tg: (264f6be..) topic/debian/trademark_compliance (depends on: master)
diff --git a/debian/patches/topic/debian/use_debian-included_fonts.diff b/debian/patches/topic/debian/use_debian-included_fonts.diff
new file mode 100644
index 0000000..1dc1841
--- /dev/null
+++ b/debian/patches/topic/debian/use_debian-included_fonts.diff
@@ -0,0 +1,44 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/debian/use_debian-included_fonts
+
+Choose fonts that are included on a debian system.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/newview/app_settings/settings.xml | 6 +++---
+ 1 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
+index 27eacfb..da0c8cb 100644
+--- a/indra/newview/app_settings/settings.xml
++++ b/indra/newview/app_settings/settings.xml
+@@ -3466,7 +3466,7 @@
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+- <string>profontwindows.ttf</string>
++ <string>/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf</string>
+ </map>
+ <key>FontSansSerif</key>
+ <map>
+@@ -3477,7 +3477,7 @@
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+- <string>MtBkLfRg.ttf</string>
++ <string>/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf</string>
+ </map>
+ <key>FontSansSerifBold</key>
+ <map>
+@@ -3488,7 +3488,7 @@
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+- <string>MtBdLfRg.ttf</string>
++ <string>/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf</string>
+ </map>
+ <key>FontSansSerifFallback</key>
+ <map>
+--
+tg: (964668f..) topic/debian/use_debian-included_fonts (depends on: master)
diff --git a/debian/patches/topic/debian/use_debian_channel.diff b/debian/patches/topic/debian/use_debian_channel.diff
new file mode 100644
index 0000000..cd1cb6e
--- /dev/null
+++ b/debian/patches/topic/debian/use_debian_channel.diff
@@ -0,0 +1,25 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/debian/use_debian_channel
+
+Patch to use a custom viewer channel to connect to the SecondLife servers
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llcommon/llversionviewer.h | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
+index 258a5e2..cc34f10 100644
+--- a/indra/llcommon/llversionviewer.h
++++ b/indra/llcommon/llversionviewer.h
+@@ -37,6 +37,6 @@ const S32 LL_VERSION_MINOR = 22;
+ const S32 LL_VERSION_PATCH = 10;
+ const S32 LL_VERSION_BUILD = 0;
+
+-const char * const LL_CHANNEL = "Second Life Release";
++const char * const LL_CHANNEL = "Open Metaverse Viewer";
+
+ #endif
+--
+tg: (2e673c3..) topic/debian/use_debian_channel (depends on: master)
diff --git a/debian/patches/topic/debian/viewericon.diff b/debian/patches/topic/debian/viewericon.diff
new file mode 100644
index 0000000..dae55e0
--- /dev/null
+++ b/debian/patches/topic/debian/viewericon.diff
@@ -0,0 +1,717 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/debian/viewericon
+
+Adds an XPM icon to the build.
+This should be removed and the icon should be added directly in debian/ and not applied as a patch.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/newview/res/viewericon.xpm | 697 ++++++++++++++++++++++++++++++++++++++
+ 1 files changed, 697 insertions(+), 0 deletions(-)
+
+diff --git a/indra/newview/res/viewericon.xpm b/indra/newview/res/viewericon.xpm
+new file mode 100644
+index 0000000..7cf88ad
+--- /dev/null
++++ b/indra/newview/res/viewericon.xpm
+@@ -0,0 +1,697 @@
++/* XPM */
++static char * viewericon_xpm[] = {
++"32 32 662 2",
++" c None",
++". c #2594FF",
++"+ c #2693FF",
++"@ c #2894FF",
++"# c #2996FF",
++"$ c #2B98FF",
++"% c #2C9AFF",
++"& c #2E99FF",
++"* c #319AFF",
++"= c #319BFF",
++"- c #329CFF",
++"; c #339EFF",
++"> c #349EFF",
++", c #329DFF",
++"' c #3099FF",
++") c #2D99FF",
++"! c #2A97FF",
++"~ c #2191FF",
++"{ c #2692FF",
++"] c #2994FF",
++"^ c #2A96FF",
++"/ c #2B9AFF",
++"( c #329BFF",
++"_ c #359FFF",
++": c #35A0FF",
++"< c #36A0FF",
++"[ c #359EFF",
++"} c #2E98FF",
++"| c #2293FF",
++"1 c #2995FF",
++"2 c #339DFF",
++"3 c #369FFF",
++"4 c #2D9AFF",
++"5 c #2394FF",
++"6 c #2F98FF",
++"7 c #38A2FF",
++"8 c #39A2FF",
++"9 c #3BA4FF",
++"0 c #3AA3FF",
++"a c #38A1FF",
++"b c #37A1FF",
++"c c #309AFF",
++"d c #2593FF",
++"e c #2997FF",
++"f c #349FFF",
++"g c #3BA3FF",
++"h c #3CA4FF",
++"i c #3EA6FF",
++"j c #3EA7FF",
++"k c #3FA7FF",
++"l c #2D91FE",
++"m c #298BFC",
++"n c #298AFA",
++"o c #2989FA",
++"p c #2889FA",
++"q c #2688F8",
++"r c #2486F8",
++"s c #2386F8",
++"t c #2488FA",
++"u c #2E96FE",
++"v c #2893FF",
++"w c #3DA5FF",
++"x c #3FA8FF",
++"y c #2F96FE",
++"z c #1978F4",
++"A c #136DEE",
++"B c #126AEB",
++"C c #1269EA",
++"D c #116AE9",
++"E c #106AE8",
++"F c #1069E6",
++"G c #1068E6",
++"H c #116AEA",
++"I c #1C7BF7",
++"J c #2A94FF",
++"K c #3AA2FF",
++"L c #3DA6FF",
++"M c #40ADFF",
++"N c #41AEFF",
++"O c #43B0FF",
++"P c #2D95FD",
++"Q c #1472EF",
++"R c #0F69E6",
++"S c #0D67E2",
++"T c #0D66E1",
++"U c #0C65E0",
++"V c #0C64E0",
++"W c #0C63E0",
++"X c #0E64E2",
++"Y c #1874F2",
++"Z c #2C96FF",
++"` c #2E97FE",
++" . c #2A91FE",
++".. c #2C93FE",
++"+. c #2F94FE",
++"@. c #3097FE",
++"#. c #3098FE",
++"$. c #3197FE",
++"%. c #359BFE",
++"&. c #40ACFF",
++"*. c #44B1FF",
++"=. c #45B2FF",
++"-. c #46B2FF",
++";. c #47B3FF",
++">. c #3098FD",
++",. c #1475EE",
++"'. c #0E6AE6",
++"). c #0C69E2",
++"!. c #0C68E1",
++"~. c #0C67E0",
++"{. c #0C66E0",
++"]. c #0B63E0",
++"^. c #0E65E2",
++"/. c #1876F2",
++"(. c #2C97FF",
++"_. c #309BFF",
++":. c #1473F6",
++"<. c #1069F3",
++"[. c #106CF3",
++"}. c #1069F4",
++"|. c #1068F8",
++"1. c #106CF8",
++"2. c #106DF6",
++"3. c #116EF6",
++"4. c #1475F8",
++"5. c #39A3FE",
++"6. c #3099FE",
++"7. c #1478F0",
++"8. c #0E6CE7",
++"9. c #0C6AE4",
++"0. c #0E67E2",
++"a. c #1878F3",
++"b. c #319BFE",
++"c. c #116DF4",
++"d. c #106AF3",
++"e. c #106AF4",
++"f. c #1067F7",
++"g. c #106BF8",
++"h. c #106DF8",
++"i. c #106DF5",
++"j. c #1271F7",
++"k. c #1577F8",
++"l. c #36A0FE",
++"m. c #319AFE",
++"n. c #147AF0",
++"o. c #0E6EE8",
++"p. c #0C6CE6",
++"q. c #0C68E2",
++"r. c #0E68E4",
++"s. c #1A7BF4",
++"t. c #2D97FF",
++"u. c #329CFE",
++"v. c #126EF4",
++"w. c #1068F5",
++"x. c #1069F8",
++"y. c #106EF8",
++"z. c #106EF4",
++"A. c #1270F7",
++"B. c #1477F8",
++"C. c #1578F8",
++"D. c #369FFE",
++"E. c #319BFD",
++"F. c #1479EF",
++"G. c #0E6EE7",
++"H. c #0C6BE5",
++"I. c #0F6AE6",
++"J. c #1B7CF5",
++"K. c #1270F4",
++"L. c #106BF3",
++"M. c #1374F8",
++"N. c #369EFE",
++"O. c #3099FB",
++"P. c #1272E8",
++"Q. c #0C6ADF",
++"R. c #0C6AE0",
++"S. c #0C6CE4",
++"T. c #0F6AE7",
++"U. c #1C7CF5",
++"V. c #3EA5FF",
++"W. c #329DFE",
++"X. c #106DF7",
++"Y. c #106EF5",
++"Z. c #1271F8",
++"`. c #116DF0",
++" + c #0D62DE",
++".+ c #0D60DB",
++"++ c #257DF0",
++"@+ c #318CF6",
++"#+ c #318EF9",
++"$+ c #328EF9",
++"%+ c #2079EA",
++"&+ c #0B5ACA",
++"*+ c #0857C6",
++"=+ c #0A61D3",
++"-+ c #0C6CE5",
++";+ c #1C7AF5",
++">+ c #3A91FA",
++",+ c #3D94FA",
++"'+ c #3A92FA",
++")+ c #1969EC",
++"!+ c #1761EE",
++"~+ c #1764F0",
++"{+ c #1767EE",
++"]+ c #1868ED",
++"^+ c #1A6AEE",
++"/+ c #0D48BD",
++"(+ c #0A42AC",
++"_+ c #0B40AB",
++":+ c #164DCE",
++"<+ c #1D54DB",
++"[+ c #1D54DC",
++"}+ c #134AC5",
++"|+ c #083B9F",
++"1+ c #083CA2",
++"2+ c #0C4BB6",
++"3+ c #1261D4",
++"4+ c #1465DD",
++"5+ c #1365DE",
++"6+ c #1564DE",
++"7+ c #1766E2",
++"8+ c #2476EF",
++"9+ c #4599FC",
++"0+ c #469BFC",
++"a+ c #3A93FC",
++"b+ c #5273EE",
++"c+ c #5574EE",
++"d+ c #5272EC",
++"e+ c #2F53D4",
++"f+ c #2C4ED6",
++"g+ c #2C52D7",
++"h+ c #2C52D5",
++"i+ c #2E54D6",
++"j+ c #3053D4",
++"k+ c #1E3BA8",
++"l+ c #1C38A3",
++"m+ c #1D38A3",
++"n+ c #2C40BC",
++"o+ c #3545C6",
++"p+ c #3544C6",
++"q+ c #283EB6",
++"r+ c #1A349B",
++"s+ c #193398",
++"t+ c #1E3AA3",
++"u+ c #2A4FC1",
++"v+ c #2C55CA",
++"w+ c #2B55CC",
++"x+ c #2C55CC",
++"y+ c #2C55CD",
++"z+ c #2F56CF",
++"A+ c #3E61DA",
++"B+ c #5C78F0",
++"C+ c #5E79F2",
++"D+ c #5272F5",
++"E+ c #5573EE",
++"F+ c #5271EC",
++"G+ c #2C4ED5",
++"H+ c #2D52D3",
++"I+ c #2E55D4",
++"J+ c #3154D3",
++"K+ c #1E3AA7",
++"L+ c #1C39A2",
++"M+ c #1C38A2",
++"N+ c #2C42BC",
++"O+ c #3444C4",
++"P+ c #3344C3",
++"Q+ c #2D42BF",
++"R+ c #203AA9",
++"S+ c #1C36A2",
++"T+ c #2440B0",
++"U+ c #385DD0",
++"V+ c #3C63D6",
++"W+ c #3B62D6",
++"X+ c #3C62D7",
++"Y+ c #3D64D8",
++"Z+ c #4C6FE4",
++"`+ c #5C78EE",
++" @ c #5170F2",
++".@ c #5272EB",
++"+@ c #5273EB",
++"@@ c #5172EA",
++"#@ c #385CD9",
++"$@ c #2E51D4",
++"%@ c #2E54D4",
++"&@ c #2F54D2",
++"*@ c #3056D4",
++"=@ c #3256D2",
++"-@ c #1E3AA6",
++";@ c #1D39A2",
++">@ c #1F3BA9",
++",@ c #2C43BE",
++"'@ c #3244C2",
++")@ c #3244C1",
++"!@ c #3247C5",
++"~@ c #314CCA",
++"{@ c #385AD4",
++"]@ c #557DEF",
++"^@ c #557EEF",
++"/@ c #547DEE",
++"(@ c #517EEE",
++"_@ c #507EEE",
++":@ c #4C7AEC",
++"<@ c #4C78EA",
++"[@ c #4770EE",
++"}@ c #5072E7",
++"|@ c #5173E7",
++"1@ c #5474E6",
++"2@ c #5574E7",
++"3@ c #5575E8",
++"4@ c #5776E8",
++"5@ c #5978E9",
++"6@ c #5B78EA",
++"7@ c #3147C2",
++"8@ c #2D42BD",
++"9@ c #2C42BE",
++"0@ c #3043BE",
++"a@ c #3144BE",
++"b@ c #3144BF",
++"c@ c #3153CC",
++"d@ c #3058CD",
++"e@ c #3661D5",
++"f@ c #4F7DEC",
++"g@ c #507EEC",
++"h@ c #4D7EEB",
++"i@ c #4C7EEB",
++"j@ c #4B7FEA",
++"k@ c #4B7FE9",
++"l@ c #4A7EEA",
++"m@ c #467BE9",
++"n@ c #467AE5",
++"o@ c #4370E8",
++"p@ c #4E72E2",
++"q@ c #4F72E2",
++"r@ c #5072E2",
++"s@ c #5574E2",
++"t@ c #5877E5",
++"u@ c #5878E5",
++"v@ c #5978E6",
++"w@ c #5C7DE8",
++"x@ c #5D7CE9",
++"y@ c #2F48C0",
++"z@ c #2A42BA",
++"A@ c #2D41BA",
++"B@ c #3042BA",
++"C@ c #3043BA",
++"D@ c #3043B9",
++"E@ c #3046BD",
++"F@ c #2E57C8",
++"G@ c #2E58C8",
++"H@ c #3460D0",
++"I@ c #4F7DE8",
++"J@ c #4C7EE6",
++"K@ c #4A7FE6",
++"L@ c #4A7FE5",
++"M@ c #4A7FE4",
++"N@ c #477CE4",
++"O@ c #457AE4",
++"P@ c #4478DE",
++"Q@ c #406FE4",
++"R@ c #4B70DE",
++"S@ c #4D72DE",
++"T@ c #4E72DE",
++"U@ c #5073DD",
++"V@ c #5675DF",
++"W@ c #5676E1",
++"X@ c #587AE2",
++"Y@ c #5A7CE4",
++"Z@ c #2E48BB",
++"`@ c #2942B6",
++" # c #2A42B6",
++".# c #2E42B5",
++"+# c #2E43B5",
++"@# c #2E42B4",
++"## c #2D44B6",
++"$# c #2C56C4",
++"%# c #2C58C4",
++"&# c #3260CC",
++"*# c #4B7DE2",
++"=# c #497DE0",
++"-# c #477DE0",
++";# c #487DE0",
++"># c #467EE0",
++",# c #467EDF",
++"'# c #457DDE",
++")# c #437ADD",
++"!# c #4279DD",
++"~# c #4177D9",
++"{# c #3E6EDE",
++"]# c #486ED8",
++"^# c #4B71D9",
++"/# c #4B72D9",
++"(# c #4D72D8",
++"_# c #5374DA",
++":# c #5475DB",
++"<# c #5578DC",
++"[# c #567ADD",
++"}# c #2C48B7",
++"|# c #2742B1",
++"1# c #2A42B1",
++"2# c #2C42B1",
++"3# c #2C42B0",
++"4# c #2B44B2",
++"5# c #2A56BF",
++"6# c #2A57BF",
++"7# c #315EC7",
++"8# c #467CDC",
++"9# c #457CDA",
++"0# c #447CDA",
++"a# c #437CDA",
++"b# c #437CD8",
++"c# c #427CD8",
++"d# c #427AD8",
++"e# c #4077D7",
++"f# c #3F76D5",
++"g# c #3F75D4",
++"h# c #3C6FD8",
++"i# c #466ED2",
++"j# c #486ED4",
++"k# c #4A70D4",
++"l# c #4A70D3",
++"m# c #4E72D4",
++"n# c #5174D6",
++"o# c #5074D6",
++"p# c #5276D6",
++"q# c #5277D6",
++"r# c #2A46B2",
++"s# c #2540AC",
++"t# c #2840AC",
++"u# c #2A42AC",
++"v# c #2A41AC",
++"w# c #2A44AE",
++"x# c #2955BA",
++"y# c #2856BA",
++"z# c #2E5EC2",
++"A# c #447AD6",
++"B# c #437AD5",
++"C# c #417AD4",
++"D# c #407AD2",
++"E# c #3F79D1",
++"F# c #3E78D0",
++"G# c #3E76D1",
++"H# c #3E75D2",
++"I# c #3C74CF",
++"J# c #3B73CE",
++"K# c #3A6FD5",
++"L# c #436CCC",
++"M# c #456ECE",
++"N# c #466FCE",
++"O# c #4A71CE",
++"P# c #4E73D0",
++"Q# c #4E74D0",
++"R# c #4F74D0",
++"S# c #4E74CF",
++"T# c #2846AD",
++"U# c #2440A8",
++"V# c #2340A8",
++"W# c #2640A7",
++"X# c #2740A7",
++"Y# c #2841A7",
++"Z# c #2843A9",
++"`# c #2652B5",
++" $ c #2552B5",
++".$ c #2B5CBC",
++"+$ c #417AD0",
++"@$ c #3F79CE",
++"#$ c #3E78CC",
++"$$ c #3C76CA",
++"%$ c #3B76C8",
++"&$ c #3B74C9",
++"*$ c #3B74CC",
++"=$ c #3A73CD",
++"-$ c #3871CA",
++";$ c #3870C9",
++">$ c #386ED0",
++",$ c #426CC7",
++"'$ c #426CC9",
++")$ c #446EC9",
++"!$ c #466FC9",
++"~$ c #4A72CA",
++"{$ c #4C73CC",
++"]$ c #4B72CB",
++"^$ c #2E4FB0",
++"/$ c #2340A4",
++"($ c #223FA4",
++"_$ c #223FA3",
++":$ c #233FA3",
++"<$ c #2442A5",
++"[$ c #224FB0",
++"}$ c #2350B2",
++"|$ c #2E62BC",
++"1$ c #3E79CB",
++"2$ c #3B77C8",
++"3$ c #3A76C5",
++"4$ c #3873C2",
++"5$ c #3771C2",
++"6$ c #3771C5",
++"7$ c #3771C8",
++"8$ c #3770C6",
++"9$ c #3670C5",
++"0$ c #366FC6",
++"a$ c #346CCD",
++"b$ c #3F6BC2",
++"c$ c #406BC2",
++"d$ c #426DC4",
++"e$ c #426EC4",
++"f$ c #456FC4",
++"g$ c #4A72C6",
++"h$ c #4A73C7",
++"i$ c #4A72C7",
++"j$ c #466EC3",
++"k$ c #3E65BC",
++"l$ c #4065BD",
++"m$ c #4166BD",
++"n$ c #4066BD",
++"o$ c #4066BC",
++"p$ c #3F66BD",
++"q$ c #346BBF",
++"r$ c #356DC0",
++"s$ c #3B76C6",
++"t$ c #3B78C4",
++"u$ c #3874C1",
++"v$ c #3672BE",
++"w$ c #3570BD",
++"x$ c #346EBF",
++"y$ c #346EC1",
++"z$ c #346DC1",
++"A$ c #346CC0",
++"B$ c #346DC0",
++"C$ c #346EC2",
++"D$ c #316AC9",
++"E$ c #3C6ABE",
++"F$ c #3D6BBE",
++"G$ c #3F6CC0",
++"H$ c #416CC0",
++"I$ c #426DC0",
++"J$ c #4570C0",
++"K$ c #4872C2",
++"L$ c #4A76C4",
++"M$ c #4E79C7",
++"N$ c #4F7AC7",
++"O$ c #507AC8",
++"P$ c #4E79C8",
++"Q$ c #3D79C4",
++"R$ c #3C7AC4",
++"S$ c #3A79C2",
++"T$ c #3776BF",
++"U$ c #3572BB",
++"V$ c #336FBA",
++"W$ c #326DBB",
++"X$ c #326CBD",
++"Y$ c #326ABD",
++"Z$ c #3169BB",
++"`$ c #3169BA",
++" % c #316ABB",
++".% c #2E66C3",
++"+% c #396ABA",
++"@% c #3C6BBA",
++"#% c #3C6ABB",
++"$% c #3E6CBD",
++"%% c #406CBD",
++"&% c #406DBD",
++"*% c #426EBD",
++"=% c #456FBD",
++"-% c #4672BF",
++";% c #4672C0",
++">% c #4874C0",
++",% c #4976C1",
++"'% c #4B78C2",
++")% c #4C78C3",
++"!% c #4E78C3",
++"~% c #4B78C3",
++"{% c #3C78C1",
++"]% c #3A79C1",
++"^% c #3778BE",
++"/% c #3673BA",
++"(% c #326EB8",
++"_% c #326DBA",
++":% c #326BBB",
++"<% c #316ABA",
++"[% c #2F68B9",
++"}% c #2E66B8",
++"|% c #3067BB",
++"1% c #2C63BF",
++"2% c #3769B8",
++"3% c #3969B8",
++"4% c #3B6AB8",
++"5% c #3C6AB8",
++"6% c #3E6CBA",
++"7% c #3F6DBA",
++"8% c #416DBA",
++"9% c #446FBB",
++"0% c #4672BC",
++"a% c #4672BD",
++"b% c #4674BC",
++"c% c #4674BD",
++"d% c #4775BE",
++"e% c #4575BE",
++"f% c #3A76C0",
++"g% c #3776BE",
++"h% c #3573BB",
++"i% c #326EB6",
++"j% c #306CB7",
++"k% c #316CBB",
++"l% c #3068B8",
++"m% c #2E66B9",
++"n% c #2E66BA",
++"o% c #3068BE",
++"p% c #2B62C0",
++"q% c #3769B6",
++"r% c #3A68B6",
++"s% c #3B69B6",
++"t% c #406DBA",
++"u% c #436EBA",
++"v% c #4571BB",
++"w% c #4372BC",
++"x% c #3774BE",
++"y% c #3875C3",
++"z% c #316DB6",
++"A% c #2F6BB5",
++"B% c #306BB9",
++"C% c #326ABB",
++"D% c #3169BD",
++"E% c #3169BF",
++"F% c #3169C0",
++"G% c #3067BE",
++"H% c #2E66BC",
++"I% c #285FBC",
++"J% c #3667B5",
++"K% c #3768B6",
++"L% c #3C6BB9",
++"M% c #416EBA",
++"N% c #4470BA",
++"O% c #4570BB",
++"P% c #3873BD",
++"Q% c #3A76C6",
++"R% c #3974C3",
++"S% c #336FBC",
++"T% c #316CB9",
++"U% c #326CBB",
++"V% c #336CBD",
++"W% c #346CC2",
++"X% c #326AC2",
++"Y% c #3068C1",
++"Z% c #2B64B8",
++"`% c #245AB6",
++" & c #3466B5",
++".& c #3668B5",
++"+& c #3868B6",
++"@& c #3F6CBA",
++"#& c #426FBA",
++"$& c #436FBA",
++"%& c #4271BA",
++"&& c #3772BB",
++"*& c #3975C3",
++"=& c #3974C4",
++"-& c #3772C2",
++";& c #346FBF",
++">& c #326CBC",
++",& c #3169BC",
++"'& c #2D65BC",
++")& c #2961B5",
++"!& c #2861B4",
++"~& c #2056B2",
++". . + @ # $ % % & * * * = - - ; ; > > ; , - - * * * ' ) % % ! ~ ",
++". { ] ^ / % ) ' * * ( , > > _ : < < < : : [ > ; - * * * } % % | ",
++"+ 1 ^ % % & * * ( 2 > 3 < < < < < < < < < < < 3 _ > - * * ' 4 5 ",
++"@ ^ % % 6 * * - > 3 < < < < 7 8 9 9 0 0 a b < < < < [ ; = * c d ",
++"e / % ' * * , f < < < 7 g h i j j j j j i i 9 8 b < < 3 > - * + ",
++"$ % & * * ; [ < < a 9 i j k k k k h l m n o p p q r s t u > - v ",
++"% ) * * , _ < < 8 w k k k k k k x y z A B C D E F G G H I [ > J ",
++"% ' * , [ < < K L k k k k x M N O P Q R S T U U V W W X Y > < Z ",
++"& * = ` . ...+. at .#.$.%.&.*.=.-.;.>.,.'.).!.~.{.U ].].^./.< < (.",
++"c * _.:.<.[.}.|.1.2.3.4.5.;.;.;.;.6.7.8.9.).).!.{.U V 0.a.8 b (.",
++"* ( b.c.d.e.f.g.h.i.j.k.l.;.;.;.;.m.n.o.p.p.9.).q.{.{.r.s.h b t.",
++"* , u.v.[.w.x.y.z.A.B.C.D.;.;.;.;.E.F.G.p.p.p.H.).q.~.I.J.w 9 } ",
++"* > u.K.L.f.1.2.3.M.C.C.N.;.;.;.;.O.P.Q.R.S.p.p.H.).).T.U.V.w 6 ",
++"- > W.K.w.|.X.Y.Z.`. +.+++ at +@+#+$+%+&+*+=+Q.-+p.p.9.).E ;+V.i - ",
++">+,+'+)+!+~+{+]+^+/+(+_+:+<+<+[+[+}+|+1+2+3+4+5+5+5+6+7+8+9+0+a+",
++"b+c+d+e+f+g+h+i+j+k+l+m+n+o+o+o+p+q+r+s+t+u+v+w+w+x+y+z+A+B+C+D+",
++"b+E+F+e+G+h+H+I+J+K+L+M+N+O+O+P+P+Q+R+S+T+U+V+V+W+W+X+Y+Z+`+`+ @",
++". at +@@@#@$@%@&@*@=@-@;@>@,@'@)@)@)@)@!@~@{@]@^@/@(@(@_ at _@_@:@<@[@",
++"}@|@|@1 at 2@3 at 4@5 at 6@7 at 8@9 at 0@a at a@a at a@b at c@d at e@f at g@h at i@j at j@k at l@m at n@o@",
++"p at q@r at s@t at u@v at w@x at y@z at A@B at C@D at D@D at E@F at G@H at I@J at K@L at L@L at M@N at O@P at Q@",
++"R at S@T at U@V at W@W at X@Y at Z@`@ #.#+#+#+#@###$#%#&#*#=#-#;#>#,#'#)#!#~#{#",
++"]#^#/#(#_#:#:#<#[#}#|#|#1#2#2#3#3#4#5#6#7#8#9#0#a#b#c#d#e#f#g#h#",
++"i#j#k#l#m#n#o#p#q#r#s#s#s#t#u#v#u#w#x#y#z#A#B#C#D#E#F#G#H#I#J#K#",
++"L#M#N#N#O#P#Q#R#S#T#U#U#V#V#W#X#Y#Z#`# $.$+$@$#$$$%$&$*$=$-$;$>$",
++",$'$)$)$!$~${${$]$^$/$($_$_$_$_$:$<$[$}$|$1$2$3$4$5$6$7$8$9$0$a$",
++"b$c$d$e$e$f$g$h$i$j$k$l$m$m$n$n$o$p$q$r$s$t$u$v$w$x$y$z$A$B$C$D$",
++"E$F$G$H$I$I$J$K$K$K$K$L$M$N$O$O$O$P$Q$R$S$T$U$V$W$X$Y$Z$`$ %Y$.%",
++"+%@%#%$%%%&%*%=%-%;%;%;%>%,%'%)%!%~%{%]%^%/%(%_%:%<%[%}%}%}%|%1%",
++"2%3%4%5%6%7%7%8%9%0%a%a%0%0%b%c%d%e%f%g%h%i%j%k%<%l%}%m%m%n%o%p%",
++"q%q%r%s%5%6%7%7%t%u%v%0%0%0%0%0%0%w%x%y%v$z%A%B%C%Z$D%E%F%G%H%I%",
++"J%q%K%s%s%L%6%7%7%7%M%N%O%0%0%0%0%w%P%Q%R%S%T%U%V%A$W%X%Y%H%Z%`%",
++" &.&q%+&s%s%5%6%@&7%7%t%t%#&$&N%N%%&&&*&=&-&;&>&C%,&o%'&)&!&!&~&"};
+--
+tg: (c89fb97..) topic/debian/viewericon (depends on: master)
diff --git a/debian/patches/topic/features/jira-backported/AvatarLoginList.diff b/debian/patches/topic/features/jira-backported/AvatarLoginList.diff
new file mode 100644
index 0000000..4f1da11
--- /dev/null
+++ b/debian/patches/topic/features/jira-backported/AvatarLoginList.diff
@@ -0,0 +1,512 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/features/jira-backported/AvatarLoginList
+
+Add login choice for multiple avatars
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llui/llcombobox.cpp | 8 ++
+ indra/llui/llcombobox.h | 1 +
+ indra/newview/app_settings/settings.xml | 44 ++++++++++++
+ indra/newview/llpanelgeneral.cpp | 8 ++-
+ indra/newview/llpanellogin.cpp | 70 +++++++++++++++++++-
+ indra/newview/llpanellogin.h | 2 +
+ indra/newview/llstartup.cpp | 43 ++++++++++++
+ indra/newview/llviewercontrol.cpp | 28 ++++++++
+ .../skins/default/xui/en-us/panel_login.xml | 12 ++--
+ .../xui/en-us/panel_preferences_general.xml | 66 ++++++++++++-------
+ 10 files changed, 249 insertions(+), 33 deletions(-)
+
+diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
+index 3aec2ee..27c1ace 100644
+--- a/indra/llui/llcombobox.cpp
++++ b/indra/llui/llcombobox.cpp
+@@ -450,6 +450,14 @@ void LLComboBox::setButtonVisible(BOOL visible)
+ }
+ }
+
++void LLComboBox::setTextEntryVisible(BOOL visible)
++{
++ if (mTextEntry)
++ {
++ mTextEntry->setVisible(visible);
++ }
++}
++
+ void LLComboBox::draw()
+ {
+ mButton->setEnabled(getEnabled() /*&& !mList->isEmpty()*/);
+diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h
+index efcb798..5eac287 100644
+--- a/indra/llui/llcombobox.h
++++ b/indra/llui/llcombobox.h
+@@ -173,6 +173,7 @@ public:
+ void setTextEntryCallback( void (*cb)(LLLineEditor*, void*) ) { mTextEntryCallback = cb; }
+
+ void setButtonVisible(BOOL visible);
++ void setTextEntryVisible(BOOL visible);
+
+ static void onButtonDown(void *userdata);
+ static void onItemSelected(LLUICtrl* item, void *userdata);
+diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
+index 27eacfb..bdb7bc2 100644
+--- a/indra/newview/app_settings/settings.xml
++++ b/indra/newview/app_settings/settings.xml
+@@ -4158,6 +4158,50 @@
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
++ <key>LoginMRUClearPassword</key>
++ <map>
++ <key>Comment</key>
++ <string>Clear password upon changing the avatar name on the login screen</string>
++ <key>Persist</key>
++ <integer>1</integer>
++ <key>Type</key>
++ <string>Boolean</string>
++ <key>Value</key>
++ <integer>1</integer>
++ </map>
++ <key>LoginMRUEnabled</key>
++ <map>
++ <key>Comment</key>
++ <string>Allow saving a list of alternate avatar names upon login</string>
++ <key>Persist</key>
++ <integer>1</integer>
++ <key>Type</key>
++ <string>Boolean</string>
++ <key>Value</key>
++ <integer>0</integer>
++ </map>
++ <key>LoginMRUList</key>
++ <map>
++ <key>Comment</key>
++ <string>List of previous avatar names presented at login</string>
++ <key>Persist</key>
++ <integer>1</integer>
++ <key>Type</key>
++ <string>LLSD</string>
++ <key>Value</key>
++ <array/>
++ </map>
++ <key>LoginMRULength</key>
++ <map>
++ <key>Comment</key>
++ <string>Number of names to store in LoginMRUList</string>
++ <key>Persist</key>
++ <integer>1</integer>
++ <key>Type</key>
++ <string>S32</string>
++ <key>Value</key>
++ <integer>10</integer>
++ </map>
+ <key>LSLFindCaseInsensitivity</key>
+ <map>
+ <key>Comment</key>
+diff --git a/indra/newview/llpanelgeneral.cpp b/indra/newview/llpanelgeneral.cpp
+index f370116..0067369 100644
+--- a/indra/newview/llpanelgeneral.cpp
++++ b/indra/newview/llpanelgeneral.cpp
+@@ -40,6 +40,7 @@
+ #include "lluictrlfactory.h"
+ #include "llurlsimstring.h"
+ #include "llviewercontrol.h"
++#include "llstartup.h"
+
+ LLPanelGeneral::LLPanelGeneral()
+ {
+@@ -70,6 +71,9 @@ BOOL LLPanelGeneral::postBuild()
+ childSetValue("ui_scale_slider", gSavedSettings.getF32("UIScaleFactor"));
+ childSetValue("ui_auto_scale", gSavedSettings.getBOOL("UIAutoScale"));
+
++ childSetValue("save_names_checkbox", gSavedSettings.getBOOL("LoginMRUEnabled"));
++ childSetValue("clear_password_checkbox", gSavedSettings.getBOOL("LoginMRUClearPassword"));
++
+ LLComboBox* crash_behavior_combobox = getChild<LLComboBox>("crash_behavior_combobox");
+ crash_behavior_combobox->setCurrentByIndex(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING));
+
+@@ -87,7 +91,9 @@ void LLPanelGeneral::apply()
+ {
+ LLComboBox* fade_out_combobox = getChild<LLComboBox>("fade_out_combobox");
+ gSavedSettings.setS32("RenderName", fade_out_combobox->getCurrentIndex());
+-
++
++ gSavedSettings.setBOOL("LoginMRUEnabled", childGetValue("save_names_checkbox"));
++ gSavedSettings.setBOOL("LoginMRUClearPassword", childGetValue("clear_password_checkbox"));
+ gSavedSettings.setBOOL("LoginLastLocation", childGetValue("default_start_location").asString() == "MyLastLocation");
+ gSavedSettings.setBOOL("ShowStartLocation", childGetValue("show_location_checkbox"));
+ gSavedSettings.setBOOL("RenderHideGroupTitleAll", childGetValue("show_all_title_checkbox"));
+diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
+index 64eecd6..cbdd42c 100644
+--- a/indra/newview/llpanellogin.cpp
++++ b/indra/newview/llpanellogin.cpp
+@@ -393,6 +393,33 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
+ childSetPrevalidate("first_name_edit", LLLineEditor::prevalidatePrintableNoSpace);
+ childSetPrevalidate("last_name_edit", LLLineEditor::prevalidatePrintableNoSpace);
+
++ LLComboBox* name_combo = getChild<LLComboBox>("name_combo");
++ // Only works as intended with this combination of flags,
++ // and in this order of setting them, therefore overriding xui
++ name_combo->setAllowTextEntry(TRUE);
++ name_combo->setTextEntryVisible(FALSE);
++ // Send it down the line, or the invisible text entry covers up our normal
++ // input fields if placed after them in xui
++ sendChildToBack(getChildView("name_combo"));
++
++ bool mru_enabled = gSavedSettings.getBOOL("LoginMRUEnabled");
++ if (mru_enabled)
++ {
++ LLSD name_list = gSavedSettings.getLLSD("LoginMRUList");
++ if (name_list.isArray())
++ {
++ for (LLSD::array_iterator iter = name_list.endArray() - 1; iter != name_list.beginArray()-1; --iter)
++ {
++ if (iter->isMap() && iter->has("first") && iter->has("last"))
++ {
++ name_combo->add((*iter)["first"].asString() + " " + (*iter)["last"].asString(), *iter);
++ }
++ }
++ }
++ childSetCommitCallback("name_combo", onSelectLoginMRU, this);
++ }
++ setLoginMRUEnabled(mru_enabled);
++
+ childSetCommitCallback("password_edit", mungePassword);
+ childSetKeystrokeCallback("password_edit", onPassKey, this);
+ childSetUserData("password_edit", this);
+@@ -941,8 +968,10 @@ void LLPanelLogin::loadLoginPage()
+ char* curl_channel = curl_escape(gSavedSettings.getString("VersionChannelName").c_str(), 0);
+ char* curl_version = curl_escape(version.c_str(), 0);
+
+- oStr << "&channel=" << curl_channel;
+- oStr << "&version=" << curl_version;
++ //Hide the channel and version from the webserver or else
++ //it does not return a nice picture for us ;-(
++ //oStr << "&channel=" << curl_channel;
++ //oStr << "&version=" << curl_version;
+
+ curl_free(curl_channel);
+ curl_free(curl_version);
+@@ -1214,3 +1243,40 @@ void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe, void*)
+ onSelectServer(combo, NULL);
+ }
+ }
++
++// static
++void LLPanelLogin::setLoginMRUEnabled(bool enabled)
++{
++ if (!sInstance)
++ {
++ return;
++ }
++
++ LLComboBox* combo = sInstance->getChild<LLComboBox>("name_combo");
++ if (!enabled) combo->removeall();
++ combo->setButtonVisible(enabled);
++ combo->setEnabled(enabled && combo->getItemCount() != 0);
++}
++
++//static
++void LLPanelLogin::onSelectLoginMRU(LLUICtrl* caller, void* user_data)
++{
++ LLPanelLogin* panel = (LLPanelLogin*) user_data;
++ LLComboBox* name_combo = (LLComboBox*) caller;
++ LLLineEditor* first_name_edit = panel->getChild<LLLineEditor>("first_name_edit");
++ LLLineEditor* last_name_edit = panel->getChild<LLLineEditor>("last_name_edit");
++ LLLineEditor* password_edit = panel->getChild<LLLineEditor>("password_edit");
++
++ LLSD selected_name = name_combo->getValue();
++ std::string first_name = selected_name["first"].asString();
++ std::string last_name = selected_name["last"].asString();
++
++ if (gSavedSettings.getBOOL("LoginMRUClearPassword")
++ && (first_name_edit->getText() != first_name || last_name_edit->getText() != last_name))
++ {
++ password_edit->setText(LLStringUtil::null);
++ }
++ first_name_edit->setText(first_name);
++ last_name_edit->setText(last_name);
++ password_edit->setFocus(TRUE);
++}
+diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h
+index dba6cf4..2de5dde 100644
+--- a/indra/newview/llpanellogin.h
++++ b/indra/newview/llpanellogin.h
+@@ -85,6 +85,7 @@ public:
+
+ static void setFields(const std::string& firstname, const std::string& lastname,
+ const std::string& password, BOOL remember);
++ static void setLoginMRUEnabled(bool enabled);
+
+ static void addServer(const std::string& server, S32 domain_name);
+ static void refreshLocation( bool force_visible );
+@@ -115,6 +116,7 @@ private:
+ static void onPassKey(LLLineEditor* caller, void* user_data);
+ static void onSelectServer(LLUICtrl*, void*);
+ static void onServerComboLostFocus(LLFocusableElement*, void*);
++ static void onSelectLoginMRU(LLUICtrl*, void*);
+
+ private:
+ LLPointer<LLUIImage> mLogoImage;
+diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
+index 5f25dc3..fe91ec1 100644
+--- a/indra/newview/llstartup.cpp
++++ b/indra/newview/llstartup.cpp
+@@ -1341,6 +1341,49 @@ bool idle_startup()
+ gSavedSettings.setString("FirstName", firstname);
+ gSavedSettings.setString("LastName", lastname);
+
++ if (gSavedSettings.getBOOL("LoginMRUEnabled"))
++ {
++ LLSD mru_list = gSavedSettings.getLLSD("LoginMRUList");
++ if (mru_list.isArray())
++ {
++ // Prepend the current login name to the MRU
++ LLSD login_entry(LLSD::emptyMap());
++ login_entry["first"] = firstname;
++ login_entry["last"] = lastname;
++ mru_list.insert(0, login_entry);
++
++ const S32 names_max = gSavedSettings.getS32("LoginMRULength");
++ for (S32 i = 1; i != mru_list.size(); )
++ {
++ // Trim excess entries
++ if (i >= names_max)
++ {
++ mru_list.erase(i);
++ continue;
++ }
++ // Remove any other instances of the current name
++ login_entry = mru_list[i];
++ if (login_entry.isMap() && login_entry.has("first") && login_entry.has("last"))
++ {
++ if (login_entry["first"].asString() == firstname
++ && login_entry["last"].asString() == lastname)
++ {
++ mru_list.erase(i);
++ }
++ else
++ {
++ ++i;
++ }
++ }
++ }
++ gSavedSettings.setLLSD("LoginMRUList", mru_list);
++ }
++ }
++ else
++ {
++ gSavedSettings.setLLSD("LoginMRUList", LLSD::emptyArray());
++ }
++
+ if (remember_password)
+ {
+ save_password_to_disk(password.c_str());
+diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
+index df39ee7..3bcfa12 100644
+--- a/indra/newview/llviewercontrol.cpp
++++ b/indra/newview/llviewercontrol.cpp
+@@ -70,6 +70,8 @@
+ #include "llvosurfacepatch.h"
+ #include "llvowlsky.h"
+ #include "llrender.h"
++#include "llstartup.h"
++#include "llpanellogin.h"
+
+ #ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ BOOL gHackGodmode = FALSE;
+@@ -429,6 +431,31 @@ bool handleVoiceClientPrefsChanged(const LLSD& newvalue)
+ return true;
+ }
+
++bool handleLoginMRUPrefsChanged(const LLSD& newvalue)
++{
++ bool enabled = newvalue.asBoolean();
++ if (enabled)
++ {
++ // Initialize the list with the current name if logged in
++ if (LLStartUp::getStartupState() >= STATE_WORLD_INIT)
++ {
++ // list was previously empty, no need to go through sorting
++ LLSD names = LLSD::emptyArray();
++ names.append(LLSD::emptyMap());
++ names[0]["first"] = gSavedSettings.getString("FirstName");
++ names[0]["last"] = gSavedSettings.getString("LastName");
++ gSavedSettings.setLLSD("LoginMRUList", names);
++ }
++ }
++ else
++ {
++ // Clear the list of avatar names when disabled
++ gSavedSettings.setLLSD("LoginMRUList", LLSD::emptyArray());
++ }
++ LLPanelLogin::setLoginMRUEnabled(enabled);
++ return true;
++}
++
+ ////////////////////////////////////////////////////////////////////////////
+
+ void settings_setup_listeners()
+@@ -556,6 +583,7 @@ void settings_setup_listeners()
+ gSavedSettings.getControl("VoiceOutputAudioDevice")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
+ gSavedSettings.getControl("AudioLevelMic")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
+ gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
++ gSavedSettings.getControl("LoginMRUEnabled")->getSignal()->connect(boost::bind(&handleLoginMRUPrefsChanged, _1));
+ }
+
+ template <> eControlType get_control_type<U32>(const U32& in, LLSD& out)
+diff --git a/indra/newview/skins/default/xui/en-us/panel_login.xml b/indra/newview/skins/default/xui/en-us/panel_login.xml
+index 2122ee7..b048f94 100644
+--- a/indra/newview/skins/default/xui/en-us/panel_login.xml
++++ b/indra/newview/skins/default/xui/en-us/panel_login.xml
+@@ -11,26 +11,28 @@
+ <string name="forgot_password_url">
+ http://secondlife.com/account/request.php
+ </string>
++ <combo_box name="name_combo" allow_text_entry="true" follows="left|bottom"
++ font="SansSerif" left="32" bottom="30" width="252" height="20" mouse_opaque="true"/>
+ <text bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
+ bottom="54" drop_shadow_visible="true" follows="left|bottom"
+ font="SansSerif" h_pad="0" halign="left" height="16"
+- left="32" mouse_opaque="true" name="first_name_text" v_pad="0" width="120">
++ left="32" mouse_opaque="true" name="first_name_text" v_pad="0" width="114">
+ First Name:
+ </text>
+ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="-24"
+ follows="left|bottom" font="SansSerif" handle_edit_keys_directly="true"
+ height="20" left="32" max_length="31" mouse_opaque="true"
+- name="first_name_edit" select_all_on_focus_received="true" width="120" />
++ name="first_name_edit" select_all_on_focus_received="true" width="118" />
+ <text bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
+ bottom="54" drop_shadow_visible="true" follows="left|bottom"
+ font="SansSerif" h_pad="0" halign="left" height="16"
+- left="164" mouse_opaque="true" name="last_name_text" v_pad="0" width="120">
++ left_delta="117" mouse_opaque="true" name="last_name_text" v_pad="0" width="114">
+ Last Name:
+ </text>
+ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="-24"
+ follows="left|bottom" font="SansSerif" handle_edit_keys_directly="true"
+- height="20" left="164" max_length="31" mouse_opaque="true"
+- name="last_name_edit" select_all_on_focus_received="true" width="120" />
++ height="20" left_delta="0" max_length="31" mouse_opaque="true"
++ name="last_name_edit" select_all_on_focus_received="true" width="114" />
+ <text bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
+ bottom="54" drop_shadow_visible="true" follows="left|bottom"
+ font="SansSerif" h_pad="0" halign="left" height="16"
+diff --git a/indra/newview/skins/default/xui/en-us/panel_preferences_general.xml b/indra/newview/skins/default/xui/en-us/panel_preferences_general.xml
+index ea04dfc..8a19967 100644
+--- a/indra/newview/skins/default/xui/en-us/panel_preferences_general.xml
++++ b/indra/newview/skins/default/xui/en-us/panel_preferences_general.xml
+@@ -2,22 +2,49 @@
+ <panel border="true" bottom="-409" enabled="true" follows="left|top|right|bottom"
+ height="408" label="General" left="102" mouse_opaque="true"
+ name="general_panel" width="517">
+- <radio_group bottom="-45" draw_border="false" follows="left|bottom" height="40" left="151"
++ <text type="string" length="1" bottom="-25" follows="left|top" font="SansSerifSmall" h_pad="0"
++ halign="left" height="16" left="10" name="alt_avatars_textbox" v_pad="0"
++ width="394">
++ Recent Logins:
++ </text>
++ <check_box name="save_names_checkbox" follows="left|top"
++ left="151" bottom_delta="0" width="149" height="16"
++ label="Save name list" initial_value="true"
++ radio_style="false" font="SansSerifSmall"
++ tool_tip="Save a list of recently used avatar names.\nYou may wish to turn this off if more than one person has access to this computer."/>
++ <check_box bottom_delta="0" follows="left|top"
++ font="SansSerifSmall" height="16" initial_value="false"
++ label="Clear password when selecting" left_delta="105" mouse_opaque="true"
++ tool_tip="Clears the password entry box when selecting a different avatar name at login."
++ name="clear_password_checkbox" radio_style="false" width="200" />
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-22" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" left="10"
++ mouse_opaque="true" name="start_location_textbox" v_pad="0" width="394">
++ Start Location:
++ </text>
++ <radio_group bottom_delta="-22" draw_border="false" follows="left|bottom" height="40" left="151"
+ name="default_start_location" width="220">
+ <radio_item bottom="-20" height="20" left="0" name="MyHome" width="50"
+ tool_tip="Log into my home location by default.">
+ My Home
+ </radio_item>
+- <radio_item bottom="-20" height="20" left="100" name="MyLastLocation" width="50"
++ <radio_item bottom="-20" height="20" left="105" name="MyLastLocation" width="50"
+ tool_tip="Log into my last location by default.">
+ My Last Location
+ </radio_item>
+ </radio_group>
+- <check_box bottom="-44" enabled="true" follows="left|top"
++ <check_box bottom_delta="0" enabled="true" follows="left|top"
+ font="SansSerifSmall" height="16" initial_value="true"
+ label="Show Start Location on Login Screen" left="151" mouse_opaque="true"
+ name="show_location_checkbox" radio_style="false" width="256" />
+- <combo_box bottom_delta="-25" follows="left|top" height="18" left="155"
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-25" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" left="10"
++ mouse_opaque="true" name="show_names_textbox" v_pad="0" width="394">
++ Show Name Tags:
++ </text>
++ <combo_box bottom_delta="-4" follows="left|top" height="18" left="155"
+ mouse_opaque="true" name="fade_out_combobox" width="146">
+ <combo_item name="Never" value="Never">
+ Never
+@@ -30,13 +57,14 @@
+ </combo_item>
+ </combo_box>
+ <check_box bottom_delta="-25" follows="left|top"
+- font="SansSerifSmall" height="16" initial_value="true"
+- label="Small Avatar Names" left="151" name="small_avatar_names_checkbox"
+- width="200" />
+- <check_box bottom_delta="-18" follows="left|top"
+ font="SansSerifSmall" height="16" initial_value="false"
+ label="Hide My Name On My Screen" left="151" name="show_my_name_checkbox"
++ width="180" />
++ <check_box bottom_delta="0" follows="left|top"
++ font="SansSerifSmall" height="16" initial_value="true"
++ label="Small Avatar Names" left_delta="180" name="small_avatar_names_checkbox"
+ width="200" />
++ width="149" />
+ <text type="string" length="1" bottom_delta="-24" follows="left|top" font="SansSerifSmall" h_pad="0"
+ halign="left" height="16" left="10" name="group_titles_textbox" v_pad="0"
+ width="394">
+@@ -44,12 +72,12 @@
+ </text>
+ <check_box bottom_delta="0" follows="left|top"
+ font="SansSerifSmall" height="16" initial_value="false"
+- label="Hide All Group Titles" left="151" mouse_opaque="true"
+- name="show_all_title_checkbox" radio_style="false" width="256" />
+- <check_box bottom_delta="-18" follows="left|top"
+- font="SansSerifSmall" height="16" initial_value="false"
+ label="Hide My Group Title" left="151" name="show_my_title_checkbox"
+- radio_style="false" width="256" />
++ radio_style="false" width="149" />
++ <check_box bottom_delta="0" follows="left|top"
++ font="SansSerifSmall" height="16" initial_value="false"
++ label="Hide All Group Titles" left_delta="180" mouse_opaque="true"
++ name="show_all_title_checkbox" radio_style="false" width="200" />
+ <color_swatch border_color="0.45098 0.517647 0.607843 1" bottom="-210"
+ can_apply_immediately="false" color="1 1 1 1"
+ enabled="true" follows="left|top" height="48" label="" left="153"
+@@ -94,18 +122,6 @@
+ name="show_search_panel" radio_style="false"
+ tool_tip="Display the embedded search panel." width="256" />
+ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
+- bottom="-20" drop_shadow_visible="true" enabled="true" follows="left|top"
+- font="SansSerifSmall" h_pad="0" halign="left" height="10" left="10"
+- mouse_opaque="true" name="start_location_textbox" v_pad="0" width="394">
+- Start Location:
+- </text>
+- <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
+- bottom="-64" drop_shadow_visible="true" enabled="true" follows="left|top"
+- font="SansSerifSmall" h_pad="0" halign="left" height="10" left="10"
+- mouse_opaque="true" name="show_names_textbox" v_pad="0" width="394">
+- Show Names:
+- </text>
+- <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
+ bottom="-184" drop_shadow_visible="true" enabled="true" follows="left|top"
+ font="SansSerifSmall" h_pad="0" halign="left" height="10" left="10"
+ mouse_opaque="true" name="effects_color_textbox" v_pad="0" width="394">
+--
+tg: (2c826ba..) topic/features/jira-backported/AvatarLoginList (depends on: upstream)
diff --git a/debian/patches/topic/features/jira-backported/VWR-11663_Save_Load_scripts.diff b/debian/patches/topic/features/jira-backported/VWR-11663_Save_Load_scripts.diff
new file mode 100644
index 0000000..c9ce21e
--- /dev/null
+++ b/debian/patches/topic/features/jira-backported/VWR-11663_Save_Load_scripts.diff
@@ -0,0 +1,190 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/features/jira-backported/VWR-11663_Save_Load_scripts
+
+<patch description>
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/llfilepicker.cpp | 17 +++++
+ indra/newview/llfilepicker.h | 3 +
+ indra/newview/llpreviewscript.cpp | 62 ++++++++++++++++++++
+ indra/newview/llpreviewscript.h | 2 +
+ .../default/xui/en-us/floater_script_ed_panel.xml | 6 ++
+ 5 files changed, 90 insertions(+), 0 deletions(-)
+
+diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
+index f9616ba..9522930 100644
+--- a/indra/newview/llfilepicker.cpp
++++ b/indra/newview/llfilepicker.cpp
+@@ -58,6 +58,7 @@ LLFilePicker LLFilePicker::sInstance;
+ #define XML_FILTER L"XML files (*.xml)\0*.xml\0"
+ #define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
+ #define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
++#define TEXT_FILTER L"Text files (*.txt; *.rtf)\0*.txt;*.rtf;*.lsl\0"
+ #endif
+
+ //
+@@ -160,6 +161,10 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
+ ANIM_FILTER \
+ L"\0";
+ break;
++ case FFLOAD_TEXT:
++ mOFN.lpstrFilter = TEXT_FILTER \
++ L"\0";
++ break;
+ case FFLOAD_WAV:
+ mOFN.lpstrFilter = SOUND_FILTER \
+ L"\0";
+@@ -327,6 +332,18 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename)
+ L"Targa, Bitmap Images (*.tga; *.bmp)\0*.tga;*.bmp\0" \
+ L"\0";
+ break;
++ case FFSAVE_TEXT:
++ if (filename.empty())
++ {
++ wcsncpy( mFilesW,L"untitled.txt", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
++ }
++ mOFN.lpstrDefExt = L"txt";
++ mOFN.lpstrFilter =
++ L"Text files (*.txt)\0*.txt\0"
++ L"RTF Files (*.rtf)\0*.rtf\0"
++ L"LSL Files (*.lsl)\0*.lsl\0"
++ L"\0";
++ break;
+ case FFSAVE_WAV:
+ if (filename.empty())
+ {
+diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
+index aea414a..3dc0301 100644
+--- a/indra/newview/llfilepicker.h
++++ b/indra/newview/llfilepicker.h
+@@ -90,6 +90,8 @@ public:
+ FFLOAD_XML = 6,
+ FFLOAD_SLOBJECT = 7,
+ FFLOAD_RAW = 8,
++ FFLOAD_TEXT = 9,
++
+ };
+
+ enum ESaveFilter
+@@ -109,6 +111,7 @@ public:
+ FFSAVE_J2C = 12,
+ FFSAVE_PNG = 13,
+ FFSAVE_JPEG = 14,
++ FFSAVE_TEXT = 15,
+ };
+
+ // open the dialog. This is a modal operation
+diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
+index af52a4b..a12369b 100644
+--- a/indra/newview/llpreviewscript.cpp
++++ b/indra/newview/llpreviewscript.cpp
+@@ -436,6 +436,14 @@ void LLScriptEdCore::initMenu()
+ menuItem->setMenuCallback(onBtnHelp, this);
+ menuItem->setEnabledCallback(NULL);
+
++ menuItem = getChild<LLMenuItemCallGL>("Load from Disc");
++ menuItem->setMenuCallback(onBtnLoadFromDisc, this);
++ menuItem->setEnabledCallback(NULL);
++
++ menuItem = getChild<LLMenuItemCallGL>("Save to Disc");
++ menuItem->setMenuCallback(onBtnSaveToDisc, this);
++ menuItem->setEnabledCallback(hasChanged);
++
+ menuItem = getChild<LLMenuItemCallGL>("LSL Wiki Help...");
+ menuItem->setMenuCallback(onBtnDynamicHelp, this);
+ menuItem->setEnabledCallback(NULL);
+@@ -823,6 +831,60 @@ void LLScriptEdCore::onBtnUndoChanges( void* userdata )
+ }
+ }
+
++void LLScriptEdCore::onBtnSaveToDisc( void* userdata )
++{
++
++ LLViewerStats::getInstance()->incStat( LLViewerStats::ST_LSL_SAVE_COUNT );
++
++ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
++
++ if( self->mSaveCallback )
++ {
++ LLFilePicker& file_picker = LLFilePicker::instance();
++ if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_TEXT ) )
++ {
++ return;
++ }
++
++ std::string filename = file_picker.getFirstFile();
++ std::string scriptText=self->mEditor->getText();
++ std::ofstream fout(filename.c_str());
++ fout<<(scriptText);
++ fout.close();
++ self->mSaveCallback( self->mUserdata, FALSE );
++
++ }
++
++}
++void LLScriptEdCore::onBtnLoadFromDisc( void* data )
++{
++
++ LLScriptEdCore* self = (LLScriptEdCore*) data;
++
++ LLFilePicker& file_picker = LLFilePicker::instance();
++ if( !file_picker.getOpenFile( LLFilePicker::FFLOAD_TEXT ) )
++ {
++ return;
++ }
++
++ std::string filename = file_picker.getFirstFile();
++
++ std::ifstream fin(filename.c_str());
++
++ std::string line;
++ std::string linetotal;
++ self->mEditor->clear();
++ while (!fin.eof())
++ {
++ getline(fin,line);
++ line=line+"\n";
++ self->mEditor->insertText(line);
++
++ }
++ fin.close();
++
++}
++
+ void LLScriptEdCore::onSearchMenu(void* userdata)
+ {
+ LLScriptEdCore* sec = (LLScriptEdCore*)userdata;
+diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h
+index 7026482..97e721d 100644
+--- a/indra/newview/llpreviewscript.h
++++ b/indra/newview/llpreviewscript.h
+@@ -95,6 +95,8 @@ public:
+ static void doSave( void* userdata, BOOL close_after_save );
+ static void onBtnSave(void*);
+ static void onBtnUndoChanges(void*);
++ static void onBtnSaveToDisc(void*);
++ static void onBtnLoadFromDisc(void*);
+ static void onSearchMenu(void* userdata);
+
+ static void onUndoMenu(void* userdata);
+diff --git a/indra/newview/skins/default/xui/en-us/floater_script_ed_panel.xml b/indra/newview/skins/default/xui/en-us/floater_script_ed_panel.xml
+index b83a6df..154cb1c 100644
+--- a/indra/newview/skins/default/xui/en-us/floater_script_ed_panel.xml
++++ b/indra/newview/skins/default/xui/en-us/floater_script_ed_panel.xml
+@@ -35,6 +35,12 @@
+ width="138" />
+ <menu_item_call bottom_delta="-58" height="20" label="Revert All Changes" left="0"
+ mouse_opaque="true" name="Revert All Changes" width="138" />
++ <menu_item_separator bottom_delta="-66" height="8" left="0" mouse_opaque="true" name="separator"
++ width="138" />
++ <menu_item_call bottom_delta="-76" height="20" label="Save to Disc" left="0"
++ mouse_opaque="true" name="Save to Disc" width="138" />
++ <menu_item_call bottom_delta="-96" height="20" label="Load from Disc" left="0"
++ mouse_opaque="true" name="Load from Disc" width="138" />
+ </menu>
+ <menu bottom="665" drop_shadow="true" enabled="true" height="198"
+ left="222" mouse_opaque="false" name="Edit" opaque="true" tear_off="false"
+--
+tg: (2c826ba..) topic/features/jira-backported/VWR-11663_Save_Load_scripts (depends on: upstream)
diff --git a/debian/patches/topic/features/jira-backported/VWR-5082_set_bulk_inv_permissions.diff b/debian/patches/topic/features/jira-backported/VWR-5082_set_bulk_inv_permissions.diff
new file mode 100644
index 0000000..8f58933
--- /dev/null
+++ b/debian/patches/topic/features/jira-backported/VWR-5082_set_bulk_inv_permissions.diff
@@ -0,0 +1,965 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/features/jira-backported/VWR-5082_set_bulk_inv_permissions
+
+This is applied upstream (in a feature branch) and allows multiple object inventory items to have permissions set in one go.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/newview/CMakeLists.txt | 1 +
+ indra/newview/llfloaterbulkpermission.cpp | 594 ++++++++++++++++++++
+ indra/newview/llfloaterbulkpermission.h | 143 +++++
+ indra/newview/llviewermenu.cpp | 12 +
+ .../skins/default/xui/en-us/floater_bulk_perms.xml | 127 +++++
+ .../skins/default/xui/en-us/menu_viewer.xml | 5 +
+ 6 files changed, 882 insertions(+), 0 deletions(-)
+
+diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
+index b95ba37..35ae507 100644
+--- a/indra/newview/CMakeLists.txt
++++ b/indra/newview/CMakeLists.txt
+@@ -127,6 +127,7 @@ set(viewer_SOURCE_FILES
+ llfloateravatartextures.cpp
+ llfloaterbeacons.cpp
+ llfloaterbuildoptions.cpp
++ llfloaterbulkpermission.cpp
+ llfloaterbump.cpp
+ llfloaterbuycontents.cpp
+ llfloaterbuy.cpp
+diff --git a/indra/newview/llfloaterbulkpermission.cpp b/indra/newview/llfloaterbulkpermission.cpp
+new file mode 100644
+index 0000000..e66d1af
+--- /dev/null
++++ b/indra/newview/llfloaterbulkpermission.cpp
+@@ -0,0 +1,594 @@
++/**
++ * @file llfloaterbulkpermissions.cpp
++ * @brief A floater which allows task inventory item's properties to be changed on mass.
++ *
++ * $LicenseInfo:firstyear=2008&license=viewergpl$
++ *
++ * Copyright (c) 2008, Linden Research, Inc.
++ *
++ * Second Life Viewer Source Code
++ * The source code in this file ("Source Code") is provided by Linden Lab
++ * to you under the terms of the GNU General Public License, version 2.0
++ * ("GPL"), unless you have obtained a separate licensing agreement
++ * ("Other License"), formally executed by you and Linden Lab. Terms of
++ * the GPL can be found in doc/GPL-license.txt in this distribution, or
++ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
++ *
++ * There are special exceptions to the terms and conditions of the GPL as
++ * it is applied to this Source Code. View the full text of the exception
++ * in the file doc/FLOSS-exception.txt in this software distribution, or
++ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
++ *
++ * By copying, modifying or distributing this software, you acknowledge
++ * that you have read and understood your obligations described above,
++ * and agree to abide by those obligations.
++ *
++ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
++ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
++ * COMPLETENESS OR PERFORMANCE.
++ * $/LicenseInfo$
++ */
++
++/* Allow multiple task inventory properties to be set in one go, by Michelle2 Zenovka */
++
++/* TODO
++
++ * Add in the option to select objects or task inventory
++s
++
++It would be nice to set the permissions on groups of prims as well as task inventory
++
++*/
++
++
++#include "llviewerprecompiledheaders.h"
++#include "llfloaterbulkpermission.h"
++#include "llagent.h"
++#include "llchat.h"
++#include "llviewerwindow.h"
++#include "llviewerobject.h"
++#include "llviewerobjectlist.h"
++#include "llviewerregion.h"
++#include "lscript_rt_interface.h"
++#include "llviewercontrol.h"
++#include "llviewerobject.h"
++#include "llviewerregion.h"
++#include "llresmgr.h"
++#include "llbutton.h"
++#include "lldir.h"
++#include "llfloaterchat.h"
++#include "llviewerstats.h"
++#include "lluictrlfactory.h"
++#include "llselectmgr.h"
++#include "llinventory.h"
++
++
++#include <algorithm>
++#include <functional>
++#include "llcachename.h"
++#include "lldbstrings.h"
++#include "llinventory.h"
++
++#include "llagent.h"
++#include "llbutton.h"
++#include "llcheckboxctrl.h"
++#include "llfloateravatarinfo.h"
++#include "llfloatergroupinfo.h"
++#include "llinventorymodel.h"
++#include "lllineeditor.h"
++#include "llradiogroup.h"
++#include "llresmgr.h"
++#include "roles_constants.h"
++#include "llselectmgr.h"
++#include "lltextbox.h"
++#include "lluiconstants.h"
++#include "llviewerinventory.h"
++#include "llviewerobjectlist.h"
++#include "llviewerregion.h"
++#include "llviewercontrol.h"
++
++#include "lluictrlfactory.h"
++
++
++const char* BULKPERM_QUEUE_TITLE = "Update Progress";
++const char* BULKPERM_START_STRING = "update";
++
++namespace
++{
++ struct BulkQueueObjects : public LLSelectedObjectFunctor
++ {
++ BOOL scripted;
++ BOOL modifiable;
++ LLFloaterBulkPermission* mQueue;
++ BulkQueueObjects(LLFloaterBulkPermission* q) : mQueue(q), scripted(FALSE), modifiable(FALSE) {}
++ virtual bool apply(LLViewerObject* obj)
++ {
++ scripted = obj->flagScripted();
++ modifiable = obj->permModify();
++
++ mQueue->addObject(obj->getID());
++ return false;
++
++ }
++ };
++}
++
++///----------------------------------------------------------------------------
++/// Class LLFloaterBulkPermission
++///----------------------------------------------------------------------------
++
++// static
++LLMap<LLUUID, LLFloaterBulkPermission*> LLFloaterBulkPermission::sInstances;
++
++
++// Default constructor
++LLFloaterBulkPermission::LLFloaterBulkPermission(const std::string& name,
++ const LLRect& rect,
++ const char* title,
++ const char* start_string) :
++ LLFloater(name, rect, title,
++ RESIZE_YES, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT,
++ DRAG_ON_TOP, MINIMIZE_YES, CLOSE_YES)
++{
++
++ req_perm_mask=0; // This should match the default state the checkboxes are set to
++ recurse=false;
++
++ LLUICtrlFactory::getInstance()->buildFloater(this,"floater_bulk_perms.xml");
++
++ childSetAction("Apply...",onApplyBtn,this);
++ childSetEnabled("Apply...",TRUE);
++
++ childSetCommitCallback("Modify",&onCommitPermissions, this);
++ childSetCommitCallback("Trans",&onCommitPermissions, this);
++ childSetCommitCallback("Copy",&onCommitPermissions, this);
++
++ //childSetCommitCallback("Recurse",&onRecurse, this);
++
++ childSetCommitCallback("Parent",&onParent, this);
++
++ childSetCommitCallback("objects",&InvSelection, this);
++ childSetCommitCallback("scripts",&InvSelection, this);
++ childSetCommitCallback("textures",&InvSelection, this);
++ childSetCommitCallback("sounds",&InvSelection, this);
++ childSetCommitCallback("animations",&InvSelection, this);
++ childSetCommitCallback("notecards",&InvSelection, this);
++ childSetCommitCallback("landmarks",&InvSelection, this);
++ childSetCommitCallback("bodyparts",&InvSelection, this);
++ childSetCommitCallback("clothing",&InvSelection, this);
++ childSetCommitCallback("gestures",&InvSelection, this);
++
++ //Set variable state to XUI default state consistancy
++ processObject=getChild<LLCheckBoxCtrl>("objects")->get();
++ processScript=getChild<LLCheckBoxCtrl>("scripts")->get();
++ processTexture=getChild<LLCheckBoxCtrl>("textures")->get();
++ processSound=getChild<LLCheckBoxCtrl>("sounds")->get();
++ processAnimation=getChild<LLCheckBoxCtrl>("animations")->get();
++ processNotecard=getChild<LLCheckBoxCtrl>("notecards")->get();
++ processGesture=getChild<LLCheckBoxCtrl>("gestures")->get();
++ processClothing=getChild<LLCheckBoxCtrl>("clothing")->get();
++ processBodypart=getChild<LLCheckBoxCtrl>("bodyparts")->get();
++ processLandmark=getChild<LLCheckBoxCtrl>("landmarks")->get();
++ parent=getChild<LLCheckBoxCtrl>("Parent")->get();
++
++
++ setTitle(title);
++
++ if (!getHost())
++ {
++ LLRect curRect = getRect();
++ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
++ }
++
++ mStartString = start_string;
++ mDone = FALSE;
++ sInstances.addData(mID, this);
++
++}
++
++void LLFloaterBulkPermission::doApply()
++{
++ // Its alive now do the nasty work that the ScriptQueue and friends try to do in the menu code
++ // but first grab the user options
++
++ LLScrollListCtrl* list = getChild<LLScrollListCtrl>("queue output");
++ list->deleteAllItems();
++
++ //Apply to selected objects if requested first
++
++ if(parent)
++ {
++ llinfos<< "Setting permission on parent items" << llendl;
++ LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_NEXT_OWNER,true, req_perm_mask);
++ LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_NEXT_OWNER,false, ~req_perm_mask); //How annoying need to set and unset
++ }
++
++
++ LLFloaterBulkPermission* q;
++ q=(LLFloaterBulkPermission*)this;
++
++ BulkQueueObjects func(q);
++ bool fail = LLSelectMgr::getInstance()->getSelection()->applyToObjects(&func);
++ if(fail)
++ {
++ if ( !func.modifiable )
++ {
++ gViewerWindow->alertXml("NO MODIFY");
++ }
++ else
++ {
++ llwarns << "Bad logic. Are there actualy any items in that prim?" << llendl;
++ }
++ }
++ else
++ {
++ if (!q->start())
++ {
++ llwarns << "Unexpected failure attepmting to set permissions." << llendl;
++ }
++ }
++}
++
++// Destroys the object
++LLFloaterBulkPermission::~LLFloaterBulkPermission()
++{
++ sInstances.removeData(mID);
++}
++
++// find an instance by ID. Return NULL if it does not exist.
++// static
++LLFloaterBulkPermission* LLFloaterBulkPermission::findInstance(const LLUUID& id)
++{
++ if(sInstances.checkData(id))
++ {
++ return sInstances.getData(id);
++ }
++ return NULL;
++}
++
++
++// This is the callback method for the viewer object currently being
++// worked on.
++// NOT static, virtual!
++void LLFloaterBulkPermission::inventoryChanged(LLViewerObject* viewer_object,
++ InventoryObjectList* inv,
++ S32,
++ void* q_id)
++{
++ llinfos << "LLFloaterBulkPermission::inventoryChanged() for object "
++ << viewer_object->getID() << llendl;
++
++ //Remove this listener from the object since its
++ //listener callback is now being executed.
++
++ //We remove the listener here because the function
++ //removeVOInventoryListener removes the listener from a ViewerObject
++ //which it internally stores.
++
++ //If we call this further down in the function, calls to handleInventory
++ //and nextObject may update the interally stored viewer object causing
++ //the removal of the incorrect listener from an incorrect object.
++
++ //Fixes SL-6119:Recompile scripts fails to complete
++ removeVOInventoryListener();
++
++ if (viewer_object && inv && (viewer_object->getID() == mCurrentObjectID) )
++ {
++ handleInventory(viewer_object, inv);
++ }
++ else
++ {
++ // something went wrong...
++ // note that we're not working on this one, and move onto the
++ // next object in the list.
++ llwarns << "No inventory for " << mCurrentObjectID
++ << llendl;
++ nextObject();
++ }
++}
++
++void LLFloaterBulkPermission::onApplyBtn(void* user_data)
++{
++ LLFloaterBulkPermission* self = (LLFloaterBulkPermission*)user_data;
++ self->doApply();
++}
++
++
++// static
++void LLFloaterBulkPermission::InvSelection(LLUICtrl* ctrl, void* data)
++{
++ LLFloaterBulkPermission* self = (LLFloaterBulkPermission*)data;
++
++ self->processObject=self->getChild<LLCheckBoxCtrl>("objects")->get();
++ self->processScript=self->getChild<LLCheckBoxCtrl>("scripts")->get();
++ self->processTexture=self->getChild<LLCheckBoxCtrl>("textures")->get();
++ self->processSound=self->getChild<LLCheckBoxCtrl>("sounds")->get();
++ self->processAnimation=self->getChild<LLCheckBoxCtrl>("animations")->get();
++ self->processNotecard=self->getChild<LLCheckBoxCtrl>("notecards")->get();
++ self->processGesture=self->getChild<LLCheckBoxCtrl>("gestures")->get();
++ self->processClothing=self->getChild<LLCheckBoxCtrl>("clothing")->get();
++ self->processBodypart=self->getChild<LLCheckBoxCtrl>("bodyparts")->get();
++ self->processLandmark=self->getChild<LLCheckBoxCtrl>("landmarks")->get();
++
++
++}
++
++// static
++void LLFloaterBulkPermission::onParent(LLUICtrl* ctrl, void* data)
++{
++ LLFloaterBulkPermission* self = (LLFloaterBulkPermission*)data;
++ self->parent=self->getChild<LLCheckBoxCtrl>("Parent")->get();
++}
++
++// static
++void LLFloaterBulkPermission::onRecurse(LLUICtrl* ctrl, void* data)
++{
++ LLFloaterBulkPermission* self = (LLFloaterBulkPermission*)data;
++ self->recurse=self->getChild<LLCheckBoxCtrl>("Recurse")->get();
++}
++
++// static
++void LLFloaterBulkPermission::onCommitPermissions(LLUICtrl* ctrl, void* data)
++{
++ LLFloaterBulkPermission* self = (LLFloaterBulkPermission*)data;
++ LLCheckBoxCtrl* CheckModify = self->getChild<LLCheckBoxCtrl>("Modify");
++ LLCheckBoxCtrl* CheckCopy = self->getChild<LLCheckBoxCtrl>("Copy");
++ LLCheckBoxCtrl* CheckTrans = self->getChild<LLCheckBoxCtrl>("Trans");
++
++ self->req_perm_mask=0;
++
++ if(CheckModify->get())
++ {
++ self->req_perm_mask|=PERM_MODIFY;
++ }
++ else
++ {
++ self->req_perm_mask&=~PERM_MODIFY;
++ }
++
++ if(CheckCopy->get())
++ {
++ self->req_perm_mask|=PERM_COPY;
++ }
++ else
++ {
++ self->req_perm_mask&=~PERM_COPY;
++ }
++
++ if(CheckTrans->get())
++ {
++ self->req_perm_mask|=PERM_TRANSFER;
++ }
++ else
++ {
++ self->req_perm_mask&=~PERM_TRANSFER;
++ }
++
++
++}
++
++void LLFloaterBulkPermission::addObject(const LLUUID& id)
++{
++ mObjectIDs.put(id);
++}
++
++BOOL LLFloaterBulkPermission::start()
++{
++ llinfos << "LLFloaterBulkPermission::start()" << llendl;
++ char buffer[MAX_STRING]; /*Flawfinder: ignore*/
++ snprintf(buffer, sizeof(buffer), "Starting %s of %d items.", mStartString, mObjectIDs.count()); /* Flawfinder: ignore */
++
++ LLScrollListCtrl* list = getChild<LLScrollListCtrl>("queue output");
++ list->addCommentText(buffer);
++
++ return nextObject();
++}
++
++BOOL LLFloaterBulkPermission::isDone() const
++{
++ return (mCurrentObjectID.isNull() || (mObjectIDs.count() == 0));
++}
++
++// go to the next object. If no objects left, it falls out silently
++// and waits to be killed by the window being closed.
++BOOL LLFloaterBulkPermission::nextObject()
++{
++ S32 count;
++ BOOL successful_start = FALSE;
++ do
++ {
++ count = mObjectIDs.count();
++ llinfos << "LLFloaterBulkPermission::nextObject() - " << count
++ << " objects left to process." << llendl;
++ mCurrentObjectID.setNull();
++ if(count > 0)
++ {
++ successful_start = popNext();
++ }
++ llinfos << "LLFloaterBulkPermission::nextObject() "
++ << (successful_start ? "successful" : "unsuccessful")
++ << llendl;
++ } while((mObjectIDs.count() > 0) && !successful_start);
++
++ if(isDone() && !mDone)
++ {
++
++ LLScrollListCtrl* list = getChild<LLScrollListCtrl>("queue output");
++ mDone = TRUE;
++ char buffer[MAX_STRING]; /*Flawfinder: ignore*/
++ snprintf(buffer, sizeof(buffer), "Done."); /* Flawfinder: ignore */
++ list->addCommentText(buffer);
++
++ }
++ return successful_start;
++}
++
++// returns true if the queue has started, otherwise false. This
++// method pops the top object off of the queue.
++BOOL LLFloaterBulkPermission::popNext()
++{
++ // get the first element off of the container, and attempt to get
++ // the inventory.
++ BOOL rv = FALSE;
++ S32 count = mObjectIDs.count();
++ if(mCurrentObjectID.isNull() && (count > 0))
++ {
++ mCurrentObjectID = mObjectIDs.get(0);
++ llinfos << "LLFloaterBulkPermission::popNext() - mCurrentID: "
++ << mCurrentObjectID << llendl;
++ mObjectIDs.remove(0);
++ LLViewerObject* obj = gObjectList.findObject(mCurrentObjectID);
++ if(obj)
++ {
++ llinfos << "LLFloaterBulkPermission::popNext() requesting inv for "
++ << mCurrentObjectID << llendl;
++ LLUUID* id = new LLUUID(mID);
++
++ registerVOInventoryListener(obj,id);
++ requestVOInventory();
++ rv = TRUE;
++ }
++ else
++ {
++ llinfos<<"LLFloaterBulkPermission::popNext() returned a NULL LLViewerObject" <<llendl;
++ //Arrrg what do we do here?
++ }
++ }
++
++ return rv;
++}
++
++
++// static
++LLFloaterBulkPermission* LLFloaterBulkPermission::create()
++{
++ S32 left, top;
++ gFloaterView->getNewFloaterPosition(&left, &top);
++ LLRect rect = gSavedSettings.getRect("CompileOutputRect");
++ rect.translate(left - rect.mLeft, top - rect.mTop);
++ LLFloaterBulkPermission* new_queue = new LLFloaterBulkPermission("queue",rect,"Setting Bulk permissions","Results");
++ new_queue->open(); /*Flawfinder: ignore*/
++ return new_queue;
++}
++
++
++void LLFloaterBulkPermission::handleInventory(LLViewerObject* viewer_obj, InventoryObjectList* inv)
++{
++ // find all of the lsl, leaving off duplicates. We'll remove
++ // all matching asset uuids on compilation success.
++
++ llinfos<<"handleInventory"<<llendl;
++
++ char buffer[MAX_STRING]; /*Flawfinder: ignore*/
++ LLScrollListCtrl* list = getChild<LLScrollListCtrl>("queue output");
++
++ InventoryObjectList::const_iterator it = inv->begin();
++ InventoryObjectList::const_iterator end = inv->end();
++ for ( ; it != end; ++it)
++ {
++ llinfos<<"Doing iterator of inventory"<<llendl;
++
++ if( ( (*it)->getType() == LLAssetType::AT_LSL_TEXT && processScript) ||
++ ( (*it)->getType() == LLAssetType::AT_TEXTURE && processTexture) ||
++ ( (*it)->getType() == LLAssetType::AT_SOUND && processSound) ||
++ ( (*it)->getType() == LLAssetType::AT_LANDMARK && processLandmark) ||
++ ( (*it)->getType() == LLAssetType::AT_CLOTHING && processClothing) ||
++ ( (*it)->getType() == LLAssetType::AT_OBJECT && processObject) ||
++ ( (*it)->getType() == LLAssetType::AT_NOTECARD && processNotecard) ||
++ ( (*it)->getType() == LLAssetType::AT_BODYPART && processBodypart) ||
++ ( (*it)->getType() == LLAssetType::AT_ANIMATION && processAnimation) ||
++ ( (*it)->getType() == LLAssetType::AT_GESTURE && processGesture))
++ {
++
++ LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
++
++ if (object)
++ {
++ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
++ LLViewerInventoryItem* new_item = (LLViewerInventoryItem*)item;
++ LLPermissions perm(new_item->getPermissions());
++
++ // chomp the inventory name so it fits in the scroll window nicely
++ // and the user can see the [OK]
++ std::string invname;
++ invname=item->getName().substr(0,item->getName().size() < 30 ? item->getName().size() : 30 );
++
++ // My attempt at checking valid permissions, CHECK ME
++ // note its not actually bad to try to set permissions that are not allowed as the
++ // server will protect against this, but it will piss the user off if its wrong
++ if(
++ (perm.getCreator()==gAgentID) ||
++ (perm.getMaskOwner() & PERM_TRANSFER) && (perm.getMaskOwner() & PERM_MODIFY) ||
++ (gAgent.getGroupID()==perm.getGroup() && (perm.getMaskGroup() & PERM_TRANSFER) && (perm.getMaskGroup() & PERM_MODIFY))
++ ){
++ llinfos<<"Setting perms"<<llendl;
++ perm.setMaskNext(req_perm_mask);
++ new_item->setPermissions(perm);
++ updateInventory(object,new_item,TASK_INVENTORY_ITEM_KEY,FALSE);
++ snprintf(buffer, sizeof(buffer), "Setting perms on '%s' [OK]", invname.c_str()); /* Flawfinder: ignore */
++ }
++ else
++ {
++ llinfos<<"NOT setting perms"<<llendl;
++ snprintf(buffer, sizeof(buffer), "Setting perms on '%s' [FAILED]", invname.c_str()); /* Flawfinder: ignore */
++
++ }
++
++ list->addCommentText(buffer);
++
++ if(recurse && ( (*it)->getType() == LLAssetType::AT_OBJECT && processObject))
++ {
++ //Add this object back to the queue to be processed as it has inventory
++ snprintf(buffer, sizeof(buffer), "Queueing object '%s' for open", invname.c_str());
++ llwarns << "Queueing object "<< invname.c_str() << " ID "<< (*it)->getUUID()<<llendl;
++ mObjectIDs.put((*it)->getUUID());
++ // This will not YET work. as this is not a viewer object the unpack will fail
++ }
++
++ }
++ }
++ }
++
++ nextObject();
++}
++
++
++// Avoid inventory callbacks etc by just fire and forgetting the message with the permissions update
++// we could do this via LLViewerObject::updateInventory but that uses inventory call backs and buggers
++// us up and we would have a dodgy item iterator
++
++void LLFloaterBulkPermission::updateInventory(
++ LLViewerObject* object,
++ LLViewerInventoryItem* item,
++ U8 key,
++ bool is_new)
++{
++ LLMemType mt(LLMemType::MTYPE_OBJECT);
++
++
++ // This slices the object into what we're concerned about on the
++ // viewer. The simulator will take the permissions and transfer
++ // ownership.
++ LLPointer<LLViewerInventoryItem> task_item =
++ new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(),
++ item->getAssetUUID(), item->getType(),
++ item->getInventoryType(),
++ item->getName(), item->getDescription(),
++ item->getSaleInfo(),
++ item->getFlags(),
++ item->getCreationDate());
++ task_item->setTransactionID(item->getTransactionID());
++ LLMessageSystem* msg = gMessageSystem;
++ msg->newMessageFast(_PREHASH_UpdateTaskInventory);
++ msg->nextBlockFast(_PREHASH_AgentData);
++ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
++ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
++ msg->nextBlockFast(_PREHASH_UpdateData);
++ msg->addU32Fast(_PREHASH_LocalID, object->mLocalID);
++ msg->addU8Fast(_PREHASH_Key, key);
++ msg->nextBlockFast(_PREHASH_InventoryData);
++ task_item->packMessage(msg);
++ msg->sendReliable(object->getRegion()->getHost());
++
++}
++
+diff --git a/indra/newview/llfloaterbulkpermission.h b/indra/newview/llfloaterbulkpermission.h
+new file mode 100644
+index 0000000..3de871b
+--- /dev/null
++++ b/indra/newview/llfloaterbulkpermission.h
+@@ -0,0 +1,143 @@
++/**
++ * @file llfloaterbulkpermissions.h
++ * @brief A floater which allows task inventory item's properties to be changed on mass.
++ *
++ * $LicenseInfo:firstyear=2008&license=viewergpl$
++ *
++ * Copyright (c) 2008, Linden Research, Inc.
++ *
++ * Second Life Viewer Source Code
++ * The source code in this file ("Source Code") is provided by Linden Lab
++ * to you under the terms of the GNU General Public License, version 2.0
++ * ("GPL"), unless you have obtained a separate licensing agreement
++ * ("Other License"), formally executed by you and Linden Lab. Terms of
++ * the GPL can be found in doc/GPL-license.txt in this distribution, or
++ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
++ *
++ * There are special exceptions to the terms and conditions of the GPL as
++ * it is applied to this Source Code. View the full text of the exception
++ * in the file doc/FLOSS-exception.txt in this software distribution, or
++ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
++ *
++ * By copying, modifying or distributing this software, you acknowledge
++ * that you have read and understood your obligations described above,
++ * and agree to abide by those obligations.
++ *
++ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
++ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
++ * COMPLETENESS OR PERFORMANCE.
++ * $/LicenseInfo$
++ */
++
++/* Allow multiple task inventory properties to be set in one go, by Michelle2 Zenovka */
++
++#ifndef LL_LLBULKPERMISSION_H
++#define LL_LLBULKPERMISSION_H
++
++#include "lldarray.h"
++#include "llinventory.h"
++#include "llviewerobject.h"
++#include "llvoinventorylistener.h"
++#include "llmap.h"
++#include "lluuid.h"
++
++#include "llfloater.h"
++#include "llscrolllistctrl.h"
++
++#include "llviewerinventory.h"
++
++class LLFloaterBulkPermission : public LLFloater, public LLVOInventoryListener
++{
++public:
++ // addObject() accepts an object id.
++ void addObject(const LLUUID& id);
++
++ // start() returns TRUE if the queue has started, otherwise FALSE.
++ BOOL start();
++
++ // Use this method to create a reset queue. Once created, it
++ // will be responsible for it's own destruction.
++ static LLFloaterBulkPermission * create();
++
++protected:
++ LLFloaterBulkPermission(const std::string& name, const LLRect& rect,
++ const char* title, const char* start_string);
++ virtual ~LLFloaterBulkPermission();
++
++ // This is the callback method for the viewer object currently
++ // being worked on.
++ /*virtual*/ void inventoryChanged(LLViewerObject* obj,
++ InventoryObjectList* inv,
++ S32 serial_num,
++ void* queue);
++
++ // This is called by inventoryChanged
++ void handleInventory(LLViewerObject* viewer_obj,
++ InventoryObjectList* inv);
++
++
++ void updateInventory(LLViewerObject* object,
++ LLViewerInventoryItem* item,
++ U8 key,
++ bool is_new);
++
++
++ static void onCloseBtn(void* user_data);
++ static void onApplyBtn(void* user_data);
++ static void onCommitPermissions(LLUICtrl* ctrl, void* data);
++ static void InvSelection(LLUICtrl* ctrl, void* data);
++ static void onRecurse(LLUICtrl* ctrl, void* data);
++ static void onParent(LLUICtrl* ctrl, void* data);
++
++ // returns true if this is done
++ BOOL isDone() const;
++
++ //Read the settings and Apply the permissions
++ void doApply();
++
++ // go to the next object. If no objects left, it falls out
++ // silently and waits to be killed by the deleteIfDone() callback.
++ BOOL nextObject();
++ BOOL popNext();
++
++ // Get this instances ID.
++ const LLUUID& getID() const { return mID; }
++
++ // find an instance by ID. Return NULL if it does not exist.
++ static LLFloaterBulkPermission* findInstance(const LLUUID& id);
++
++ U32 req_perm_mask;
++
++ BOOL processObject;
++ BOOL processScript;
++ BOOL processTexture;
++ BOOL processSound;
++ BOOL processAnimation;
++ BOOL processCallingcard;
++ BOOL processNotecard;
++ BOOL processGesture;
++ BOOL processClothing;
++ BOOL processBodypart;
++ BOOL processLandmark;
++
++ BOOL recurse;
++ BOOL parent;
++
++protected:
++ // UI
++ LLScrollListCtrl* mMessages;
++ LLButton* mCloseBtn;
++
++ // Object Queue
++ LLDynamicArray<LLUUID> mObjectIDs;
++ LLUUID mCurrentObjectID;
++ BOOL mDone;
++
++ LLUUID mID;
++ static LLMap<LLUUID, LLFloaterBulkPermission*> sInstances;
++
++ const char* mStartString;
++
++};
++
++#endif
+diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
+index 023c758..29f4cef 100644
+--- a/indra/newview/llviewermenu.cpp
++++ b/indra/newview/llviewermenu.cpp
+@@ -87,6 +87,7 @@
+ #include "llfloateravatartextures.h"
+ #include "llfloaterbeacons.h"
+ #include "llfloaterbuildoptions.h"
++#include "llfloaterbulkpermission.h"
+ #include "llfloaterbump.h"
+ #include "llfloaterbuy.h"
+ #include "llfloaterbuycontents.h"
+@@ -6024,6 +6025,16 @@ void queue_actions(LLFloaterScriptQueue* q, const std::string& noscriptmsg, cons
+ }
+ }
+
++class LLToolsSetBulkPerms : public view_listener_t
++{
++ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
++ {
++ LLFloaterBulkPermission* queue = NULL;
++ queue = LLFloaterBulkPermission::create();
++ return true;
++ }
++};
++
+ void handle_compile_queue(std::string to_lang)
+ {
+ LLFloaterCompileQueue* queue;
+@@ -7516,6 +7527,7 @@ void initialize_menus()
+ addMenu(new LLToolsSaveToInventory(), "Tools.SaveToInventory");
+ addMenu(new LLToolsSaveToObjectInventory(), "Tools.SaveToObjectInventory");
+ addMenu(new LLToolsSelectedScriptAction(), "Tools.SelectedScriptAction");
++ addMenu(new LLToolsSetBulkPerms(), "Tools.SetBulkPerms");
+
+ addMenu(new LLToolsEnableToolNotPie(), "Tools.EnableToolNotPie");
+ addMenu(new LLToolsEnableLink(), "Tools.EnableLink");
+diff --git a/indra/newview/skins/default/xui/en-us/floater_bulk_perms.xml b/indra/newview/skins/default/xui/en-us/floater_bulk_perms.xml
+new file mode 100644
+index 0000000..3dd838a
+--- /dev/null
++++ b/indra/newview/skins/default/xui/en-us/floater_bulk_perms.xml
+@@ -0,0 +1,127 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
++<floater bottom="-554" can_close="true" can_drag_on_left="false" can_minimize="true"
++ can_resize="false" can_tear_off="false" enabled="true" height="420"
++ left="367" min_height="1000" min_width="460" mouse_opaque="true"
++ name="floaterrecursiveperms" title="Bulk permission settings"
++ width="460">
++
++<text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom="-40" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" left="10"
++ mouse_opaque="true" name="applyto" v_pad="0" width="206">Apply to</text>
++
++<check_box bottom="-70" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Scripts" left="10" mouse_opaque="true"
++ name="scripts" radio_style="false"
++ tool_tip="Apply bulk permissions to scripts"
++ width="219"/>
++
++<check_box bottom="-90" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Textures" left="10" mouse_opaque="true"
++ name="textures" radio_style="false"
++ tool_tip="Apply bulk permissions to textures"
++ width="219"/>
++
++<check_box bottom="-110" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Animations" left="10" mouse_opaque="true"
++ name="animations" radio_style="false"
++ tool_tip="Apply bulk permissions to animations"
++ width="219"/>
++
++<check_box bottom="-130" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Sounds" left="10" mouse_opaque="true"
++ name="sounds" radio_style="false"
++ tool_tip="Apply bulk permissions to sounds"
++ width="219"/>
++
++<check_box bottom="-150" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Contained objects" left="10" mouse_opaque="true"
++ name="objects" radio_style="false"
++ tool_tip="Apply bulk permissions to objects inside inventory"
++ width="219"/>
++
++<check_box bottom="-170" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Landmarks" left="10" mouse_opaque="true"
++ name="landmarks" radio_style="false"
++ tool_tip="Apply bulk permissions to landmarks"
++ width="219"/>
++
++<check_box bottom="-190" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Notecards" left="10" mouse_opaque="true"
++ name="notecards" radio_style="false"
++ tool_tip="Apply bulk permissions to notecards"
++ width="219"/>
++
++<check_box bottom="-210" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Gesture" left="10" mouse_opaque="true"
++ name="gestures" radio_style="false"
++ tool_tip="Apply bulk permissions to gestures"
++ width="219"/>
++
++<check_box bottom="-230" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Clothing" left="10" mouse_opaque="true"
++ name="clothing" radio_style="false"
++ tool_tip="Apply bulk permissions to clothing"
++ width="219"/>
++
++<check_box bottom="-250" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Bodypart" left="10" mouse_opaque="true"
++ name="bodyparts" radio_style="false"
++ tool_tip="Apply bulk permissions to bodyparts"
++ width="219"/>
++
++<text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom="-40" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" left="200"
++ mouse_opaque="true" name="applyto" v_pad="0" width="206">Permissions</text>
++
++
++<check_box bottom="-110" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Copy" left="200" mouse_opaque="true"
++ name="Copy" radio_style="false"
++ tool_tip="Next owner can copy"
++ width="219"/>
++
++<check_box bottom="-130" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Modify" left="200" mouse_opaque="true"
++ name="Modify" radio_style="false"
++ tool_tip="Next owner can modify"
++ width="219"/>
++
++<check_box bottom="-150" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Trans" left="200" mouse_opaque="true"
++ name="Trans" radio_style="false"
++ tool_tip="Next owner can transfer"
++ width="219"/>
++
++<check_box bottom="-230" enabled="true" follows="left|top" font="SansSerifSmall"
++ height="16" initial_value="false"
++ label="Modify Parent Prims" left="200" mouse_opaque="true"
++ name="Parent" radio_style="false"
++ tool_tip="Modify parent prims not just inventory"
++ width="219" default="true"/>
++
++<scroll_list background_visible="true" bottom="-410" column_padding="5" draw_border="true"
++ draw_heading="false" draw_stripes="true" enabled="true"
++ follows="left|top|right|bottom" height="140" left="10" mouse_opaque="true"
++ multi_select="false" name="queue output" width="440" />
++
++<button bottom="-70" enabled="true" follows="left|top" font="SansSerif"
++ halign="center" height="20" label="Apply..."
++ label_selected="Apply..." left="295" mouse_opaque="true"
++ name="Apply..." scale_image="true" width="145" />
++
++</floater>
+diff --git a/indra/newview/skins/default/xui/en-us/menu_viewer.xml b/indra/newview/skins/default/xui/en-us/menu_viewer.xml
+index d2c4923..8b57505 100644
+--- a/indra/newview/skins/default/xui/en-us/menu_viewer.xml
++++ b/indra/newview/skins/default/xui/en-us/menu_viewer.xml
+@@ -704,6 +704,11 @@
+ <on_click function="Tools.SelectedScriptAction" userdata="stop" />
+ <on_enable function="EditableSelected" />
+ </menu_item_call>
++ <menu_item_call bottom="-468" enabled="false" height="19" label="Set permissions on selected task inventory"
++ left="0" mouse_opaque="true" name="Set permissions on selected task inventory" width="250">
++ <on_click function="Tools.SetBulkPerms" userdata="" />
++ <on_enable function="EditableSelected" />
++ </menu_item_call>
+ </menu>
+ <menu bottom="219" create_jump_keys="true" drop_shadow="true" enabled="true"
+ height="317" label="Help" left="227" mouse_opaque="false" name="Help"
+--
+tg: (2c826ba..) topic/features/jira-backported/VWR-5082_set_bulk_inv_permissions (depends on: upstream)
diff --git a/debian/patches/topic/features/jira-backported/VWR-7531-Dynamic-Grid-List.diff b/debian/patches/topic/features/jira-backported/VWR-7531-Dynamic-Grid-List.diff
new file mode 100644
index 0000000..f5f1ed8
--- /dev/null
+++ b/debian/patches/topic/features/jira-backported/VWR-7531-Dynamic-Grid-List.diff
@@ -0,0 +1,735 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/features/jira-backported/VWR-7531-Dynamic-Grid-List
+
+<patch description>
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/app_settings/grids.xml | 24 +++
+ indra/newview/app_settings/settings.xml | 2 +-
+ indra/newview/llappviewer.cpp | 25 ---
+ indra/newview/llpanellogin.cpp | 122 ++-------------
+ indra/newview/llstartup.cpp | 6 +-
+ indra/newview/llviewernetwork.cpp | 260 +++++++++++++++----------------
+ indra/newview/llviewernetwork.h | 48 ++----
+ 7 files changed, 186 insertions(+), 301 deletions(-)
+
+diff --git a/indra/newview/app_settings/grids.xml b/indra/newview/app_settings/grids.xml
+new file mode 100644
+index 0000000..24d1183
+--- /dev/null
++++ b/indra/newview/app_settings/grids.xml
+@@ -0,0 +1,24 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<!-- this is the LLSD version -->
++<llsd>
++ <map>
++ <key>grids</key>
++ <array>
++ <map>
++ <key>name</key><string>util.aditi.lindenlab.com</string>
++ <key>label</key><string>Aditi</string>
++ <key>login_uri</key><string>https://login.aditi.lindenlab.com/cgi-bin/login.cgi</string>
++ <key>helper_uri</key><string>http://aditi-secondlife.webdev.lindenlab.com/helpers/</string>
++ <key>login_page</key><string>http://secondlife.com/app/login/</string>
++ </map>
++ <map>
++ <key>name</key><string>osgrid</string>
++ <key>label</key><string>OSGrid</string>
++ <key>login_uri</key><string>http://osgrid.org:8002</string>
++ <key>helper_uri</key><string>http://osgrid.org/loginpage.htm</string>
++ <key>menu_color</key><string>green</string>
++ <key>login_page</key><string>http://osgrid.org/</string>
++ </map>
++ </array>
++ </map>
++</llsd>
+diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
+index 27eacfb..9560f79 100644
+--- a/indra/newview/app_settings/settings.xml
++++ b/indra/newview/app_settings/settings.xml
+@@ -3587,7 +3587,7 @@
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+- <integer>0</integer>
++ <integer>1</integer>
+ </map>
+ <key>ForceMandatoryUpdate</key>
+ <map>
+diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
+index 09d1141..d93e503 100644
+--- a/indra/newview/llappviewer.cpp
++++ b/indra/newview/llappviewer.cpp
+@@ -453,31 +453,6 @@ static void settings_modify()
+
+ void LLAppViewer::initGridChoice()
+ {
+- // Load up the initial grid choice from:
+- // - hard coded defaults...
+- // - command line settings...
+- // - if dev build, persisted settings...
+-
+- // Set the "grid choice", this is specified by command line.
+- std::string grid_choice = gSavedSettings.getString("CmdLineGridChoice");
+- LLViewerLogin::getInstance()->setGridChoice(grid_choice);
+-
+- // Load last server choice by default
+- // ignored if the command line grid choice has been set
+- if(grid_choice.empty())
+- {
+- S32 server = gSavedSettings.getS32("ServerChoice");
+- server = llclamp(server, 0, (S32)GRID_INFO_COUNT - 1);
+- if(server == GRID_INFO_OTHER)
+- {
+- std::string custom_server = gSavedSettings.getString("CustomServer");
+- LLViewerLogin::getInstance()->setGridChoice(custom_server);
+- }
+- else if(server != (S32)GRID_INFO_NONE)
+- {
+- LLViewerLogin::getInstance()->setGridChoice((EGridInfo)server);
+- }
+- }
+ }
+
+ //virtual
+diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
+index 64eecd6..268ec23 100644
+--- a/indra/newview/llpanellogin.cpp
++++ b/indra/newview/llpanellogin.cpp
+@@ -131,103 +131,8 @@ void LLLoginHandler::parse(const LLSD& queryMap)
+ mWebLoginKey = queryMap["web_login_key"].asUUID();
+ mFirstName = queryMap["first_name"].asString();
+ mLastName = queryMap["last_name"].asString();
+-
+- EGridInfo grid_choice = GRID_INFO_NONE;
+- if (queryMap["grid"].asString() == "aditi")
+- {
+- grid_choice = GRID_INFO_ADITI;
+- }
+- else if (queryMap["grid"].asString() == "agni")
+- {
+- grid_choice = GRID_INFO_AGNI;
+- }
+- else if (queryMap["grid"].asString() == "siva")
+- {
+- grid_choice = GRID_INFO_SIVA;
+- }
+- else if (queryMap["grid"].asString() == "damballah")
+- {
+- grid_choice = GRID_INFO_DAMBALLAH;
+- }
+- else if (queryMap["grid"].asString() == "durga")
+- {
+- grid_choice = GRID_INFO_DURGA;
+- }
+- else if (queryMap["grid"].asString() == "shakti")
+- {
+- grid_choice = GRID_INFO_SHAKTI;
+- }
+- else if (queryMap["grid"].asString() == "soma")
+- {
+- grid_choice = GRID_INFO_SOMA;
+- }
+- else if (queryMap["grid"].asString() == "ganga")
+- {
+- grid_choice = GRID_INFO_GANGA;
+- }
+- else if (queryMap["grid"].asString() == "vaak")
+- {
+- grid_choice = GRID_INFO_VAAK;
+- }
+- else if (queryMap["grid"].asString() == "uma")
+- {
+- grid_choice = GRID_INFO_UMA;
+- }
+- else if (queryMap["grid"].asString() == "mohini")
+- {
+- grid_choice = GRID_INFO_MOHINI;
+- }
+- else if (queryMap["grid"].asString() == "yami")
+- {
+- grid_choice = GRID_INFO_YAMI;
+- }
+- else if (queryMap["grid"].asString() == "nandi")
+- {
+- grid_choice = GRID_INFO_NANDI;
+- }
+- else if (queryMap["grid"].asString() == "mitra")
+- {
+- grid_choice = GRID_INFO_MITRA;
+- }
+- else if (queryMap["grid"].asString() == "radha")
+- {
+- grid_choice = GRID_INFO_RADHA;
+- }
+- else if (queryMap["grid"].asString() == "ravi")
+- {
+- grid_choice = GRID_INFO_RAVI;
+- }
+- else if (queryMap["grid"].asString() == "aruna")
+- {
+- grid_choice = GRID_INFO_ARUNA;
+- }
+- else if (queryMap["grid"].asString() == "bharati")
+- {
+- grid_choice = GRID_INFO_BHARATI;
+- }
+- else if (queryMap["grid"].asString() == "chandra")
+- {
+- grid_choice = GRID_INFO_CHANDRA;
+- }
+- else if (queryMap["grid"].asString() == "danu")
+- {
+- grid_choice = GRID_INFO_DANU;
+- }
+- else if (queryMap["grid"].asString() == "parvati")
+- {
+- grid_choice = GRID_INFO_PARVATI;
+- }
+- else if (queryMap["grid"].asString() == "skanda")
+- {
+- grid_choice = GRID_INFO_SKANDA;
+- }
+
+- if(grid_choice != GRID_INFO_NONE)
+- {
+- LLViewerLogin::getInstance()->setGridChoice(grid_choice);
+- }
+-
+- std::string startLocation = queryMap["location"].asString();
++ std::string startLocation = queryMap["location"].asString();
+
+ if (startLocation == "specify")
+ {
+@@ -909,11 +814,8 @@ void LLPanelLogin::loadLoginPage()
+
+ std::ostringstream oStr;
+
+- std::string login_page = gSavedSettings.getString("LoginPage");
+- if (login_page.empty())
+- {
+- login_page = sInstance->getString( "real_url" );
+- }
++ std::string login_page=LLViewerLogin::getInstance()->getGridPage();
++
+ oStr << login_page;
+
+ // Use the right delimeter depending on how LLURI parses the URL
+@@ -953,6 +855,7 @@ void LLPanelLogin::loadLoginPage()
+ curl_free(curl_grid);
+
+ gViewerWindow->setMenuBackgroundColor(false, !LLViewerLogin::getInstance()->isInProductionGrid());
++ LLViewerLogin::getInstance()->setMenuColor();
+ gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor());
+
+
+@@ -1175,7 +1078,7 @@ void LLPanelLogin::onSelectServer(LLUICtrl*, void*)
+ {
+ grid_index = combo->getValue().asInteger();
+
+- if ((S32)GRID_INFO_OTHER == grid_index)
++ if ((S32)LLViewerLogin::getInstance()->getGridCount() == grid_index)
+ {
+ // This happens if the user specifies a custom grid
+ // via command line.
+@@ -1185,7 +1088,7 @@ void LLPanelLogin::onSelectServer(LLUICtrl*, void*)
+ else
+ {
+ // no valid selection, return other
+- grid_index = (S32)GRID_INFO_OTHER;
++ grid_index = (S32)LLViewerLogin::getInstance()->getGridCount();
+ grid_label = combo_val.asString();
+ }
+
+@@ -1193,15 +1096,24 @@ void LLPanelLogin::onSelectServer(LLUICtrl*, void*)
+ // from the command line.
+ LLViewerLogin* vl = LLViewerLogin::getInstance();
+ vl->resetURIs();
+- if(grid_index != GRID_INFO_OTHER)
++ if(grid_index != LLViewerLogin::getInstance()->getGridCount())
+ {
+- vl->setGridChoice((EGridInfo)grid_index);
++ vl->setGridChoice(grid_index);
+ }
+ else
+ {
+ vl->setGridChoice(grid_label);
+ }
+
++ // clear the password if we are switching grids so we don't send
++ // the wrong pass to the wrong grid.
++ if (sInstance)
++ {
++ // no method to clear a text box?
++ const std::string nothing("");
++ sInstance->childSetText("password_edit", nothing);
++ }
++
+ // grid changed so show new splash screen (possibly)
+ loadLoginPage();
+ }
+diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
+index 5f25dc3..92bd3d1 100644
+--- a/indra/newview/llstartup.cpp
++++ b/indra/newview/llstartup.cpp
+@@ -2525,12 +2525,12 @@ void login_show()
+
+ LL_DEBUGS("AppInit") << "Setting Servers" << LL_ENDL;
+
+- LLPanelLogin::addServer(LLViewerLogin::getInstance()->getGridLabel(), LLViewerLogin::getInstance()->getGridChoice());
++ //LLPanelLogin::addServer(LLViewerLogin::getInstance()->getGridLabel(), LLViewerLogin::getInstance()->getGridChoice());
+
+ LLViewerLogin* vl = LLViewerLogin::getInstance();
+- for(int grid_index = GRID_INFO_ADITI; grid_index < GRID_INFO_OTHER; ++grid_index)
++ for(int grid_index = 1; grid_index < LLViewerLogin::getInstance()->getGridCount() ; ++grid_index)
+ {
+- LLPanelLogin::addServer(vl->getKnownGridLabel((EGridInfo)grid_index), grid_index);
++ LLPanelLogin::addServer(vl->getKnownGridLabel(grid_index).c_str(), grid_index);
+ }
+ }
+
+diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp
+index 1830b76..22a05c0 100644
+--- a/indra/newview/llviewernetwork.cpp
++++ b/indra/newview/llviewernetwork.cpp
+@@ -34,148 +34,126 @@
+
+ #include "llviewernetwork.h"
+ #include "llviewercontrol.h"
++#include "llsd.h"
++#include "llsdserialize.h"
++#include "llviewermenu.h"
+
+-struct LLGridData
+-{
+- const char* mLabel;
+- const char* mName;
+- const char* mLoginURI;
+- const char* mHelperURI;
+-};
++const int DEFAULT_GRID_CHOICE=1;
++const int GRID_INFO_NONE=0;
+
+-static LLGridData gGridInfo[GRID_INFO_COUNT] =
+-{
+- { "None", "", "", ""},
+- { "Aditi",
+- "util.aditi.lindenlab.com",
+- "https://login.aditi.lindenlab.com/cgi-bin/login.cgi",
+- "http://aditi-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Agni",
+- "util.agni.lindenlab.com",
+- "https://login.agni.lindenlab.com/cgi-bin/login.cgi",
+- "https://secondlife.com/helpers/" },
+- { "Aruna",
+- "util.aruna.lindenlab.com",
+- "https://login.aruna.lindenlab.com/cgi-bin/login.cgi",
+- "http://aruna-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Bharati",
+- "util.bharati.lindenlab.com",
+- "https://login.bharati.lindenlab.com/cgi-bin/login.cgi",
+- "http://bharati-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Chandra",
+- "util.chandra.lindenlab.com",
+- "https://login.chandra.lindenlab.com/cgi-bin/login.cgi",
+- "http://chandra-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Damballah",
+- "util.damballah.lindenlab.com",
+- "https://login.damballah.lindenlab.com/cgi-bin/login.cgi",
+- "http://damballah-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Danu",
+- "util.danu.lindenlab.com",
+- "https://login.danu.lindenlab.com/cgi-bin/login.cgi",
+- "http://danu-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Durga",
+- "util.durga.lindenlab.com",
+- "https://login.durga.lindenlab.com/cgi-bin/login.cgi",
+- "http://durga-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Ganga",
+- "util.ganga.lindenlab.com",
+- "https://login.ganga.lindenlab.com/cgi-bin/login.cgi",
+- "http://ganga-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Mitra",
+- "util.mitra.lindenlab.com",
+- "https://login.mitra.lindenlab.com/cgi-bin/login.cgi",
+- "http://mitra-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Mohini",
+- "util.mohini.lindenlab.com",
+- "https://login.mohini.lindenlab.com/cgi-bin/login.cgi",
+- "http://mohini-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Nandi",
+- "util.nandi.lindenlab.com",
+- "https://login.nandi.lindenlab.com/cgi-bin/login.cgi",
+- "http://nandi-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Parvati",
+- "util.parvati.lindenlab.com",
+- "https://login.parvati.lindenlab.com/cgi-bin/login.cgi",
+- "http://parvati-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Radha",
+- "util.radha.lindenlab.com",
+- "https://login.radha.lindenlab.com/cgi-bin/login.cgi",
+- "http://radha-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Ravi",
+- "util.ravi.lindenlab.com",
+- "https://login.ravi.lindenlab.com/cgi-bin/login.cgi",
+- "http://ravi-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Siva",
+- "util.siva.lindenlab.com",
+- "https://login.siva.lindenlab.com/cgi-bin/login.cgi",
+- "http://siva-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Shakti",
+- "util.shakti.lindenlab.com",
+- "https://login.shakti.lindenlab.com/cgi-bin/login.cgi",
+- "http://shakti-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Skanda",
+- "util.skanda.lindenlab.com",
+- "https://login.skanda.lindenlab.com/cgi-bin/login.cgi",
+- "http://skanda-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Soma",
+- "util.soma.lindenlab.com",
+- "https://login.soma.lindenlab.com/cgi-bin/login.cgi",
+- "http://soma-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Uma",
+- "util.uma.lindenlab.com",
+- "https://login.uma.lindenlab.com/cgi-bin/login.cgi",
+- "http://uma-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Vaak",
+- "util.vaak.lindenlab.com",
+- "https://login.vaak.lindenlab.com/cgi-bin/login.cgi",
+- "http://vaak-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Yami",
+- "util.yami.lindenlab.com",
+- "https://login.yami.lindenlab.com/cgi-bin/login.cgi",
+- "http://yami-secondlife.webdev.lindenlab.com/helpers/" },
+- { "Local",
+- "localhost",
+- "https://login.dmz.lindenlab.com/cgi-bin/login.cgi",
+- "" },
+- { "Other",
+- "",
+- "https://login.dmz.lindenlab.com/cgi-bin/login.cgi",
+- "" }
+-};
++unsigned char gMACAddress[MAC_ADDRESS_BYTES]; /* Flawfinder: ignore */
+
+-const EGridInfo DEFAULT_GRID_CHOICE = GRID_INFO_AGNI;
++ LLViewerLogin::LLViewerLogin() :
++ mGridChoice(DEFAULT_GRID_CHOICE)
++ {
+
++ gridcount=0;
++ LLSD array = mGridList.emptyArray();
++ LLSD entry = mGridList.emptyMap();
++ entry.insert( "label", "None" );
++ entry.insert( "name", "" );
++ entry.insert( "login_uri", "" );
++ entry.insert( "helper_uri", "" );
++ entry.insert( "login_page", "" );
+
+-unsigned char gMACAddress[MAC_ADDRESS_BYTES]; /* Flawfinder: ignore */
++ array.append( entry );
++ gridcount++;
++
++ entry = mGridList.emptyMap();
++ entry.insert( "label", "Agni" );
++ entry.insert( "name", "util.agni.lindenlab.com" );
++ entry.insert( "login_uri", "https://login.agni.lindenlab.com/cgi-bin/login.cgi" );
++ entry.insert( "helper_uri", "https://secondlife.com/helpers/" );
++ entry.insert( "login_page", "http://secondlife.com/app/login/" );
++ array.append( entry );
++ gridcount++;
++
++ mGridList.insert("grids", array );
++
++ // load the linden grids if available
++ loadGridsLLSD( gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"grids.xml") );
++ // see if we have a grids.xml file in "user_settings" to append
++ loadGridsLLSD( gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"grids.xml") );
++}
++
++void LLViewerLogin::loadGridsLLSD( std::string xml_filename )
++{
++ LLSD other_grids;
++ llifstream llsd_xml;
++ llsd_xml.open( xml_filename.c_str(), std::ios::in | std::ios::binary );
++
++ if( llsd_xml.is_open()) {
++ llinfos << "Reading grid info: " << xml_filename << llendl;
++ LLSDSerialize::fromXML( other_grids, llsd_xml );
++ for(LLSD::map_iterator grid_itr = other_grids.beginMap();
++ grid_itr != other_grids.endMap();
++ ++grid_itr)
++ {
++ LLSD::String key_name = grid_itr->first;
++ LLSD grid_array = grid_itr->second;
++ llinfos << "reading: " << key_name << llendl;
++ if( grid_array.isArray()) {
++ for( int i = 0; i < grid_array.size(); i++ ) {
++ LLSD gmap = grid_array[i];
++ if( gmap.has("name") && gmap.has("label") &&
++ gmap.has("login_uri") && gmap.has("helper_uri" )) {
++ mGridList["grids"].append( gmap );
++ gridcount++;
++ llinfos << "Added grid: " << gmap.get("name") << llendl;
++ }
++ else {
++ if( gmap.has("name")) {
++ llwarns << "Incomplete grid definition: " << gmap.get("name")<<llendl;
++ } else {
++ llwarns << "Incomplete grid definition: no name specified" << llendl;
++ }
++ }
++ }
++ } else {
++ llwarns << "\"grids\" is not an array" << llendl;
++ }
++ }
++ llsd_xml.close();
++ }
++}
+
+-LLViewerLogin::LLViewerLogin() :
+- mGridChoice(DEFAULT_GRID_CHOICE)
++void LLViewerLogin::setMenuColor() const
+ {
++ if( mGridList["grids"][mGridChoice].has("menu_color")) {
++ std::string colorName = mGridList["grids"][mGridChoice].get("menu_color").asString();
++ LLColor4 color4;
++ LLColor4::parseColor( colorName.c_str(), &color4 );
++ if( color4 != LLColor4::black ) {
++ gMenuBarView->setBackgroundColor(color4);
++ }
++ }
+ }
+
+-void LLViewerLogin::setGridChoice(EGridInfo grid)
++
++void LLViewerLogin::setGridChoice(int grid)
+ {
+- if(grid < 0 || grid >= GRID_INFO_COUNT)
++ if(grid < 0 || grid >= mGridList.get("grids").size())
+ {
+- llerrs << "Invalid grid index specified." << llendl;
++ llwarns << "Invalid grid index specified." << llendl;
++ grid = DEFAULT_GRID_CHOICE;
+ }
+
+ if(mGridChoice != grid || gSavedSettings.getS32("ServerChoice") != grid)
+ {
+ mGridChoice = grid;
+- if(GRID_INFO_LOCAL == mGridChoice)
++ if(mGridList.get("grids")[mGridChoice].get("label").asString() == "Local" )
+ {
+ mGridName = LOOPBACK_ADDRESS_STRING;
+ }
+- else if(GRID_INFO_OTHER == mGridChoice)
++ else if(getGridCount() == mGridChoice)
+ {
+ // *FIX:Mani - could this possibly be valid?
+ mGridName = "other";
+ }
+ else
+ {
+- mGridName = gGridInfo[mGridChoice].mLabel;
++ mGridName = mGridList.get("grids")[mGridChoice].get("label").asString();
++
+ }
+
+ gSavedSettings.setS32("ServerChoice", mGridChoice);
+@@ -193,20 +171,20 @@ void LLViewerLogin::setGridChoice(const std::string& grid_name)
+ {
+ // find the grid choice from the user setting.
+ int grid_index = GRID_INFO_NONE;
+- for(;grid_index < GRID_INFO_OTHER; ++grid_index)
++ for(;grid_index < mGridList["grids"].size(); ++grid_index)
+ {
+- if(0 == LLStringUtil::compareInsensitive(gGridInfo[grid_index].mLabel, grid_name))
++ if(mGridList["grids"][grid_index].get("label").asString()==grid_name.c_str())
+ {
+ // Founding a matching label in the list...
+- setGridChoice((EGridInfo)grid_index);
++ setGridChoice(grid_index);
+ break;
+ }
+ }
+
+- if(GRID_INFO_OTHER == grid_index)
++ if(getGridCount() == grid_index)
+ {
+ // *FIX:MEP Can and should we validate that this is an IP address?
+- mGridChoice = GRID_INFO_OTHER;
++ mGridChoice = getGridCount();
+ mGridName = grid_name;
+ gSavedSettings.setS32("ServerChoice", mGridChoice);
+ gSavedSettings.setString("CustomServer", mGridName);
+@@ -221,7 +199,7 @@ void LLViewerLogin::resetURIs()
+ gSavedSettings.setString("CmdLineHelperURI", "");
+ }
+
+-EGridInfo LLViewerLogin::getGridChoice() const
++int LLViewerLogin::getGridChoice() const
+ {
+ return mGridChoice;
+ }
+@@ -232,21 +210,37 @@ std::string LLViewerLogin::getGridLabel() const
+ {
+ return "None";
+ }
+- else if(mGridChoice < GRID_INFO_OTHER)
++ else if(mGridChoice < getGridCount())
+ {
+- return gGridInfo[mGridChoice].mLabel;
++ return mGridList["grids"][mGridChoice].get("label").asString();
+ }
+
+ return mGridName;
+ }
+
+-std::string LLViewerLogin::getKnownGridLabel(EGridInfo grid_index) const
++std::string LLViewerLogin::getGridPage() const
++{
++
++
++ if(mGridChoice == GRID_INFO_NONE)
++ {
++ return "None";
++ }
++ else if(mGridChoice < getGridCount())
++ {
++ return mGridList["grids"][mGridChoice].get("login_page").asString();
++ }
++
++ return "None";
++}
++
++std::string LLViewerLogin::getKnownGridLabel(int grid_index) const
+ {
+- if(grid_index > GRID_INFO_NONE && grid_index < GRID_INFO_OTHER)
++ if(grid_index > GRID_INFO_NONE && grid_index < getGridCount())
+ {
+- return gGridInfo[grid_index].mLabel;
++ return mGridList.get("grids")[grid_index].get("label").asString();
+ }
+- return gGridInfo[GRID_INFO_NONE].mLabel;
++ return mGridList.get("grids")[GRID_INFO_NONE].get("label").asString();
+ }
+
+ void LLViewerLogin::getLoginURIs(std::vector<std::string>& uris) const
+@@ -283,9 +277,9 @@ void LLViewerLogin::getLoginURIs(std::vector<std::string>& uris) const
+ {
+ // If its a known grid choice, get the uri from the table,
+ // else try the grid name.
+- if(mGridChoice > GRID_INFO_NONE && mGridChoice < GRID_INFO_OTHER)
++ if(mGridChoice > GRID_INFO_NONE && mGridChoice < getGridCount())
+ {
+- uris.push_back(gGridInfo[mGridChoice].mLoginURI);
++ uris.push_back(mGridList["grids"][mGridChoice].get("login_uri").asString());
+ }
+ else
+ {
+@@ -300,9 +294,9 @@ std::string LLViewerLogin::getHelperURI() const
+ if (helper_uri.empty())
+ {
+ // grab URI from selected grid
+- if(mGridChoice > GRID_INFO_NONE && mGridChoice < GRID_INFO_OTHER)
++ if(mGridChoice > GRID_INFO_NONE && mGridChoice < getGridCount())
+ {
+- helper_uri = gGridInfo[mGridChoice].mHelperURI;
++ helper_uri = mGridList["grids"][mGridChoice].get("helper_uri").asString();
+ }
+
+ if (helper_uri.empty())
+diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h
+index 0bb298d..2573f0b 100644
+--- a/indra/newview/llviewernetwork.h
++++ b/indra/newview/llviewernetwork.h
+@@ -34,36 +34,7 @@
+ #define LL_LLVIEWERNETWORK_H
+
+ class LLHost;
+-
+-enum EGridInfo
+-{
+- GRID_INFO_NONE,
+- GRID_INFO_ADITI,
+- GRID_INFO_AGNI,
+- GRID_INFO_ARUNA,
+- GRID_INFO_BHARATI,
+- GRID_INFO_CHANDRA,
+- GRID_INFO_DAMBALLAH,
+- GRID_INFO_DANU,
+- GRID_INFO_DURGA,
+- GRID_INFO_GANGA,
+- GRID_INFO_MITRA,
+- GRID_INFO_MOHINI,
+- GRID_INFO_NANDI,
+- GRID_INFO_PARVATI,
+- GRID_INFO_RADHA,
+- GRID_INFO_RAVI,
+- GRID_INFO_SIVA,
+- GRID_INFO_SHAKTI,
+- GRID_INFO_SKANDA,
+- GRID_INFO_SOMA,
+- GRID_INFO_UMA,
+- GRID_INFO_VAAK,
+- GRID_INFO_YAMI,
+- GRID_INFO_LOCAL,
+- GRID_INFO_OTHER, // IP address set via command line option
+- GRID_INFO_COUNT
+-};
++class LLSD;
+
+ /**
+ * @brief A class to manage the viewer's login state.
+@@ -74,7 +45,7 @@ class LLViewerLogin : public LLSingleton<LLViewerLogin>
+ public:
+ LLViewerLogin();
+
+- void setGridChoice(EGridInfo grid);
++ void setGridChoice(const int grid);
+ void setGridChoice(const std::string& grid_name);
+ void resetURIs();
+
+@@ -82,8 +53,10 @@ public:
+ * @brief Get the enumeration of the grid choice.
+ * Should only return values > 0 && < GRID_INFO_COUNT
+ **/
+- EGridInfo getGridChoice() const;
++ int getGridChoice() const;
+
++ int getGridCount() const { return gridcount; }
++
+ /**
+ * @brief Get a readable label for the grid choice.
+ * Returns the readable name for the grid choice.
+@@ -91,17 +64,24 @@ public:
+ * the string used to specifiy the grid.
+ **/
+ std::string getGridLabel() const;
++ std::string getGridPage() const;
+
+- std::string getKnownGridLabel(EGridInfo grid_index) const;
++ std::string getKnownGridLabel(const int grid_index) const;
+
+ void getLoginURIs(std::vector<std::string>& uris) const;
+ std::string getHelperURI() const;
+
+ bool isInProductionGrid();
+
++ void setMenuColor() const;
++
++ void loadGridsLLSD( std::string filename );
++
+ private:
+- EGridInfo mGridChoice;
++ int gridcount;
++ int mGridChoice;
+ std::string mGridName;
++ LLSD mGridList;
+ };
+
+ const S32 MAC_ADDRESS_BYTES = 6;
+--
+tg: (2c826ba..) topic/features/jira-backported/VWR-7531-Dynamic-Grid-List (depends on: upstream)
diff --git a/debian/patches/topic/features/jira-backported/avatar_list.diff b/debian/patches/topic/features/jira-backported/avatar_list.diff
new file mode 100644
index 0000000..5e75521
--- /dev/null
+++ b/debian/patches/topic/features/jira-backported/avatar_list.diff
@@ -0,0 +1,5211 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/features/jira-backported/avatar_list
+
+Dale Glass's avatar scanner as published on JIRA and rebased by Carjay
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llaudio/audioengine.h | 1 +
+ indra/llmessage/llmessagetemplate.h | 111 +-
+ indra/llmessage/message.cpp | 28 +-
+ indra/llmessage/message.h | 18 +-
+ indra/llui/llscrolllistctrl.cpp | 41 +
+ indra/llui/llscrolllistctrl.h | 2 +
+ indra/newview/CMakeLists.txt | 3 +
+ indra/newview/app_settings/settings.xml | 16 +
+ indra/newview/llagent.cpp | 121 +-
+ indra/newview/llagent.h | 14 +
+ indra/newview/llappviewer.cpp | 4 +
+ indra/newview/llchatbar.cpp | 34 +-
+ indra/newview/llfloateravatarlist.cpp | 2187 ++++++++++++++++++++
+ indra/newview/llfloateravatarlist.h | 868 ++++++++
+ indra/newview/llfloaterpreference.cpp | 7 +
+ indra/newview/llfloaterpreference.h | 2 +
+ indra/newview/llfloaterreporter.cpp | 13 +-
+ indra/newview/llfloaterreporter.h | 2 +-
+ indra/newview/llpaneldatabase.cpp | 149 ++
+ indra/newview/llpaneldatabase.h | 65 +
+ indra/newview/llstartup.cpp | 2 +-
+ indra/newview/lltoolbar.cpp | 9 +
+ indra/newview/lltoolbar.h | 1 +
+ indra/newview/llviewermenu.cpp | 14 +
+ indra/newview/llviewermessage.cpp | 3 +
+ indra/newview/llviewerobject.h | 3 +-
+ indra/newview/llviewerwindow.cpp | 8 +-
+ indra/newview/llwebbrowserctrl.h | 2 +
+ indra/newview/pipeline.cpp | 42 +
+ indra/newview/skins/default/xui/en-us/alerts.xml | 135 ++
+ .../default/xui/en-us/floater_avatar_scanner.xml | 413 ++++
+ .../skins/default/xui/en-us/menu_viewer.xml | 13 +
+ indra/newview/skins/default/xui/en-us/notify.xml | 6 +
+ .../xui/en-us/panel_preferences_database.xml | 166 ++
+ .../skins/default/xui/en-us/panel_toolbar.xml | 7 +
+ 35 files changed, 4447 insertions(+), 63 deletions(-)
+
+diff --git a/indra/llaudio/audioengine.h b/indra/llaudio/audioengine.h
+index b582f14..0468862 100644
+--- a/indra/llaudio/audioengine.h
++++ b/indra/llaudio/audioengine.h
+@@ -313,6 +313,7 @@ public:
+ bool setupChannel();
+ bool play(const LLUUID &audio_id); // Start the audio source playing
+
++ const LLUUID &getOwnerID() { return mOwnerID; }
+ bool hasPendingPreloads() const; // Has preloads that haven't been done yet
+
+ friend class LLAudioEngine;
+diff --git a/indra/llmessage/llmessagetemplate.h b/indra/llmessage/llmessagetemplate.h
+index 2390eea..6638a75 100644
+--- a/indra/llmessage/llmessagetemplate.h
++++ b/indra/llmessage/llmessagetemplate.h
+@@ -35,7 +35,11 @@
+ #include "lldarray.h"
+ #include "message.h" // TODO: babbage: Remove...
+ #include "llstat.h"
++#include "llmsgvariabletype.h"
+ #include "llstl.h"
++#include <list>
++#include <algorithm>
++#include <functional>
+
+ class LLMsgVarData
+ {
+@@ -270,6 +274,30 @@ enum EMsgDeprecation
+ MD_DEPRECATED
+ };
+
++class LLMessageTemplateHandlerEntry
++{
++public:
++ LLMessageTemplateHandlerEntry(message_handler_func_t handler, void **userdata = NULL) :
++ mHandlerFunc(handler), mUserData(userdata) {}
++
++ void call(LLMessageSystem *msgsystem) const { mHandlerFunc(msgsystem, mUserData); }
++
++ bool operator==(const LLMessageTemplateHandlerEntry&a) { return mHandlerFunc == a.mHandlerFunc; }
++private:
++ // message handler function (this is set by each application)
++ message_handler_func_t mHandlerFunc;
++ void **mUserData;
++};
++
++class callHandler : public std::unary_function<LLMessageTemplateHandlerEntry, void>
++{
++public:
++ callHandler(LLMessageSystem *msg) : mMsg(msg) {}
++ void operator()(const LLMessageTemplateHandlerEntry& a) const { a.call(mMsg); }
++private:
++ LLMessageSystem *mMsg;
++};
++
+ class LLMessageTemplate
+ {
+ public:
+@@ -291,9 +319,7 @@ public:
+ mTotalDecodeTime(0.f),
+ mMaxDecodeTimePerMsg(0.f),
+ mBanFromTrusted(false),
+- mBanFromUntrusted(false),
+- mHandlerFunc(NULL),
+- mUserData(NULL)
++ mBanFromUntrusted(false)
+ {
+ mName = LLMessageStringTable::getInstance()->getString(name);
+ }
+@@ -361,21 +387,84 @@ public:
+ return mDeprecation;
+ }
+
+- void setHandlerFunc(void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data)
++ /**
++ * @brief Adds a handler
++ * This function adds a new handler to be called when the message arrives.
++ * Repeated additions of the same handler function will be ignored.
++ * @note delHandlerFunc() must be called to remove the registration
++ * @param handler Function to call
++ * @param user_data User specified data to pass to the function
++ */
++ void addHandlerFunc(message_handler_func_t handler, void **user_data)
++ {
++ LLMessageTemplateHandlerEntry h(handler, user_data);
++
++ if ( std::find(mHandlers.begin(), mHandlers.end(), h ) != mHandlers.end() )
++ {
++ return;
++ }
++
++ mHandlers.push_back( h );
++ }
++
++ /**
++ * @brief Sets a handler
++ * This function sets a handler to be called when the message arrives.
++ * Any existing handlers are unregistered.
++ * @note delHandlerFunc() must be called to remove the registration
++ * @param handler Function to call
++ * @param user_data User specified data to pass to the function
++ */
++ void setHandlerFunc(message_handler_func_t handler, void **user_data)
++ {
++ mHandlers.clear();
++ if( handler )
++ {
++ addHandlerFunc(handler, user_data);
++ }
++ else
++ {
++ llwarns << "code has reset handler for \"" << mName << "\" by setting it to NULL." << llendl;
++ }
++ }
++
++ /**
++ * @brief Removes a handler
++ * Removes a handler from the list of handlers.
++ * Attempts to remove handlers that aren't in the list are silently
++ * ignored.
++ * @param handler Function to remove
++ */
++ void delHandlerFunc(message_handler_func_t handler)
+ {
+- mHandlerFunc = handler_func;
+- mUserData = user_data;
++ mHandlers.remove( LLMessageTemplateHandlerEntry(handler) );
+ }
+
+ BOOL callHandlerFunc(LLMessageSystem *msgsystem) const
+ {
+- if (mHandlerFunc)
++ if ( mHandlers.empty() )
++ {
++ return FALSE;
++ }
++ /*
++ * Be on the safe side and use for_each only when necessary. There is Linden code ("ReplyPayPrice") that
++ * does not take the multiple reply handlers into account and simply tries to unregister
++ * by setting the handler function to 0, unfortunately from within the reply handler so in this case
++ * the for_each iterator inside std_algo.h is invalidated leading to a crash if the memory is reused
++ * in between.
++ */
++ else if( mHandlers.size() == 1 )
+ {
+ LLPerfBlock msg_cb_time("msg_cb", mName);
+- mHandlerFunc(msgsystem, mUserData);
++ mHandlers.begin()->call(msgsystem);
++ return TRUE;
++ }
++ else
++ {
++ LLPerfBlock msg_cb_time("msg_cb", mName);
++ std::for_each(mHandlers.begin(), mHandlers.end(), callHandler(msgsystem));
+ return TRUE;
+ }
+- return FALSE;
+ }
+
+ bool isUdpBanned() const
+@@ -420,9 +509,7 @@ public:
+ bool mBanFromUntrusted;
+
+ private:
+- // message handler function (this is set by each application)
+- void (*mHandlerFunc)(LLMessageSystem *msgsystem, void **user_data);
+- void **mUserData;
++ std::list<LLMessageTemplateHandlerEntry> mHandlers;
+ };
+
+ #endif // LL_LLMESSAGETEMPLATE_H
+diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp
+index ccc3d79..30d7676 100644
+--- a/indra/llmessage/message.cpp
++++ b/indra/llmessage/message.cpp
+@@ -2972,7 +2972,7 @@ void LLMessageSystem::addTemplate(LLMessageTemplate *templatep)
+ }
+
+
+-void LLMessageSystem::setHandlerFuncFast(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data)
++void LLMessageSystem::setHandlerFuncFast(const char *name, message_handler_func_t handler_func, void **user_data)
+ {
+ LLMessageTemplate* msgtemplate = get_ptr_in_map(mMessageTemplates, name);
+ if (msgtemplate)
+@@ -2985,6 +2985,32 @@ void LLMessageSystem::setHandlerFuncFast(const char *name, void (*handler_func)(
+ }
+ }
+
++void LLMessageSystem::addHandlerFuncFast(const char *name, message_handler_func_t handler_func, void **user_data)
++{
++ LLMessageTemplate* msgtemplate = get_ptr_in_map(mMessageTemplates, name);
++ if (msgtemplate)
++ {
++ msgtemplate->addHandlerFunc(handler_func, user_data);
++ }
++ else
++ {
++ llerrs << name << " is not a known message name!" << llendl;
++ }
++}
++
++void LLMessageSystem::delHandlerFuncFast(const char *name, message_handler_func_t handler_func)
++{
++ LLMessageTemplate* msgtemplate = get_ptr_in_map(mMessageTemplates, name);
++ if (msgtemplate)
++ {
++ msgtemplate->delHandlerFunc(handler_func);
++ }
++ else
++ {
++ llerrs << name << " is not a known message name!" << llendl;
++ }
++}
++
+ bool LLMessageSystem::callHandler(const char *name,
+ bool trustedSource, LLMessageSystem* msg)
+ {
+diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h
+index c503a58..bace5a1 100644
+--- a/indra/llmessage/message.h
++++ b/indra/llmessage/message.h
+@@ -179,7 +179,7 @@ enum EMessageException
+ MX_WROTE_PAST_BUFFER_SIZE // wrote past buffer size in zero code expand
+ };
+ typedef void (*msg_exception_callback)(LLMessageSystem*,void*,EMessageException);
+-
++typedef void (*message_handler_func_t)(LLMessageSystem *msgsystem, void **user_data);
+
+ // message data pieces are used to collect the data called for by the message template
+ class LLMsgData;
+@@ -299,12 +299,24 @@ public:
+
+
+ // methods for building, sending, receiving, and handling messages
+- void setHandlerFuncFast(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data = NULL);
+- void setHandlerFunc(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data = NULL)
++ void setHandlerFuncFast(const char *name, message_handler_func_t, void **user_data = NULL);
++ void setHandlerFunc(const char *name, message_handler_func_t handler_func, void **user_data = NULL)
+ {
+ setHandlerFuncFast(LLMessageStringTable::getInstance()->getString(name), handler_func, user_data);
+ }
+
++ void addHandlerFuncFast(const char *name, message_handler_func_t, void **user_data = NULL);
++ void addHandlerFunc(const char *name, message_handler_func_t handler_func, void **user_data = NULL)
++ {
++ addHandlerFuncFast(LLMessageStringTable::getInstance()->getString(name), handler_func, user_data);
++ }
++
++ void delHandlerFuncFast(const char *name, message_handler_func_t);
++ void delHandlerFunc(const char *name, message_handler_func_t handler_func)
++ {
++ delHandlerFuncFast(LLMessageStringTable::getInstance()->getString(name), handler_func);
++ }
++
+ // Set a callback function for a message system exception.
+ void setExceptionFunc(EMessageException exception, msg_exception_callback func, void* data = NULL);
+ // Call the specified exception func, and return TRUE if a
+diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
+index 7dba55f..8cff81e 100644
+--- a/indra/llui/llscrolllistctrl.cpp
++++ b/indra/llui/llscrolllistctrl.cpp
+@@ -703,6 +703,29 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
+ return ret;
+ }
+
++/**
++ * Returns the selected IDs
++ * @returns List of selected IDs
++ * @author Dale Glass
++ */
++LLDynamicArray<LLUUID> LLScrollListCtrl::getSelectedIDs()
++{
++ LLUUID selected_id;
++ LLDynamicArray<LLUUID> ret;
++
++ item_list::const_iterator iter;
++ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
++ {
++ LLScrollListItem* item = *iter;
++ if (item->getSelected())
++ {
++ ret.push_back(item->getUUID());
++ }
++ }
++ return ret;
++}
++
++
+ S32 LLScrollListCtrl::getFirstSelectedIndex() const
+ {
+ S32 CurSelectedIndex = 0;
+@@ -2534,6 +2557,23 @@ void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void
+ }
+
+
++/**
++ * Re-sorts the list
++ *
++ * This function allows to avoid multiple unnecessary sorts in the case where
++ * multiple elements will be added or removed at once.
++ * @author Dale Glass
++ */
++void LLScrollListCtrl::sort()
++{
++ // sort by column 0, in ascending order
++ std::stable_sort(
++ mItemList.begin(),
++ mItemList.end(),
++ SortScrollListItem(mSortColumns));
++}
++
++
+ void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending)
+ {
+ std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.find(name);
+@@ -3851,3 +3891,4 @@ BOOL LLColumnHeader::canResize()
+ {
+ return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth);
+ }
++
+diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
+index f276562..859e6ae 100644
+--- a/indra/llui/llscrolllistctrl.h
++++ b/indra/llui/llscrolllistctrl.h
+@@ -421,6 +421,7 @@ public:
+ // Sets an array of column descriptors
+ void setColumnHeadings(LLSD headings);
+ void sortByColumnIndex(U32 column, BOOL ascending);
++ void sort();
+
+ // LLCtrlListInterface functions
+ virtual S32 getItemCount() const;
+@@ -518,6 +519,7 @@ public:
+ virtual S32 getFirstSelectedIndex() const;
+ std::vector<LLScrollListItem*> getAllSelected() const;
+ LLScrollListItem* getLastSelectedItem() const { return mLastSelected; }
++ LLDynamicArray<LLUUID> getSelectedIDs();
+
+ // iterate over all items
+ LLScrollListItem* getFirstData() const;
+diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
+index b95ba37..ec98c7c 100644
+--- a/indra/newview/CMakeLists.txt
++++ b/indra/newview/CMakeLists.txt
+@@ -123,6 +123,7 @@ set(viewer_SOURCE_FILES
+ llfloateranimpreview.cpp
+ llfloaterauction.cpp
+ llfloateravatarinfo.cpp
++ llfloateravatarlist.cpp
+ llfloateravatarpicker.cpp
+ llfloateravatartextures.cpp
+ llfloaterbeacons.cpp
+@@ -245,6 +246,7 @@ set(viewer_SOURCE_FILES
+ llpanelaudioprefs.cpp
+ llpanelaudiovolume.cpp
+ llpanelavatar.cpp
++ llpaneldatabase.cpp
+ llpanelclassified.cpp
+ llpanelcontents.cpp
+ llpaneldebug.cpp
+@@ -639,6 +641,7 @@ set(viewer_HEADER_FILES
+ llpanelaudioprefs.h
+ llpanelaudiovolume.h
+ llpanelavatar.h
++ llpaneldatabase.h
+ llpanelclassified.h
+ llpanelcontents.h
+ llpaneldebug.h
+diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
+index 27eacfb..225894c 100644
+--- a/indra/newview/app_settings/settings.xml
++++ b/indra/newview/app_settings/settings.xml
+@@ -2622,6 +2622,22 @@
+ <integer>0</integer>
+ </array>
+ </map>
++ <key>FloaterAvatarListRect</key>
++ <map>
++ <key>Comment</key>
++ <string>Rectangle avatar list window</string>
++ <key>Persist</key>
++ <integer>1</integer>
++ <key>Type</key>
++ <string>Rect</string>
++ <key>Value</key>
++ <array>
++ <integer>0</integer>
++ <integer>400</integer>
++ <integer>200</integer>
++ <integer>0</integer>
++ </array>
++ </map>
+ <key>FloaterAudioVolumeRect</key>
+ <map>
+ <key>Comment</key>
+diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
+index 301f00b..23982ee 100644
+--- a/indra/newview/llagent.cpp
++++ b/indra/newview/llagent.cpp
+@@ -67,6 +67,7 @@
+ #include "llfloater.h"
+ #include "llfloateractivespeakers.h"
+ #include "llfloateravatarinfo.h"
++#include "llfloateravatarlist.h"
+ #include "llfloaterbuildoptions.h"
+ #include "llfloatercamera.h"
+ #include "llfloaterchat.h"
+@@ -2747,7 +2748,18 @@ void LLAgent::startTyping()
+ {
+ sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START);
+ }
+- gChatBar->sendChatFromViewer("", CHAT_TYPE_START, FALSE);
++ sendChat("", 0, CHAT_TYPE_START, false);
++
++ // Addition for avatar list support.
++ // Makes the fact that this avatar is typing appear in the list
++ if ( NULL != gFloaterAvatarList )
++ {
++ LLAvatarListEntry *ent = gFloaterAvatarList->getAvatarEntry(getID());
++ if ( NULL != ent )
++ {
++ ent->setActivity(ACTIVITY_TYPING);
++ }
++ }
+ }
+
+ //-----------------------------------------------------------------------------
+@@ -2759,7 +2771,7 @@ void LLAgent::stopTyping()
+ {
+ clearRenderState(AGENT_STATE_TYPING);
+ sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP);
+- gChatBar->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE);
++ sendChat("", 0, CHAT_TYPE_STOP, false);
+ }
+ }
+
+@@ -4585,10 +4597,16 @@ void LLAgent::heardChat(const LLUUID& id)
+ mChatTimer.reset();
+ }
+
++
++void LLAgent::lookAtLastChat()
++{
++ lookAtObject(mLastChatterID, CAMERA_POSITION_SELF);
++}
++
+ //-----------------------------------------------------------------------------
+ // lookAtLastChat()
+ //-----------------------------------------------------------------------------
+-void LLAgent::lookAtLastChat()
++void LLAgent::lookAtObject(LLUUID object_id, ECameraPosition camera_pos)
+ {
+ // Block if camera is animating or not in normal third person camera mode
+ if (mCameraAnimating || !cameraThirdPerson())
+@@ -4596,7 +4614,7 @@ void LLAgent::lookAtLastChat()
+ return;
+ }
+
+- LLViewerObject *chatter = gObjectList.findObject(mLastChatterID);
++ LLViewerObject *chatter = gObjectList.findObject(object_id);
+ if (chatter)
+ {
+ LLVector3 delta_pos;
+@@ -4625,15 +4643,39 @@ void LLAgent::lookAtLastChat()
+ new_camera_pos -= delta_pos * 0.4f;
+ new_camera_pos += left * 0.3f;
+ new_camera_pos += up * 0.2f;
++
++ F32 radius = chatter_av->getVObjRadius();
++ LLVector3d view_dist(radius, radius, 0.0f);
++
+ if (chatter_av->mHeadp)
+ {
+- setFocusGlobal(getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()), mLastChatterID);
++ setFocusGlobal(getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()), object_id);
+ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition());
++
++ switch(camera_pos)
++ {
++ case CAMERA_POSITION_SELF:
++ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition());
++ break;
++ case CAMERA_POSITION_OBJECT:
++ mCameraFocusOffsetTarget = view_dist;
++ break;
++ }
+ }
+ else
+ {
+- setFocusGlobal(chatter->getPositionGlobal(), mLastChatterID);
++ setFocusGlobal(chatter->getPositionGlobal(), object_id);
+ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
++
++ switch(camera_pos)
++ {
++ case CAMERA_POSITION_SELF:
++ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
++ break;
++ case CAMERA_POSITION_OBJECT:
++ mCameraFocusOffsetTarget = view_dist;
++ break;
++ }
+ }
+ setFocusOnAvatar(FALSE, TRUE);
+ }
+@@ -4655,8 +4697,20 @@ void LLAgent::lookAtLastChat()
+ new_camera_pos += left * 0.3f;
+ new_camera_pos += up * 0.2f;
+
+- setFocusGlobal(chatter->getPositionGlobal(), mLastChatterID);
+- mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
++ setFocusGlobal(chatter->getPositionGlobal(), object_id);
++
++ switch(camera_pos)
++ {
++ case CAMERA_POSITION_SELF:
++ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
++ break;
++ case CAMERA_POSITION_OBJECT:
++ F32 radius = chatter->getVObjRadius();
++ LLVector3d view_dist(radius, radius, 0.0f);
++ mCameraFocusOffsetTarget = view_dist;
++ break;
++ }
++
+ setFocusOnAvatar(FALSE, TRUE);
+ }
+ }
+@@ -6012,6 +6066,57 @@ void LLAgent::setTeleportState(ETeleportState state)
+ }
+ }
+
++void LLAgent::sendChat(const std::string &text, S32 channel, EChatType type, bool animate)
++{
++
++ // Don't animate for chats people can't hear (chat to scripts)
++ if (animate && (channel == 0))
++ {
++ if (type == CHAT_TYPE_WHISPER)
++ {
++ lldebugs << "You whisper " << text << llendl;
++ sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START);
++ }
++ else if (type == CHAT_TYPE_NORMAL)
++ {
++ lldebugs << "You say " << text << llendl;
++ sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START);
++ }
++ else if (type == CHAT_TYPE_SHOUT)
++ {
++ lldebugs << "You shout " << text << llendl;
++ sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START);
++ }
++ else
++ {
++ llinfos << "send_chat_from_viewer() - invalid volume" << llendl;
++ return;
++ }
++ }
++ else
++ {
++ if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP)
++ {
++ lldebugs << "Channel chat: " << text << llendl;
++ }
++ }
++
++ LLMessageSystem* msg = gMessageSystem;
++
++ msg->newMessageFast(_PREHASH_ChatFromViewer);
++ msg->nextBlockFast(_PREHASH_AgentData);
++ msg->addUUIDFast(_PREHASH_AgentID, getID());
++ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
++ msg->nextBlockFast(_PREHASH_ChatData);
++ msg->addStringFast(_PREHASH_Message, text);
++ msg->addU8Fast(_PREHASH_Type, type);
++ msg->addS32("Channel", channel);
++
++ gAgent.sendReliableMessage();
++
++ LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT);
++}
++
+ void LLAgent::fidget()
+ {
+ if (!getAFK())
+diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
+index 3d3a94b..2b325f9 100644
+--- a/indra/newview/llagent.h
++++ b/indra/newview/llagent.h
+@@ -36,6 +36,7 @@
+
+ #include "indra_constants.h"
+ #include "llmath.h"
++#include "llchat.h"
+ #include "llcontrol.h"
+ #include "llcoordframe.h"
+ #include "llevent.h"
+@@ -80,6 +81,15 @@ typedef enum e_camera_modes
+ CAMERA_MODE_FOLLOW
+ } ECameraMode;
+
++/**
++ * @brief When looking at an object, where is the camera offset from
++ */
++typedef enum e_camera_position
++{
++ CAMERA_POSITION_SELF, /** Camera positioned at our position */
++ CAMERA_POSITION_OBJECT /** Camera positioned at observed object's position */
++} ECameraPosition;
++
+ typedef enum e_anim_request
+ {
+ ANIM_REQUEST_START,
+@@ -206,6 +216,7 @@ public:
+
+ void heardChat(const LLUUID& id);
+ void lookAtLastChat();
++ void lookAtObject(LLUUID avatar_id, ECameraPosition camera_pos);
+ F32 getTypingTime() { return mTypingTimer.getElapsedTimeF32(); }
+
+ void setAFK();
+@@ -479,6 +490,9 @@ public:
+
+ const std::string getTeleportSourceSLURL() const { return mTeleportSourceSLURL; }
+
++ void sendChat(const std::string &text, S32 channel = 0, EChatType type = CHAT_TYPE_NORMAL, bool animate = false);
++ void sendChat(const std::wstring &text, S32 channel = 0, EChatType type = CHAT_TYPE_NORMAL, bool animate = false)
++ { sendChat(wstring_to_utf8str(text), channel, type, animate); }
+
+ // Setting the ability for this avatar to proxy for another avatar.
+ //static void processAddModifyAbility(LLMessageSystem* msg, void**);
+diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
+index 09d1141..bdd0def 100644
+--- a/indra/newview/llappviewer.cpp
++++ b/indra/newview/llappviewer.cpp
+@@ -33,6 +33,7 @@
+ #include "llviewerprecompiledheaders.h"
+ #include "llappviewer.h"
+ #include "llprimitive.h"
++#include "llfloateravatarlist.h"
+
+ #include "llversionviewer.h"
+ #include "llfeaturemanager.h"
+@@ -3166,6 +3167,9 @@ void LLAppViewer::idle()
+ }
+ gFrameStats.addFrameData();
+ }
++
++ // Update avatar list
++ gFloaterAvatarList->updateAvatarList();
+
+ if (!gDisconnected)
+ {
+diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp
+index 84ebf98..1678bc3 100644
+--- a/indra/newview/llchatbar.cpp
++++ b/indra/newview/llchatbar.cpp
+@@ -585,39 +585,7 @@ void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL
+ utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1);
+ }
+
+- // Don't animate for chats people can't hear (chat to scripts)
+- if (animate && (channel == 0))
+- {
+- if (type == CHAT_TYPE_WHISPER)
+- {
+- lldebugs << "You whisper " << utf8_text << llendl;
+- gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START);
+- }
+- else if (type == CHAT_TYPE_NORMAL)
+- {
+- lldebugs << "You say " << utf8_text << llendl;
+- gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START);
+- }
+- else if (type == CHAT_TYPE_SHOUT)
+- {
+- lldebugs << "You shout " << utf8_text << llendl;
+- gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START);
+- }
+- else
+- {
+- llinfos << "send_chat_from_viewer() - invalid volume" << llendl;
+- return;
+- }
+- }
+- else
+- {
+- if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP)
+- {
+- lldebugs << "Channel chat: " << utf8_text << llendl;
+- }
+- }
+-
+- send_chat_from_viewer(utf8_out_text, type, channel);
++ gAgent.sendChat(utf8_out_text, channel, type, animate);
+ }
+
+ void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel)
+diff --git a/indra/newview/llfloateravatarlist.cpp b/indra/newview/llfloateravatarlist.cpp
+new file mode 100644
+index 0000000..e1656eb
+--- /dev/null
++++ b/indra/newview/llfloateravatarlist.cpp
+@@ -0,0 +1,2187 @@
++/**
++ * @file llfloatermute.cpp
++ * @brief Container for mute list
++ *
++ * @author Dale Glass <dale at daleglass.net>, (C) 2007
++ */
++
++#include "llviewerprecompiledheaders.h" // must be first include
++
++#include "llavatarconstants.h"
++#include "llfloateravatarlist.h"
++
++#include "lluictrlfactory.h" // builds floaters from XML
++#include "llviewerwindow.h"
++#include "llscrolllistctrl.h"
++
++#include "llvoavatar.h"
++#include "llimview.h"
++#include "llfloateravatarinfo.h"
++#include "llregionflags.h"
++#include "llfloaterreporter.h"
++#include "llagent.h"
++#include "llviewerregion.h"
++#include "lltracker.h"
++#include "llviewercontrol.h"
++#include "llviewerstats.h"
++#include "llerror.h"
++#include "llchat.h"
++#include "llviewermessage.h"
++#include "llweb.h"
++#include "llviewerobjectlist.h"
++#include "llmutelist.h"
++#include "llviewerimagelist.h"
++#include "llworld.h"
++#include "llcachename.h"
++#include "llviewercamera.h"
++
++#include <time.h>
++#include <string.h>
++
++#include <map>
++
++
++// Timeouts
++/**
++ * @brief How long to keep showing an activity, in seconds
++ */
++const F32 ACTIVITY_TIMEOUT = 1.0f;
++
++
++/**
++ * @brief How many seconds to wait between data requests
++ *
++ * This is intended to avoid flooding the server with requests
++ */
++const F32 MIN_REQUEST_INTERVAL = 1.0f;
++
++/**
++ * @brief How long to wait for a request to arrive during the first try in seconds
++ */
++const F32 FIRST_REQUEST_TIMEOUT = 16.0f;
++
++/**
++ * @brief Delay is doubled on each attempt. This is as high as it'll go
++ */
++const F32 MAX_REQUEST_TIMEOUT = 2048.0f;
++
++/**
++ * How long to wait for a request to arrive before assuming failure
++ * and showing the failure icon in the list. This is just for the user's
++ * information, if a reply arrives after this interval we'll accept it anyway.
++ */
++const F32 REQUEST_FAIL_TIMEOUT = 15.0f;
++
++/**
++ * How long to keep people who are gone in the list. After this time is reached,
++ * they're not shown in the list anymore, but still kept in memory until
++ * CLEANUP_TIMEOUT is reached.
++ */
++const F32 DEAD_KEEP_TIME = 10.0f;
++
++/**
++ * @brief How long to keep entries around before removing them.
++ *
++ * @note Longer term, data like birth and payment info should be cached on disk.
++ */
++const F32 CLEANUP_TIMEOUT = 3600.0f;
++
++
++/**
++ * @brief TrustNet channel
++ * This is fixed in the adapter script.
++ */
++const S32 TRUSTNET_CHANNEL = 0x44470002;
++
++
++extern U32 gFrameCount;
++
++
++LLAvListTrustNetScore::LLAvListTrustNetScore(std::string type, F32 score)
++{
++ Score = score;
++ Type = type;
++}
++
++LLAvatarInfo::LLAvatarInfo()
++{
++}
++
++LLAvatarInfo::LLAvatarInfo(PAYMENT_TYPE payment, ACCOUNT_TYPE account, struct tm birth)
++{
++ Payment = payment;
++ Account = account;
++ BirthDate = birth;
++}
++
++S32 LLAvatarInfo::getAge()
++{
++ time_t birth = mktime(&BirthDate);
++ time_t now = time(NULL);
++ return(S32)(difftime(now,birth) / (60*60*24));
++}
++
++void LLAvatarListEntry::setPosition(LLVector3d position)
++{
++ if ( mPosition != position )
++ {
++ setActivity(ACTIVITY_MOVING);
++ }
++
++ mPosition = position;
++ mFrame = gFrameCount;
++ mUpdateTimer.start();
++}
++
++LLVector3d LLAvatarListEntry::getPosition()
++{
++ return mPosition;
++}
++
++U32 LLAvatarListEntry::getEntryAgeFrames()
++{
++ return (gFrameCount - mFrame);
++}
++
++F32 LLAvatarListEntry::getEntryEnteredSeconds()
++{
++ return mEnteredTimer.getElapsedTimeF32();
++}
++
++F32 LLAvatarListEntry::getEntryAgeSeconds()
++{
++ return mUpdateTimer.getElapsedTimeF32();
++}
++
++void LLAvatarListEntry::setName(std::string name)
++{
++ if ( name.empty() || (name.compare(" ") == 0))
++ {
++ llwarns << "Trying to set empty name" << llendl;
++ }
++ mName = name;
++}
++
++std::string LLAvatarListEntry::getName()
++{
++ return mName;
++}
++
++LLUUID LLAvatarListEntry::getID()
++{
++ return mID;
++}
++
++void LLAvatarListEntry::setID(LLUUID id)
++{
++ if ( id.isNull() )
++ {
++ llwarns << "Trying to set null id" << llendl;
++ }
++ mID = id;
++}
++
++BOOL LLAvatarListEntry::getIsLinden()
++{
++ // Are there any employees that are not a Linden?
++ // I suppose this is a bit redundant.
++ return ( mIsLinden || ( mAvatarInfo.getValue().Account == ACCOUNT_EMPLOYEE ) );
++}
++
++void LLAvatarListEntry::setAccountCustomTitle(std::string &title)
++{
++ mAccountTitle = title;
++ mAvatarInfo.getValue().Account = ACCOUNT_CUSTOM;
++}
++
++std::string LLAvatarListEntry::getAccountCustomTitle()
++{
++ return mAccountTitle;
++}
++
++
++
++void LLAvatarListEntry::setActivity(ACTIVITY_TYPE activity)
++{
++ if ( activity >= mActivityType || mActivityTimer.getElapsedTimeF32() > ACTIVITY_TIMEOUT )
++ {
++ mActivityType = activity;
++ mActivityTimer.start();
++ }
++}
++
++ACTIVITY_TYPE LLAvatarListEntry::getActivity()
++{
++ if ( mActivityTimer.getElapsedTimeF32() > ACTIVITY_TIMEOUT )
++ {
++ mActivityType = ACTIVITY_NONE;
++ }
++
++ return mActivityType;
++}
++
++void LLAvatarListEntry::toggleMark()
++{
++ mMarked = !mMarked;
++}
++
++BOOL LLAvatarListEntry::isMarked()
++{
++ return mMarked;
++}
++
++BOOL LLAvatarListEntry::isDead()
++{
++ return getEntryAgeSeconds() > DEAD_KEEP_TIME;
++}
++
++// Avatar list is global
++LLFloaterAvatarList* gFloaterAvatarList = NULL;
++
++
++
++
++LLFloaterAvatarList::LLFloaterAvatarList() : LLFloater("avatar list")
++{
++
++ // Default values
++ mTracking = FALSE;
++ mTrackByLocation = FALSE;
++ mARLastFrame = 0;
++
++ // Create interface from XML
++ LLUICtrlFactory::getInstance()->buildFloater(this, "floater_avatar_scanner.xml");
++
++ // Floater starts hidden
++ setVisible(FALSE);
++
++ // Set callbacks
++ //childSetAction("refresh_btn", onClickRefresh, this);
++ childSetAction("profile_btn", onClickProfile, this);
++ childSetAction("im_btn", onClickIM, this);
++ childSetAction("track_btn", onClickTrack, this);
++ childSetAction("mark_btn", onClickMark, this);
++
++ childSetAction("gowarn_btn", onClickGohomerWarn, this);
++ childSetAction("goeject_btn", onClickGohomerEject, this);
++ childSetAction("goaway_btn", onClickGohomerSendAway, this);
++ childSetAction("gohome_btn", onClickGohomerSendHome, this);
++ childSetAction("gohomeoff_btn", onClickGohomerOff, this);
++ childSetAction("gokey_btn", onClickGohomerSendHomeByKey, this);
++
++ childSetAction("prev_in_list_btn", onClickPrevInList, this);
++ childSetAction("next_in_list_btn", onClickNextInList, this);
++ childSetAction("prev_marked_btn", onClickPrevMarked, this);
++ childSetAction("next_marked_btn", onClickNextMarked, this);
++
++ childSetAction("get_key_btn", onClickGetKey, this);
++
++ childSetAction("tn_rate_btn", onClickTrustNetRate, this);
++ childSetAction("tn_explain_btn", onClickTrustNetExplain, this);
++ childSetAction("tn_website_btn", onClickTrustNetWebsite, this);
++ childSetAction("tn_password_btn", onClickTrustNetGetPassword, this);
++ childSetAction("tn_renew_btn", onClickTrustNetRenew, this);
++
++ childSetAction("freeze_btn", onClickFreeze, this);
++ childSetAction("eject_btn", onClickEject, this);
++// childSetAction("ban_btn", onClickBan, this);
++// childSetAction("unban_btn", onClickUnban, this);
++ childSetAction("mute_btn", onClickMute, this);
++// childSetAction("unmute_btn", onClickUnmute, this);
++ childSetAction("ar_btn", onClickAR, this);
++ childSetAction("teleport_btn", onClickTeleport, this);
++ childSetAction("estate_eject_btn", onClickEjectFromEstate, this);
++
++ setDefaultBtn("refresh_btn");
++
++ // Get a pointer to the scroll list from the interface
++ mAvatarList = getChild<LLScrollListCtrl>("avatar_list");
++
++ mAvatarList->setCallbackUserData(this);
++ mAvatarList->setDoubleClickCallback(onDoubleClick);
++ mAvatarList->sortByColumn("distance", TRUE);
++ mDataRequestTimer.start();
++ refreshAvatarList();
++
++ LLMessageSystem *msg = gMessageSystem;
++ msg->addHandlerFunc("AvatarPropertiesReply", processAvatarPropertiesReply);
++}
++
++LLFloaterAvatarList::~LLFloaterAvatarList()
++{
++ LLMessageSystem *msg = gMessageSystem;
++ if ( msg )
++ {
++ msg->delHandlerFunc("AvatarPropertiesReply", processAvatarPropertiesReply);
++ }
++ std::map< LLUUID, LLPointer< LLHUDObject > >::iterator it = mHudObjectMap.begin();
++ for ( ; it != mHudObjectMap.end(); ++it )
++ { // clean up list
++ it->second->markDead();
++ }
++
++}
++
++
++void LLFloaterAvatarList::show()
++{
++ // Make sure we make a noise.
++ open();
++}
++
++//static
++void LLFloaterAvatarList::toggle(void*) {
++ if (!gFloaterAvatarList) {
++ llinfos << "No avatar list!" << llendl;
++ return;
++ }
++
++ if (gFloaterAvatarList->getVisible())
++ {
++ gFloaterAvatarList->close();
++ }
++ else
++ {
++ gFloaterAvatarList->show();
++ }
++}
++
++//static
++BOOL LLFloaterAvatarList::visible(void*)
++{
++ return (gFloaterAvatarList && gFloaterAvatarList->getVisible());
++}
++
++void LLFloaterAvatarList::updateFromCoarse()
++{
++ /*
++ * Walk through remaining list of coarse update avatars in all known regions
++ * this will not give us an accurate height since it's mod 2048 and least possible
++ * increment is 4 meter. Coarse Update information is accurate instantly while
++ * the object list is filled one by one.
++ *
++ * This also works for neighbour sims which makes it really handy :)
++ */
++
++ // first wipe the list clean from coarse entries
++ std::map<LLUUID, LLAvatarListEntry>::iterator iter;
++ for(iter = mAvatars.begin(); iter != mAvatars.end();)
++ {
++ LLAvatarListEntry entry = iter->second;
++ if ( entry.getIsCoarse() )
++ {
++ mAvatars.erase( iter++ );
++ }
++ else
++ {
++ ++iter;
++ }
++ }
++
++ LLWorld::region_list_t regions = LLWorld::getInstance()->getRegionList();
++ LLWorld::region_list_t::const_iterator it = regions.begin();
++
++ for ( ; it != regions.end(); ++it )
++ {
++ LLViewerRegion const *region = *it;
++ if ( !region )
++ {
++ llwarns << "null region while parsing region list" << llendl;
++ continue;
++ }
++
++ for (int idx = 0; idx < region->mMapAvatarIDs.count(); ++idx)
++ {
++ LLUUID avid = region->mMapAvatarIDs.get( idx );
++
++ if ( avid.isNull() )
++ {
++ continue;
++ }
++
++ // we need to accomodate for avatars that are stuck in the
++ // object list while still accurately received in the coarse
++ // location list
++ U32 modpos = region->mMapAvatars.get( idx );
++ LLVector3 localpos;
++ localpos[0] = (modpos >> 16) & 0xff;
++ localpos[1] = (modpos >> 8) & 0xff;
++ // scale z-position
++ localpos[2] = (modpos & 0xff) << 2;
++ LLVector3d position = region->getPosGlobalFromRegion( localpos );
++
++ if ( ( mAvatars.count( avid ) > 0 ) && ( !mAvatars[ avid ].isDead() ) )
++ {
++ // Avatar already in list but could be one of these "perpetual motion" avatars
++ // which would overlay the real coordinates, so we check for the distance disregarding
++ // the z axis
++ LLVector3d coarsepos = position;
++ coarsepos[2] = 0.0;
++ LLVector3d vopos = mAvatars[ avid ].mPosition;
++ vopos[2] = 0.0;
++ LLVector3d dist = coarsepos - vopos;
++ if ( dist.magVecSquared() > ( 50.0 * 50.0 ) )
++ {
++ // Avatar already in list, but position info is
++ // out of sync so use coarse info, we can safely overwrite
++ // the info here since we are called after the VOlist has
++ // already been parsed. The only issue is that this will now
++ // show the avatar as perpetually moving
++ mAvatars[ avid ].setPosition( coarsepos );
++ }
++ // Avatar already in list, active and
++ // close enough to coarse info, so skip
++ continue;
++ }
++
++ // Avatar not there yet, add it
++ std::string name;
++ BOOL isLinden = FALSE;
++ if ( !gCacheName->getFullName( avid, name ) )
++ {
++ continue; // wait for proper name
++ }
++ else
++ {
++ std::string first, last;
++ gCacheName->getName( avid, first, last );
++ if ( last == "Linden" )
++ {
++ isLinden = TRUE;
++ }
++ }
++
++ std::string regionname;
++ if ( gAgent.getRegion() && ( region->getName() != gAgent.getRegion()->getName() ) )
++ {
++ regionname = region->getName();
++ }
++
++ // add as coarse info
++ LLAvatarListEntry entry(avid, name, position, isLinden, TRUE, regionname);
++ mAvatars[avid] = entry;
++
++ //llinfos << "avatar list refresh from coarse: adding " << name << llendl;
++
++ }
++ }
++}
++
++
++void LLFloaterAvatarList::purgeAvatarHUDMap()
++{
++ std::map< LLUUID, LLPointer< LLHUDObject > >::iterator huditer = mHudObjectMap.begin();
++ while ( huditer != mHudObjectMap.end() )
++ {
++ if ( mAvatars.count( huditer->first ) == 0 )
++ {
++ huditer->second->markDead();
++ mHudObjectMap.erase( huditer++ );
++ }
++ else
++ {
++ ++huditer;
++ }
++ }
++}
++
++
++void LLFloaterAvatarList::updateAvatarList()
++{
++// LLVOAvatar *avatarp;
++
++ //llinfos << "avatar list refresh: updating map" << llendl;
++
++ // Check whether updates are enabled
++ LLCheckboxCtrl* check;
++ check = getChild<LLCheckBoxCtrl>("update_enabled_cb");
++
++ if ( !check->getValue() )
++ {
++ return;
++ }
++
++
++ /*
++ * Iterate over all the avatars known at the time
++ * NOTE: Is this the right way to do that? It does appear that LLVOAvatar::isInstances contains
++ * the list of avatars known to the client. This seems to do the task of tracking avatars without
++ * any additional requests.
++ *
++ * BUG: It looks like avatars sometimes get stuck in this list, and keep perpetually
++ * moving in the same direction. My current guess is that somewhere else the client
++ * doesn't notice an avatar disappeared, and keeps updating its position. This should
++ * be solved at the source of the problem.
++ */
++ for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
++ iter != LLCharacter::sInstances.end(); ++iter)
++ {
++ LLVOAvatar* avatarp = (LLVOAvatar*) *iter;
++
++ // Skip if avatar is dead(what's that?)
++ // or if the avatar is ourselves.
++ if (avatarp->isDead() || avatarp->isSelf())
++ {
++ continue;
++ }
++
++ // Get avatar data
++ LLVector3d position = gAgent.getPosGlobalFromAgent(avatarp->getCharacterPosition());
++ LLUUID avid = avatarp->getID();
++ std::string name = avatarp->getFullname();
++
++ // Apparently, sometimes the name comes out empty, with a " " name. This is because
++ // getFullname concatenates first and last name with a " " in the middle.
++ // This code will avoid adding a nameless entry to the list until it acquires a name.
++ if (name.empty() || (name.compare(" ") == 0))
++ {
++ llinfos << "Name empty for avatar " << avid << llendl;
++ continue;
++ }
++
++ if (avid.isNull())
++ {
++ llinfos << "Key empty for avatar " << name << llendl;
++ continue;
++ }
++
++ if ( ( mAvatars.count( avid ) > 0 ) && !mAvatars[avid].getIsCoarse() )
++ {
++ // Avatar already in list, update position
++ mAvatars[avid].setPosition(position);
++ }
++ else
++ {
++ // Avatar not there yet or only from coarse list, add it properly
++ BOOL isLinden = ( std::string( avatarp->getNVPair("LastName")->getString() ) == "Linden" );
++
++ LLAvatarListEntry entry(avid, name, position, isLinden);
++ mAvatars[avid] = entry;
++
++ sendAvatarPropertiesRequest(avid);
++ llinfos << "avatar list refresh: adding " << name << llendl;
++
++ }
++
++ }
++
++ updateFromCoarse();
++
++// llinfos << "avatar list refresh: done" << llendl;
++
++ expireAvatarList();
++ refreshAvatarList();
++
++ purgeAvatarHUDMap();
++
++ checkTrackingStatus();
++ processARQueue();
++}
++
++void LLFloaterAvatarList::processARQueue()
++{
++ if ( mARQueue.empty() ) return;
++
++ LLUUID avatar_id = mARQueue.front();
++
++ if ( 0 == mARLastFrame )
++ {
++ // Start of the process: Move the camera to the avatar. This happens gradually,
++ // so we'll give it a few frames
++ gAgent.lookAtObject(avatar_id, CAMERA_POSITION_OBJECT);
++ mARLastFrame = gFrameCount;
++ return;
++ }
++
++ if ( gFrameCount - mARLastFrame >= 10 )
++ {
++ // Camera should be in position, show AR screen now
++ LLFloaterReporter *report = LLFloaterReporter::showFromObject(avatar_id, false);
++ report->setMinimized(TRUE);
++
++ mARReporterQueue.push(report);
++
++ mARQueue.pop();
++ mARLastFrame = 0;
++
++ if ( mARQueue.empty() )
++ {
++ // Now that all reports are taken, open them.
++
++ while( !mARReporterQueue.empty() )
++ {
++ LLFloaterReporter *r = mARReporterQueue.front();
++ mARReporterQueue.pop();
++
++ r->open();
++ r->setMinimized(FALSE);
++ }
++ }
++ }
++}
++
++void LLFloaterAvatarList::expireAvatarList()
++{
++// llinfos << "avatar list: expiring" << llendl;
++ std::map<LLUUID, LLAvatarListEntry>::iterator iter;
++ std::queue<LLUUID> delete_queue;
++
++ for(iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
++ {
++ LLAvatarListEntry *ent = &iter->second;
++
++ if ( ent->getEntryAgeFrames() >= 2 )
++ {
++ ent->setActivity(ACTIVITY_DEAD);
++ }
++
++
++ if ( ent->getEntryAgeSeconds() > CLEANUP_TIMEOUT )
++ {
++ llinfos << "avatar list: expiring avatar " << ent->getName() << llendl;
++ LLUUID av_id = ent->getID();
++ delete_queue.push(av_id);
++ }
++ }
++
++ while(!delete_queue.empty())
++ {
++ mAvatars.erase(delete_queue.front());
++ if ( mHudObjectMap.count(delete_queue.front()) )
++ {
++ mHudObjectMap[delete_queue.front()]->markDead();
++ mHudObjectMap.erase(delete_queue.front());
++ }
++ delete_queue.pop();
++ }
++}
++
++/**
++ * Redraws the avatar list
++ * Only does anything if the avatar list is visible.
++ * @author Dale Glass
++ */
++void LLFloaterAvatarList::refreshAvatarList()
++{
++
++
++
++ // Don't update list when interface is hidden
++ if (!LLFloaterAvatarList::visible(NULL))
++ {
++ return;
++ }
++
++
++ LLCheckboxCtrl* fetch_data;
++ fetch_data = getChild<LLCheckBoxCtrl>("fetch_avdata_enabled_cb");
++
++ //BOOL db_enabled = gSavedSettings.getBOOL("DBEnabled");
++ //std::string db_avatar = gSavedPerAccountSettings.getString("DBAvatarName");
++ //if ( db_avatar.empty() )
++ //{
++ // db_enabled = FALSE;
++ //}
++
++
++
++ // We rebuild the list fully each time it's refreshed
++
++ // The assumption is that it's faster to refill it and sort than
++ // to rebuild the whole list.
++ LLDynamicArray<LLUUID> selected = mAvatarList->getSelectedIDs();
++ S32 scrollpos = mAvatarList->getScrollPos();
++
++ mAvatarList->deleteAllItems();
++
++ LLVector3d mypos = gAgent.getPositionGlobal();
++
++ unsigned int counter = 0;
++
++ std::map<LLUUID, LLAvatarListEntry>::iterator iter;
++ for(iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
++ {
++ LLSD element;
++ LLUUID av_id;
++
++
++ LLAvatarListEntry *ent = &iter->second;
++
++ // Skip if avatar hasn't been around
++ if ( ent->isDead() )
++ {
++ continue;
++ }
++
++ av_id = ent->getID();
++
++ // Get avatar name, position
++ LLAvatarInfo avinfo = ent->mAvatarInfo.getValue();
++ //LLAvListTrustNetScore avscore = ent->mTrustNetScore.getValue();
++
++ DATA_STATUS avinfo_status = ent->mAvatarInfo.getStatus();
++ //DATA_STATUS avscore_status = ent->mTrustNetScore.getStatus();
++
++ LLVector3d position = ent->getPosition();
++ LLVector3d delta = position - mypos;
++ F32 distance = (F32)delta.magVec();
++
++ std::string icon = "";
++
++ // HACK: Workaround for an apparent bug:
++ // sometimes avatar entries get stuck, and are registered
++ // by the client as perpetually moving in the same direction.
++ // this makes sure they get removed from the visible list eventually.
++ // for the coarse list this is not necessary since it is always accurate
++ if ( distance > 1024 && !ent->getIsCoarse() )
++ {
++ continue;
++ }
++
++ if ( av_id.isNull() )
++ {
++ llwarns << "Avatar with null key somehow got into the list!" << llendl;
++ continue;
++ }
++
++ counter++;
++
++ element["id"] = av_id;
++
++ element["columns"][LIST_AVATAR_ICON]["column"] = "avatar_icon";
++ element["columns"][LIST_AVATAR_ICON]["type"] = "text";
++ if ( !ent->isMarked() )
++ { // show counter if not marked
++ element["columns"][LIST_AVATAR_ICON]["value"] = llformat("%d", counter);
++ }
++ else
++ {
++ element["columns"][LIST_AVATAR_ICON]["type"] = "icon";
++ const LLUUID flag_blue("e39cbfe7-c4e7-3bad-5e5f-958082d55046");
++ element["columns"][LIST_AVATAR_ICON]["value"] = flag_blue.asString();
++ }
++
++
++ if ( ent->getIsLinden() )
++ {
++ element["columns"][LIST_AVATAR_NAME]["font-style"] = "BOLD";
++ }
++
++ if ( ent->getIsCoarse() )
++ {
++ element["columns"][LIST_AVATAR_NAME]["color"] = LLColor4::grey4.getValue();
++ }
++
++ if ( ent->isFocused() )
++ {
++ element["columns"][LIST_AVATAR_NAME]["color"] = LLColor4::cyan.getValue();
++ }
++
++ //element["columns"][LIST_AVATAR_NAME]["font-color"] = getAvatarColor(ent, distance).getValue();
++ element["columns"][LIST_AVATAR_NAME]["column"] = "avatar_name";
++ element["columns"][LIST_AVATAR_NAME]["type"] = "text";
++ std::string agentname = ent->getName();
++ if ( !ent->getIsSameRegion() )
++ {
++ agentname += " (" + ent->getRegionName() + ")";
++ }
++ element["columns"][LIST_AVATAR_NAME]["value"] = agentname.c_str();
++
++ char temp[32];
++ snprintf(temp, sizeof(temp), "%.2f", distance);
++
++ element["columns"][LIST_DISTANCE]["column"] = "distance";
++ element["columns"][LIST_DISTANCE]["type"] = "text";
++ element["columns"][LIST_DISTANCE]["value"] = temp;
++ element["columns"][LIST_DISTANCE]["color"] = getAvatarColor(ent, distance, CT_DISTANCE).getValue();
++
++
++ if ( avinfo_status == DATA_RETRIEVED )
++ {
++ element["columns"][LIST_AGE]["column"] = "age";
++ element["columns"][LIST_AGE]["type"] = "text";
++ element["columns"][LIST_AGE]["value"] = avinfo.getAge();
++ element["columns"][LIST_AGE]["color"] = getAvatarColor(ent, distance, CT_AGE).getValue();
++ }
++
++ const LLUUID info_error("bbda234c-c76e-8617-0a32-46cc15c5ec42");
++ const LLUUID info_fetching("1468fae4-2f47-6e75-d39f-3ccbd443d31c");
++ const LLUUID info_unknown("0f2d532a-1fc8-01bb-eed3-ef60e7943d1e");
++ const LLUUID payment_info_charter("07bef5d9-31b2-4cc5-999e-c2cd8b5d3a69");
++ const LLUUID payment_info_filled("9d61c4d5-e8f6-78ec-a64f-490e3a4c03d5");
++ const LLUUID payment_info_used("49ac7ef9-caaa-750a-6ec1-51358f0a1672");
++
++ /*
++ element["columns"][LIST_SCORE]["column"] = "score";
++ element["columns"][LIST_SCORE]["type"] = "text";
++
++ icon = "";
++ switch(avscore_status)
++ {
++ case DATA_UNKNOWN:
++ icon = info_unknown.asString();
++ break;
++ case DATA_REQUESTING:
++ icon = info_fetching.asString();
++ break;
++ case DATA_ERROR:
++ icon = info_error.asString();
++ case DATA_RETRIEVED:
++ element["columns"][LIST_SCORE]["value"] = avscore.Score;
++ element["columns"][LIST_SCORE]["color"] = getAvatarColor(ent, distance, CT_SCORE).getValue();
++ break;
++ }
++
++ if (!icon.empty() )
++ {
++ element["columns"][LIST_SCORE].erase("color");
++ element["columns"][LIST_SCORE]["type"] = "icon";
++ element["columns"][LIST_SCORE]["value"] = icon;
++ }*/
++
++
++ // Get an icon for the payment data
++ // These should be replaced with something proper instead of reusing whatever
++ // LL-provided images happened to fit
++ icon = "";
++
++ switch(avinfo_status)
++ {
++ case DATA_UNKNOWN:
++ icon = info_unknown.asString();
++ break;
++ case DATA_REQUESTING:
++ icon = info_fetching.asString();
++ break;
++ case DATA_ERROR:
++ icon = info_error.asString();
++ break;
++ case DATA_RETRIEVED:
++ switch(avinfo.Payment)
++ {
++ case PAYMENT_NONE:
++ break;
++ case PAYMENT_ON_FILE:
++ icon = payment_info_filled.asString();
++ break;
++ case PAYMENT_USED:
++ icon = payment_info_used.asString();
++ break;
++ case PAYMENT_LINDEN:
++ // confusingly named icon, maybe use something else
++ icon = "icon_top_pick.tga";
++ break;
++ }
++ break;
++ }
++
++ element["columns"][LIST_PAYMENT]["column"] = "payment_data";
++ element["columns"][LIST_PAYMENT]["type"] = "text";
++
++ // TODO: Add icon for "unknown" status
++ //if ( PAYMENT_NONE != avinfo.Payment && DATA_UNKNOWN != avinfo_status )
++ if ( !icon.empty() )
++ {
++ element["columns"][LIST_PAYMENT].erase("color");
++ element["columns"][LIST_PAYMENT]["type"] = "icon";
++ element["columns"][LIST_PAYMENT]["value"] = icon;
++ //llinfos << "Payment icon: " << payment_icon << llendl;
++ }
++
++ const LLUUID avatar_gone("db4592d5-c8a5-9336-019c-fcbd282d5f33");
++ const LLUUID avatar_new("33d4b23e-a29c-ac03-f7f6-c2fa197b13fe");
++ const LLUUID avatar_typing("6f083c3c-1e88-d184-6add-95402b3e108f");
++ /*<avatar_sound.tga value = "439836e2-29f5-c12f-71d4-aa59283296e1"/>
++ <flag_blue.tga value="e39cbfe7-c4e7-3bad-5e5f-958082d55046"/>
++ <flag_green.tga value="78952758-1bef-f968-d382-b39094f85aa1"/>
++ <flag_orange.tga value="c72ca7d9-42cd-02f1-ce32-ca1ea5d1c25d"/>
++ <flag_pink.tga value="a3419a89-b8d9-293c-693e-12982e574304"/>
++ <flag_purple.tga value="7982fbf8-457a-77ce-61e6-b3c7d9500d2f"/>
++ <flag_red.tga value="11ba32bf-44fe-666e-073b-00768785b4d0"/>
++ <flag_yellow.tga value="98a5a29e-e933-eeed-bdd9-4d461f557d34"/>*/
++
++ ACTIVITY_TYPE activity = ent->getActivity();
++ icon = "";
++ switch( activity )
++ {
++ case ACTIVITY_NONE:
++ break;
++ case ACTIVITY_MOVING:
++ icon = "inv_item_animation.tga";
++ break;
++ case ACTIVITY_GESTURING:
++ icon = "inv_item_gesture.tga";
++ break;
++ case ACTIVITY_SOUND:
++ icon = "inv_item_sound.tga";
++ break;
++ case ACTIVITY_REZZING:
++ icon = "ff_edit_theirs.tga";
++ break;
++ case ACTIVITY_PARTICLES:
++ // TODO: Replace with something better
++ icon = "account_id_green.tga";
++ break;
++ case ACTIVITY_NEW:
++ icon = avatar_new.asString();
++ break;
++ case ACTIVITY_TYPING:
++ icon = avatar_typing.asString();
++ break;
++ case ACTIVITY_DEAD:
++ // TODO: Replace, icon is quite inappropiate
++ icon = avatar_gone.asString();
++ break;
++ }
++
++ element["columns"][LIST_ACTIVITY]["column"] = "activity";
++ element["columns"][LIST_ACTIVITY]["type"] = "text";
++
++ if (!icon.empty() )
++ {
++ element["columns"][LIST_ACTIVITY]["type"] = "icon";
++ element["columns"][LIST_ACTIVITY]["value"] = icon;
++ //llinfos << "Activity icon: " << activity_icon << llendl;
++ }
++
++ char tempentered[32];
++ F32 entered = ent->getEntryEnteredSeconds();
++ snprintf(tempentered, sizeof(tempentered), "%u", (unsigned int)(entered/60.0));
++ element["columns"][LIST_ENTERED]["column"] = "entered";
++ element["columns"][LIST_ENTERED]["type"] = "text";
++ element["columns"][LIST_ENTERED]["value"] = tempentered;
++ element["columns"][LIST_ENTERED]["color"] = getAvatarColor(ent, distance, CT_ENTERED).getValue();
++
++ // Add to list
++ mAvatarList->addElement(element, ADD_BOTTOM);
++
++ // Request data only if fetching avatar data is enabled
++ if ( fetch_data->getValue() && ent->mAvatarInfo.requestIfNeeded() )
++ {
++ sendAvatarPropertiesRequest(av_id);
++ llinfos << "Data for avatar " << ent->getName() << " didn't arrive yet, retrying" << llendl;
++ }
++
++ /*if ( ent->mTrustNetScore.requestIfNeeded() )
++ {
++ requestTrustNetScore(av_id, ent->getName(), "behavior");
++ llinfos << "Requesting TrustNet score for " << ent->getName() << llendl;
++ }*/
++
++ //if ( db_enabled && ent->mMiscInfo.requestIfNeeded() )
++ //{
++ // requestMiscInfo(av_id, ent->getName());
++ // llinfos << "Requesting misc info for " << ent->getName() << llendl;
++ //}
++ }
++
++ // finish
++ mAvatarList->sort();
++ mAvatarList->selectMultiple(selected);
++ mAvatarList->setScrollPos(scrollpos);
++
++// llinfos << "avatar list refresh: done" << llendl;
++
++}
++
++// static
++void LLFloaterAvatarList::onClickIM(void* userdata)
++{
++ //llinfos << "LLFloaterFriends::onClickIM()" << llendl;
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++
++ LLDynamicArray<LLUUID> ids = avlist->mAvatarList->getSelectedIDs();
++ if(ids.size() > 0)
++ {
++ if(ids.size() == 1)
++ {
++ // Single avatar
++ LLUUID agent_id = ids[0];
++
++ char buffer[MAX_STRING];
++ snprintf(buffer, MAX_STRING, "%s", avlist->mAvatars[agent_id].getName().c_str());
++ gIMMgr->setFloaterOpen(TRUE);
++ gIMMgr->addSession(
++ buffer,
++ IM_NOTHING_SPECIAL,
++ agent_id);
++ }
++ else
++ {
++ // Group IM
++ LLUUID session_id;
++ session_id.generate();
++ gIMMgr->setFloaterOpen(TRUE);
++ gIMMgr->addSession("Avatars Conference", IM_SESSION_CONFERENCE_START, ids[0], ids);
++ }
++ }
++}
++
++void LLFloaterAvatarList::onClickTrack(void *userdata)
++{
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++
++ LLScrollListItem *item = avlist->mAvatarList->getFirstSelected();
++ if (!item) return;
++
++ LLUUID agent_id = item->getUUID();
++
++ if ( avlist->mTracking && avlist->mTrackedAvatar == agent_id ) {
++ LLTracker::stopTracking(NULL);
++ avlist->mTracking = FALSE;
++ }
++ else
++ {
++ avlist->mTracking = TRUE;
++ avlist->mTrackByLocation = FALSE;
++ avlist->mTrackedAvatar = agent_id;
++ LLTracker::trackAvatar(agent_id, avlist->mAvatars[agent_id].getName());
++ }
++}
++
++void LLFloaterAvatarList::sendAvatarPropertiesRequest(LLUUID avid)
++{
++
++
++ lldebugs << "LLPanelAvatar::sendAvatarPropertiesRequest()" << llendl;
++ LLMessageSystem *msg = gMessageSystem;
++
++ msg->newMessageFast(_PREHASH_AvatarPropertiesRequest);
++ msg->nextBlockFast( _PREHASH_AgentData);
++ msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() );
++ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
++ msg->addUUIDFast( _PREHASH_AvatarID, avid);
++ gAgent.sendReliableMessage();
++
++ mAvatars[avid].mAvatarInfo.requestStarted();
++}
++
++// static
++void LLFloaterAvatarList::processAvatarPropertiesReply(LLMessageSystem *msg, void**)
++{
++
++
++ LLFloaterAvatarList* self = NULL;
++ LLAvatarInfo avinfo;
++
++ BOOL identified = FALSE;
++ BOOL transacted = FALSE;
++
++ LLUUID agent_id; // your id
++ LLUUID avatar_id; // target of this panel
++ U32 flags = 0x0;
++ char born_on[DB_BORN_BUF_SIZE];
++ S32 charter_member_size = 0;
++
++ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
++ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AvatarID, avatar_id );
++
++
++ self = gFloaterAvatarList;
++
++ // Verify that the avatar is in the list, if not, ignore.
++ if ( self->mAvatarList->getItemIndex(avatar_id) < 0 )
++ {
++ return;
++ }
++
++ LLAvatarListEntry *entry = &self->mAvatars[avatar_id];
++
++ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_BornOn, DB_BORN_BUF_SIZE, born_on);
++ msg->getU32Fast(_PREHASH_PropertiesData, _PREHASH_Flags, flags);
++
++ identified = (flags & AVATAR_IDENTIFIED);
++ transacted = (flags & AVATAR_TRANSACTED);
++
++ // What's this?
++ // Let's see if I understand correctly: CharterMember property is dual purpose:
++ // it either contains a number indicating an account type (usual value), or
++ // it contains a string with a custom title. Probably that's where Philip Linden's
++ // "El Presidente" title comes from. Heh.
++ U8 caption_index = 0;
++ std::string caption_text;
++ charter_member_size = msg->getSize("PropertiesData", "CharterMember");
++
++ if(1 == charter_member_size)
++ {
++ msg->getBinaryData("PropertiesData", "CharterMember", &caption_index, 1);
++ }
++ else if(1 < charter_member_size)
++ {
++ char caption[MAX_STRING];
++ msg->getString("PropertiesData", "CharterMember", MAX_STRING, caption);
++
++ caption_text = caption;
++ entry->setAccountCustomTitle(caption_text);
++ }
++
++
++ if(caption_text.empty())
++ {
++
++ const enum ACCOUNT_TYPE ACCT_TYPE[] = {
++ ACCOUNT_RESIDENT,
++ ACCOUNT_TRIAL,
++ ACCOUNT_CHARTER_MEMBER,
++ ACCOUNT_EMPLOYEE
++ };
++
++ //enum ACCOUNT_TYPE acct =
++ avinfo.Account = ACCT_TYPE[llclamp(caption_index, (U8)0, (U8)(sizeof(ACCT_TYPE)/sizeof(ACCT_TYPE[0])-1))];
++ //entry->setAccountType(acct);
++
++
++ if ( avinfo.Account != ACCOUNT_EMPLOYEE )
++ {
++ if ( transacted )
++ {
++ avinfo.Payment = PAYMENT_USED;
++ }
++ else if ( identified )
++ {
++ avinfo.Payment = PAYMENT_ON_FILE;
++ }
++ else
++ {
++ avinfo.Payment = PAYMENT_NONE;
++ }
++ }
++ else
++ {
++ avinfo.Payment = PAYMENT_LINDEN;
++ }
++ }
++
++ // Structure must be zeroed to have sane results, as we
++ // have an incomplete string for input
++ memset(&avinfo.BirthDate, 0, sizeof(avinfo.BirthDate));
++
++ int num_read = sscanf(born_on, "%d/%d/%d", &avinfo.BirthDate.tm_mon,
++ &avinfo.BirthDate.tm_mday,
++ &avinfo.BirthDate.tm_year);
++
++ if ( num_read == 3 && avinfo.BirthDate.tm_mon <= 12 )
++ {
++ avinfo.BirthDate.tm_year -= 1900;
++ avinfo.BirthDate.tm_mon--;
++ }
++ else
++ {
++ // Zero again to remove any partially read data
++ memset(&avinfo.BirthDate, 0, sizeof(avinfo.BirthDate));
++ llwarns << "Error parsing birth date: " << born_on << llendl;
++ }
++
++ entry->mAvatarInfo.setValue(avinfo);
++}
++
++void LLFloaterAvatarList::checkTrackingStatus()
++{
++
++ if ( mTracking && LLTracker::getTrackedPositionGlobal().isExactlyZero() )
++ {
++ // trying to track an avatar, but tracker stopped tracking
++ if ( mAvatars.count( mTrackedAvatar ) > 0 && !mTrackByLocation )
++ {
++ llinfos << "Switching to location-based tracking" << llendl;
++ mTrackByLocation = TRUE;
++ }
++ else
++ {
++ // not found
++ llinfos << "Stopping tracking avatar, server-side didn't work, and not in list anymore." << llendl;
++ LLTracker::stopTracking(NULL);
++ mTracking = FALSE;
++ }
++ }
++
++ if ( mTracking && mTrackByLocation )
++ {
++ std::string name = mAvatars[mTrackedAvatar].getName();
++ std::string tooltip = "Tracking last known position";
++ name += " (near)";
++ LLTracker::trackLocation(mAvatars[mTrackedAvatar].getPosition(), name, tooltip);
++ }
++
++ //llinfos << "Tracking position: " << LLTracker::getTrackedPositionGlobal() << llendl;
++
++}
++
++
++BOOL LLFloaterAvatarList::avatarIsInList(LLUUID avatar)
++{
++ return ( mAvatars.count( avatar ) > 0 );
++}
++
++LLAvatarListEntry * LLFloaterAvatarList::getAvatarEntry(LLUUID avatar)
++{
++ if ( avatar.isNull() )
++ {
++ return NULL;
++ }
++
++ std::map<LLUUID, LLAvatarListEntry>::iterator iter;
++
++ iter = mAvatars.find(avatar);
++ if ( iter == mAvatars.end() )
++ {
++ return NULL;
++ }
++
++ return &iter->second;
++
++ //if ( mAvatars.count( avatar ) < 0 )
++ //{
++ //return NULL;
++ //}
++
++ //return &mAvatars[avatar];
++}
++
++void LLFloaterAvatarList::speakText(S32 channel, EChatType type, std::string text)
++{
++ LLMessageSystem* msg = gMessageSystem;
++
++ msg->newMessageFast(_PREHASH_ChatFromViewer);
++ msg->nextBlockFast(_PREHASH_AgentData);
++ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
++ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
++ msg->nextBlockFast(_PREHASH_ChatData);
++ msg->addStringFast(_PREHASH_Message, text);
++ msg->addU8Fast(_PREHASH_Type, type);
++ msg->addS32("Channel", channel);
++
++ gAgent.sendReliableMessage();
++
++ LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT);
++}
++
++
++void LLFloaterAvatarList::requestTrustNetScore(LLUUID avatar, const std::string name, const std::string type)
++{
++ char *temp = new char[UUID_STR_LENGTH];
++ avatar.toString(temp);
++
++ std::string text = "GetScore|" + name + "|" + temp + "|" + type;
++ speakText(TRUSTNET_CHANNEL, CHAT_TYPE_WHISPER, text);
++}
++
++//static
++void LLFloaterAvatarList::replaceVars(std::string &str, LLUUID avatar, const std::string& name)
++{
++ char *temp = new char[UUID_STR_LENGTH];
++ avatar.toString(temp);
++
++ std::string vars[][2] = {
++ {"$NAME", name},
++ {"$KEY", temp},
++ };
++
++ BOOL replaced = TRUE;
++
++ while( replaced )
++ {
++ replaced = FALSE;
++ for(U32 i=0;i<sizeof(vars)/sizeof(vars[0]);i++)
++ {
++ std::string::size_type pos = str.find(vars[i][0]);
++ if ( pos != std::string::npos )
++ {
++ str.replace(pos, vars[i][0].size(), vars[i][1]);
++ replaced = TRUE;
++ }
++ }
++ }
++
++}
++
++void LLFloaterAvatarList::requestMiscInfo(LLUUID avatar, const std::string name)
++{
++ //LLUUID db_av_key;
++
++ //std::string message = gSavedPerAccountSettings.getString("DBSendPattern");
++ //std::string db_av_name = gSavedPerAccountSettings.getString("DBAvatarName");
++ //db_av_key.set(gSavedPerAccountSettings.getString("DBAvatarKey"));
++
++
++ //llinfos << "Requesting info " << llendl;
++ //replaceVars(message, avatar, name);
++
++ //llinfos << "Request string: " << message << llendl;
++ //send_simple_im(db_av_key, message.c_str());
++ }
++
++//static
++BOOL LLFloaterAvatarList::handleIM(LLUUID from_id, const std::string message)
++{
++ LLUUID db_av_key;
++ //db_av_key.set(gSavedPerAccountSettings.getString("DBAvatarKey"));
++
++ if ( db_av_key == from_id )
++ {
++ std::map<LLUUID, LLAvatarListEntry>::iterator iter;
++
++ for(iter = gFloaterAvatarList->mAvatars.begin(); iter != gFloaterAvatarList->mAvatars.end(); iter++)
++ {
++ LLAvatarListEntry *ent = &iter->second;
++
++ // Check if the key, or the name are found in the reply.
++ // Name is only accepted if it's in the beginning of the message.
++ if ( message.find(ent->getID().asString()) != std::string::npos
++ || message.find(ent->getName().c_str()) == 0 )
++ {
++ LLMiscDBInfo info;
++ info.data = message;
++
++ llinfos << "Database reply arrived for avatar " << ent->getName() << llendl;
++ ent->mMiscInfo.setValue(info);
++ }
++ }
++
++ return TRUE;
++ }
++ return FALSE;
++}
++
++//static
++void LLFloaterAvatarList::processTrustNetReply(char *reply)
++{
++ char *tokens[10];
++ char *tmp = &reply[0];
++ U32 count = 0;
++
++ llinfos << "TrustNet reply: " << reply << llendl;
++
++
++ // Split into tokens
++ while( (NULL != (tmp = strtok(tmp, "|"))) && count < (sizeof(tokens)/sizeof(tokens[0])) )
++ {
++ tokens[count++] = tmp;
++ llinfos << "token: " << tmp << llendl;
++ tmp = NULL;
++ }
++
++ llinfos << "Got " << count << " tokens" << llendl;
++
++ if ( count >= 1 )
++ {
++ if (!strcmp(tokens[0], "Score") && count >= 4)
++ {
++ //format: key|type|score
++ LLUUID avatar(tokens[1]);
++ std::string type = tokens[2];
++ F32 score = (F32)strtod(tokens[3], NULL);
++
++ LLAvatarListEntry *ent = gFloaterAvatarList->getAvatarEntry(avatar);
++ if ( ent != NULL )
++ {
++ LLAvListTrustNetScore s(type, score);
++ ent->mTrustNetScore.setValue(s);
++ llinfos << "Score arrived for avatar " << avatar << ": " << score << llendl;
++ }
++ else
++ {
++ llinfos << "Score arrived for avatar " << avatar << ", but it wasn't in the list anymore" << llendl;
++ }
++ }
++ else if (!strcmp(tokens[0], "WebAuthToken") && count >= 2)
++ {
++ std::string URL = LLWeb::escapeURL(llformat("http://trustnet.daleglass.net/?session=%s", tokens[1]));
++ LLWeb::loadURL(URL);
++ }
++ else if (!strcmp(tokens[0], "WebPassword") && count >= 2)
++ {
++ std::string password = tokens[1];
++ gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(password));
++ }
++ else
++ {
++ llwarns << "Unrecognized TrustNet reply " << tokens[0] << llendl;
++ }
++ }
++}
++
++void LLFloaterAvatarList::luskwoodCommand(std::string cmd)
++{
++ LLDynamicArray<LLUUID> ids = mAvatarList->getSelectedIDs();
++
++ for(LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
++ {
++ LLUUID avid = *itr;
++ LLAvatarListEntry *ent = getAvatarEntry(avid);
++ if ( ent != NULL )
++ {
++ //llinfos << "Would say: " << cmd << " " << ent->getName() << llendl;
++ // Use key got gokey, name for everything else
++ speakText(0, CHAT_TYPE_SHOUT, cmd + " " + ( cmd == "gokey" ? ent->getID().asString() : ent->getName() ) );
++ }
++ }
++}
++
++//static
++void LLFloaterAvatarList::onClickMark(void *userdata)
++{
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++ LLDynamicArray<LLUUID> ids = avlist->mAvatarList->getSelectedIDs();
++
++ for(LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
++ {
++ LLUUID avid = *itr;
++ LLAvatarListEntry *ent = avlist->getAvatarEntry(avid);
++ if ( ent != NULL )
++ {
++ ent->toggleMark();
++ }
++ }
++}
++
++void LLFloaterAvatarList::handleLuskwoodDialog(S32 option, void* data)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)data;
++ if ( 0 == option )
++ {
++ self->luskwoodCommand(self->mLuskwoodCommand);
++ }
++}
++
++void LLFloaterAvatarList::handleLuskwoodGohomerOffDialog(S32 option, void* data)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)data;
++ if ( 0 == option )
++ {
++ self->speakText(0, CHAT_TYPE_SHOUT, "gohome off");
++ }
++}
++
++//static
++void LLFloaterAvatarList::onClickGohomerWarn(void *data)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)data;
++
++ self->mLuskwoodCommand = "gowarn";
++ gViewerWindow->alertXml("LuskwoodGohomerWarn", handleLuskwoodDialog, self);
++
++}
++
++//static
++void LLFloaterAvatarList::onClickGohomerEject(void *data)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)data;
++
++ self->mLuskwoodCommand = "goeject";
++ gViewerWindow->alertXml("LuskwoodGohomerEject", handleLuskwoodDialog, self);
++}
++
++//static
++void LLFloaterAvatarList::onClickGohomerSendAway(void *data)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)data;
++
++ self->mLuskwoodCommand = "goaway";
++ gViewerWindow->alertXml("LuskwoodGohomerKeepAway", handleLuskwoodDialog, self);
++}
++
++//static
++void LLFloaterAvatarList::onClickGohomerSendHome(void *data)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)data;
++
++ self->mLuskwoodCommand = "gohome";
++ gViewerWindow->alertXml("LuskwoodGohomerSendHome", handleLuskwoodDialog, self);
++}
++
++//static
++void LLFloaterAvatarList::onClickGohomerSendHomeByKey(void *data)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)data;
++
++ self->mLuskwoodCommand = "gokey";
++ gViewerWindow->alertXml("LuskwoodGohomerSendHome", handleLuskwoodDialog, self);
++}
++
++
++//static
++void LLFloaterAvatarList::onClickGohomerOff(void *data)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)data;
++
++ gViewerWindow->alertXml("LuskwoodGohomerOff", handleLuskwoodGohomerOffDialog, self);
++}
++
++LLColor4 LLFloaterAvatarList::getAvatarColor(LLAvatarListEntry *ent, F32 distance, e_coloring_type type)
++{
++ F32 r = 0.0f, g = 0.0f, b = 0.0f, a = 1.0f;
++
++ switch(type)
++ {
++ case CT_NONE:
++ return LLColor4::black;
++ break;
++ case CT_DISTANCE:
++ if ( distance <= 10.0f )
++ {
++ // whisper range
++ g = 0.7f - ( distance / 20.0f );
++ }
++ else if ( distance > 10.0f && distance <= 20.0f )
++ {
++ // talk range
++ g = 0.7f - ( (distance - 10.0f) / 20.0f );
++ b = g;
++ }
++ else if ( distance > 20.0f && distance <= 96.0f )
++ {
++ // shout range
++ r = 0.7f - ( (distance - 20.0f) / 192.0f );
++ b = r;
++ }
++ else
++ {
++ // unreachable by chat
++ r = 1.0;
++ }
++ break;
++ case CT_AGE:
++ if ( ent->mAvatarInfo.getStatus() == DATA_RETRIEVED )
++ {
++ S32 age = ent->mAvatarInfo.getValue().getAge();
++ if ( age < 14 )
++ {
++ r = 0.7f - ( age / 28 );
++ }
++ else if ( age > 14 && age <= 30 )
++ {
++ r = 0.7f - ( (age-14) / 32 );
++ g = r;
++ }
++ else if ( age > 30 && age < 90 )
++ {
++ g = 0.7f - ( (age-30) / 120 );
++ }
++ else
++ {
++ b = 1.0f;
++ }
++ }
++ break;
++ case CT_SCORE:
++ if ( ent->mTrustNetScore.getStatus() == DATA_RETRIEVED )
++ {
++ F32 score = ent->mTrustNetScore.getValue().Score;
++
++ if ( score == 0.0 )
++ {
++ b = 1.0f;
++ }
++ else if ( score == 10.0f )
++ {
++ g = 1.0f;
++ }
++ else if ( score == -10.0f )
++ {
++ r = 1.0f;
++ }
++ else if ( score > 0.0f )
++ {
++ g = 0.2f + ( score / 20.0f );
++ }
++ else if ( score < 0.0f )
++ {
++ r = 0.2f + ( score / 20.0f );
++ }
++ }
++ break;
++ case CT_PAYMENT:
++ break;
++ case CT_ENTERED:
++ F32 entered = ent->getEntryEnteredSeconds();
++ if (distance <= 20.0f)
++ {
++ if ( entered <= ( 5.0f * 60.0f ) )
++ {
++ r = 0.7f - ( entered / ( 4.0f * 5.0f * 60.0f ) );
++ b = 1.0f - ( ( ( 5.0f * 60.0f ) - entered) / ( 5.0f * 60.0f ) );
++ }
++ else
++ {
++ b = 1.0f;
++ }
++ }
++ else
++ {
++ r = g = b = 0.5f;
++ }
++ break;
++ }
++
++ return LLColor4(r,g,b,a);
++}
++
++void LLFloaterAvatarList::onDoubleClick(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++ LLScrollListItem *item = self->mAvatarList->getFirstSelected();
++ LLUUID agent_id = item->getUUID();
++ LLAvatarListEntry *ent = 0;
++
++ if ( self->mAvatars.count( agent_id ) )
++ {
++ ent = &self->mAvatars[ agent_id ];
++ }
++
++ if ( ent && ent->getIsCoarse() )
++ {
++ // nothing to look at, manipulate camera directly
++ LLQuaternion rot;
++
++ LLMatrix3 mat(rot);
++
++ LLVector3 pos( ent->getPosition() );
++
++ LLViewerCamera::getInstance()->setView(1.0);
++ LLViewerCamera::getInstance()->setOrigin( pos );
++ LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]);
++ LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]);
++ LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]); }
++ else
++ {
++ gAgent.lookAtObject(agent_id, CAMERA_POSITION_OBJECT);
++ }
++}
++
++void LLFloaterAvatarList::removeFocusFromAll()
++{
++ std::map<LLUUID, LLAvatarListEntry>::iterator iter;
++
++ for(iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
++ {
++ LLAvatarListEntry *ent = &iter->second;
++ ent->setFocus(FALSE);
++ }
++}
++
++void LLFloaterAvatarList::focusOnPrev(BOOL marked_only)
++{
++ std::map<LLUUID, LLAvatarListEntry>::iterator iter;
++ LLAvatarListEntry *prev = NULL;
++ LLAvatarListEntry *ent;
++
++ if ( mAvatars.size() == 0 )
++ {
++ return;
++ }
++
++ for(iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
++ {
++ ent = &iter->second;
++
++ if ( ent->isDead() )
++ continue;
++
++ if ( (ent->getID() == mFocusedAvatar) && (prev != NULL) )
++ {
++ removeFocusFromAll();
++ prev->setFocus(TRUE);
++ mFocusedAvatar = prev->getID();
++ gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
++ return;
++ }
++
++ if ( (!marked_only) || ent->isMarked() )
++ {
++ prev = ent;
++ }
++ }
++
++ if (prev != NULL && ((!marked_only) || prev->isMarked()) )
++ {
++ removeFocusFromAll();
++ prev->setFocus(TRUE);
++ mFocusedAvatar = prev->getID();
++ gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
++ }
++}
++
++void LLFloaterAvatarList::focusOnNext(BOOL marked_only)
++{
++
++
++ std::map<LLUUID, LLAvatarListEntry>::iterator iter;
++ BOOL found = FALSE;
++ LLAvatarListEntry *first = NULL;
++ LLAvatarListEntry *ent;
++
++ if ( mAvatars.size() == 0 )
++ {
++ return;
++ }
++
++ for(iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
++ {
++ ent = &iter->second;
++
++ if ( ent->isDead() )
++ continue;
++
++ if ( NULL == first && ((!marked_only) || ent->isMarked()))
++ {
++ first = ent;
++ }
++
++ if ( found && ((!marked_only) || ent->isMarked()) )
++ {
++ removeFocusFromAll();
++ ent->setFocus(TRUE);
++ mFocusedAvatar = ent->getID();
++ gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
++ return;
++ }
++
++ if ( ent->getID() == mFocusedAvatar )
++ {
++ found = TRUE;
++ }
++ }
++
++ if (first != NULL && ((!marked_only) || first->isMarked()))
++ {
++ removeFocusFromAll();
++ first->setFocus(TRUE);
++ mFocusedAvatar = first->getID();
++ gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
++ }
++}
++//static
++void LLFloaterAvatarList::onClickPrevInList(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++ self->focusOnPrev(FALSE);
++}
++
++//static
++void LLFloaterAvatarList::onClickNextInList(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++ self->focusOnNext(FALSE);
++}
++
++//static
++void LLFloaterAvatarList::onClickPrevMarked(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++ self->focusOnPrev(TRUE);
++}
++
++//static
++void LLFloaterAvatarList::onClickNextMarked(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++ self->focusOnNext(TRUE);
++}
++
++//static
++void LLFloaterAvatarList::onClickTrustNetRate(void *userdata)
++{
++ // LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++ llinfos << "Ratings not implemented yet" << llendl;
++}
++
++//static
++void LLFloaterAvatarList::onClickTrustNetExplain(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++ LLScrollListItem *item = self->mAvatarList->getFirstSelected();
++
++ if ( item != NULL )
++ {
++ LLAvatarListEntry *ent = self->getAvatarEntry(item->getUUID());
++ self->speakText(TRUSTNET_CHANNEL, CHAT_TYPE_WHISPER, "Explain|" + ent->getName() + "|" + ent->getID().asString());
++ }
++}
++
++//static
++void LLFloaterAvatarList::onClickTrustNetWebsite(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++
++ self->speakText(TRUSTNET_CHANNEL, CHAT_TYPE_WHISPER, "GetWebAuthToken");
++}
++
++//static
++void LLFloaterAvatarList::onClickTrustNetGetPassword(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++
++ self->speakText(TRUSTNET_CHANNEL, CHAT_TYPE_WHISPER, "GetWebPassword");
++}
++
++//static
++void LLFloaterAvatarList::onClickTrustNetRenew(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++ self->speakText(TRUSTNET_CHANNEL, CHAT_TYPE_WHISPER, "RenewSubscription");
++}
++
++//static
++void LLFloaterAvatarList::onClickGetKey(void *userdata)
++{
++ LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
++ LLScrollListItem *item = self->mAvatarList->getFirstSelected();
++
++ if ( NULL == item ) return;
++
++ LLUUID agent_id = item->getUUID();
++
++ char buffer[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
++ agent_id.toString(buffer);
++
++ gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer));
++}
++
++
++static void send_freeze(const LLUUID& avatar_id, bool freeze)
++{
++ U32 flags = 0x0;
++ if (!freeze)
++ {
++ // unfreeze
++ flags |= 0x1;
++ }
++
++ LLMessageSystem* msg = gMessageSystem;
++ LLViewerObject* avatar = gObjectList.findObject(avatar_id);
++
++ if (avatar)
++ {
++ msg->newMessage("FreezeUser");
++ msg->nextBlock("AgentData");
++ msg->addUUID("AgentID", gAgent.getID());
++ msg->addUUID("SessionID", gAgent.getSessionID());
++ msg->nextBlock("Data");
++ msg->addUUID("TargetID", avatar_id );
++ msg->addU32("Flags", flags );
++ msg->sendReliable( avatar->getRegion()->getHost() );
++ }
++}
++
++static void send_eject(const LLUUID& avatar_id, bool ban)
++{
++ LLMessageSystem* msg = gMessageSystem;
++ LLViewerObject* avatar = gObjectList.findObject(avatar_id);
++
++ if (avatar)
++ {
++ U32 flags = 0x0;
++ if ( ban )
++ {
++ // eject and add to ban list
++ flags |= 0x1;
++ }
++
++ msg->newMessage("EjectUser");
++ msg->nextBlock("AgentData");
++ msg->addUUID("AgentID", gAgent.getID() );
++ msg->addUUID("SessionID", gAgent.getSessionID() );
++ msg->nextBlock("Data");
++ msg->addUUID("TargetID", avatar_id );
++ msg->addU32("Flags", flags );
++ msg->sendReliable( avatar->getRegion()->getHost() );
++ }
++}
++
++static void send_estate_message(
++ const char* request,
++ const LLUUID &target)
++{
++
++ LLMessageSystem* msg = gMessageSystem;
++ LLUUID invoice;
++
++ // This seems to provide an ID so that the sim can say which request it's
++ // replying to. I think this can be ignored for now.
++ invoice.generate();
++
++ llinfos << "Sending estate request '" << request << "'" << llendl;
++ msg->newMessage("EstateOwnerMessage");
++ msg->nextBlockFast(_PREHASH_AgentData);
++ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
++ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
++ msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null); //not used
++ msg->nextBlock("MethodData");
++ msg->addString("Method", request);
++ msg->addUUID("Invoice", invoice);
++
++ // Agent id
++ msg->nextBlock("ParamList");
++ msg->addString("Parameter", gAgent.getID().asString().c_str());
++
++ // Target
++ msg->nextBlock("ParamList");
++ msg->addString("Parameter", target.asString().c_str());
++
++ msg->sendReliable(gAgent.getRegion()->getHost());
++}
++
++static void send_estate_ban(const LLUUID& agent)
++{
++ LLUUID invoice;
++ U32 flags = ESTATE_ACCESS_BANNED_AGENT_ADD;
++
++ invoice.generate();
++
++ LLMessageSystem* msg = gMessageSystem;
++ msg->newMessage("EstateOwnerMessage");
++ msg->nextBlockFast(_PREHASH_AgentData);
++ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
++ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
++ msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null); //not used
++
++ msg->nextBlock("MethodData");
++ msg->addString("Method", "estateaccessdelta");
++ msg->addUUID("Invoice", invoice);
++
++ char buf[MAX_STRING]; /* Flawfinder: ignore*/
++ gAgent.getID().toString(buf);
++ msg->nextBlock("ParamList");
++ msg->addString("Parameter", buf);
++
++ snprintf(buf, MAX_STRING, "%u", flags); /* Flawfinder: ignore */
++ msg->nextBlock("ParamList");
++ msg->addString("Parameter", buf);
++
++ agent.toString(buf);
++ msg->nextBlock("ParamList");
++ msg->addString("Parameter", buf);
++
++ gAgent.sendReliableMessage();
++}
++
++static void cmd_freeze(const LLUUID& avatar, const std::string &name) { send_freeze(avatar, true); }
++static void cmd_unfreeze(const LLUUID& avatar, const std::string &name) { send_freeze(avatar, false); }
++static void cmd_eject(const LLUUID& avatar, const std::string &name) { send_eject(avatar, false); }
++static void cmd_ban(const LLUUID& avatar, const std::string &name) { send_eject(avatar, true); }
++static void cmd_profile(const LLUUID& avatar, const std::string &name) { LLFloaterAvatarInfo::showFromDirectory(avatar); }
++static void cmd_mute(const LLUUID&avatar, const std::string &name) { LLMuteList::getInstance()->add(LLMute(avatar, name, LLMute::AGENT)); }
++static void cmd_unmute(const LLUUID&avatar, const std::string &name) { LLMuteList::getInstance()->remove(LLMute(avatar, name, LLMute::AGENT)); }
++static void cmd_estate_eject(const LLUUID &avatar, const std::string &name){ send_estate_message("teleporthomeuser", avatar); }
++static void cmd_estate_ban(const LLUUID &avatar, const std::string &name)
++{
++ send_estate_message("teleporthomeuser", avatar); // Kick first, just to be sure
++ send_estate_ban(avatar);
++}
++
++void LLFloaterAvatarList::doCommand(void (*func)(const LLUUID &avatar, const std::string &name))
++{
++ LLDynamicArray<LLUUID> ids = mAvatarList->getSelectedIDs();
++
++ for(LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
++ {
++ LLUUID avid = *itr;
++ LLAvatarListEntry *ent = getAvatarEntry(avid);
++ if ( ent != NULL )
++ {
++ llinfos << "Executing command on " << ent->getName() << llendl;
++ func(avid, ent->getName());
++ }
++ }
++}
++
++std::string LLFloaterAvatarList::getSelectedNames(const std::string& separator)
++{
++ std::string ret = "";
++
++ LLDynamicArray<LLUUID> ids = mAvatarList->getSelectedIDs();
++ for(LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
++ {
++ LLUUID avid = *itr;
++ LLAvatarListEntry *ent = getAvatarEntry(avid);
++ if ( ent != NULL )
++ {
++ if (!ret.empty()) ret += separator;
++ ret += ent->getName();
++ }
++ }
++
++ return ret;
++}
++
++//static
++void LLFloaterAvatarList::callbackFreeze(S32 option, void *userdata) {
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++
++ if ( option == 0 )
++ {
++ avlist->doCommand(cmd_freeze);
++ }
++ else if ( option == 1 )
++ {
++ avlist->doCommand(cmd_unfreeze);
++ }
++}
++
++//static
++void LLFloaterAvatarList::callbackEject(S32 option, void *userdata) {
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++
++ if ( option == 0 )
++ {
++ avlist->doCommand(cmd_eject);
++ }
++ else if ( option == 1 )
++ {
++ avlist->doCommand(cmd_ban);
++ }
++}
++
++//static
++void LLFloaterAvatarList::callbackMute(S32 option, void *userdata) {
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++
++ if ( option == 0 )
++ {
++ avlist->doCommand(cmd_mute);
++ }
++ else if ( option == 1 )
++ {
++ avlist->doCommand(cmd_unmute);
++ }
++}
++
++//static
++void LLFloaterAvatarList::callbackEjectFromEstate(S32 option, void *userdata) {
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++
++ if ( option == 0 )
++ {
++ avlist->doCommand(cmd_estate_eject);
++ }
++ else if ( option == 1 )
++ {
++ avlist->doCommand(cmd_estate_ban);
++ }
++}
++
++//static
++void LLFloaterAvatarList::onClickFreeze(void *userdata)
++{
++ LLStringUtil::format_map_t args;
++ args["[NAMES]"] = ((LLFloaterAvatarList*)userdata)->getSelectedNames();
++ gViewerWindow->alertXml("AvatarListFreezeAvatars", args, callbackFreeze, userdata);
++}
++
++//static
++void LLFloaterAvatarList::onClickEject(void *userdata)
++{
++ LLStringUtil::format_map_t args;
++ args["[NAMES]"] = ((LLFloaterAvatarList*)userdata)->getSelectedNames();
++ gViewerWindow->alertXml("AvatarListEjectAvatars", args, callbackEject, userdata);
++}
++
++//static
++void LLFloaterAvatarList::onClickMute(void *userdata)
++{
++ LLStringUtil::format_map_t args;
++ args["[NAMES]"] = ((LLFloaterAvatarList*)userdata)->getSelectedNames();
++ gViewerWindow->alertXml("AvatarListMuteAvatars", args, callbackMute, userdata);
++}
++
++//static
++void LLFloaterAvatarList::onClickEjectFromEstate(void *userdata)
++{
++ LLStringUtil::format_map_t args;
++ args["[NAMES]"] = ((LLFloaterAvatarList*)userdata)->getSelectedNames();
++ gViewerWindow->alertXml("AvatarListEjectAvatarsFromEstate", args, callbackEjectFromEstate, userdata);
++}
++
++
++
++//static
++void LLFloaterAvatarList::onClickAR(void *userdata)
++{
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++ LLDynamicArray<LLUUID> ids = avlist->mAvatarList->getSelectedIDs();
++
++ for(LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
++ {
++ LLUUID avid = *itr;
++ llinfos << "Adding " << avid << " to AR queue" << llendl;
++ avlist->mARQueue.push( avid );
++ }
++}
++
++// static
++void LLFloaterAvatarList::onClickProfile(void* userdata)
++{
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++ avlist->doCommand(cmd_profile);
++}
++
++//static
++void LLFloaterAvatarList::onClickTeleport(void* userdata)
++{
++ LLFloaterAvatarList *avlist = (LLFloaterAvatarList*)userdata;
++ LLScrollListItem *item = avlist->mAvatarList->getFirstSelected();
++
++ if ( item )
++ {
++ LLUUID agent_id = item->getUUID();
++ LLAvatarListEntry *ent = avlist->getAvatarEntry(agent_id);
++
++ if ( ent )
++ {
++ llinfos << "Trying to teleport to " << ent->getName() << " at " << ent->getPosition() << llendl;
++ gAgent.teleportViaLocation( ent->getPosition() );
++ }
++ }
++}
++
++
++void LLFloaterAvatarList::renderDebugBeacons()
++{
++ LLFastTimer t(LLFastTimer::FTM_TEMP1);
++ std::map<LLUUID, LLAvatarListEntry>::iterator iter;
++ for(iter = mAvatars.begin(); iter != mAvatars.end(); ++iter)
++ {
++ LLAvatarListEntry entry = iter->second;
++ if ( entry.isDead() )
++ { // remove HUD Object if it exists
++ if ( mHudObjectMap.count(entry.getID()) )
++ {
++ mHudObjectMap[entry.getID()]->markDead();
++ mHudObjectMap.erase(entry.getID());
++ }
++ continue;
++ }
++
++ std::string name = entry.getName();
++ LLVector3d avpos = entry.getPosition();
++ LLVector3d mypos = gAgent.getPositionGlobal();
++ LLVector3d delta = avpos - mypos;
++ F32 distance = (F32)delta.magVec();
++
++ std::string info = llformat( "%s %.02fm", name.c_str(), distance );
++ LLVector3 agentpos = gAgent.getPosAgentFromGlobal( avpos );
++
++ // roll our own HUD text since the debug beacons are rendered too late (after HUD update)
++ if (mHudObjectMap.count(entry.getID()) == 0)
++ {
++ mHudObjectMap[entry.getID()] = LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
++ }
++ LLHUDText* hud_textp = (LLHUDText *)mHudObjectMap[entry.getID()].get(); // :-/
++
++ hud_textp->setZCompare(FALSE);
++ hud_textp->setString(utf8str_to_wstring(info));
++ hud_textp->setColor(LLColor4::white);
++ hud_textp->setPositionAgent(agentpos);
++ hud_textp->setUseBubble(TRUE);
++ hud_textp->setMass(10.f);
++ hud_textp->setDoFade(FALSE);
++ //std::string posinfo = llformat("%f %f %f", agentpos[0], agentpos[1], agentpos[2]);
++ //hud_textp->setLabel(posinfo);
++
++ gObjectList.addDebugBeacon( agentpos, "", LLColor4(0.0f, 0.0f, 1.f, 0.5f), LLColor4::white, gSavedSettings.getS32("DebugBeaconLineWidth") );
++ }
++}
++
++bool LLFloaterAvatarList::sRenderAvatarBeacons = true;
++
+diff --git a/indra/newview/llfloateravatarlist.h b/indra/newview/llfloateravatarlist.h
+new file mode 100644
+index 0000000..78df867
+--- /dev/null
++++ b/indra/newview/llfloateravatarlist.h
+@@ -0,0 +1,868 @@
++//
++// C++ Interface: llfloateravatarlist
++//
++// Description:
++//
++//
++// Author: Dale Glass <dale at daleglass.net>, (C) 2007
++//
++// Copyright: See COPYING file that comes with this distribution
++//
++//
++#include "llfloater.h"
++#include "llfloaterreporter.h"
++#include "lluuid.h"
++#include "lltimer.h"
++#include "llchat.h"
++#include "llappviewer.h"
++#include "llscrolllistctrl.h"
++#include "llhudobject.h"
++
++#include <time.h>
++#include <map>
++
++class LLFloaterAvatarList;
++
++/**
++ * @brief Account type
++ */
++enum ACCOUNT_TYPE
++{
++ ACCOUNT_RESIDENT, /** Normal resident */
++ ACCOUNT_TRIAL, /** Trial account */
++ ACCOUNT_CHARTER_MEMBER, /** Lifetime account obtained during beta */
++ ACCOUNT_EMPLOYEE, /** Linden Lab employee */
++ ACCOUNT_CUSTOM /** Custom account title specified. Seems to apply to Philip Linden */
++};
++
++/**
++ * @brief Payment data
++ */
++enum PAYMENT_TYPE
++{
++ PAYMENT_NONE, /** No payment data on file */
++ PAYMENT_ON_FILE, /** Payment data filled, but not used */
++ PAYMENT_USED, /** Payment data used */
++ PAYMENT_LINDEN /** Payment info doesn't apply (Linden, etc) */
++};
++
++
++/**
++ * @brief Activity
++ *
++ * This enum is ordered by priority, higher values have higher priority.
++ * Since we only have one column to show activity, priority decides what
++ * to show in case of conflict.
++ */
++enum ACTIVITY_TYPE
++{
++ ACTIVITY_NONE, /** Avatar not doing anything */
++ ACTIVITY_MOVING, /** Changing position */
++ ACTIVITY_GESTURING, /** Playing a gesture */
++ ACTIVITY_SOUND, /** Playing a sound */
++ ACTIVITY_REZZING, /** Rezzing objects */
++ ACTIVITY_PARTICLES, /** Creating particles */
++ ACTIVITY_TYPING, /** Typing */
++ ACTIVITY_NEW, /** Avatar just appeared */
++ ACTIVITY_DEAD /** Avatar isn't around anymore, and will be removed soon from the list */
++};
++
++enum DATA_STATUS
++{
++ DATA_UNKNOWN,
++ DATA_REQUESTING,
++ DATA_ERROR,
++ DATA_RETRIEVED
++};
++
++/**
++ * @brief How to color the user list
++ */
++enum e_coloring_type
++{
++ CT_NONE,
++ CT_DISTANCE,
++ CT_AGE,
++ CT_SCORE,
++ CT_PAYMENT,
++ CT_ENTERED
++};
++
++/**
++ * @brief Template class for a piece of data that must be retrieved
++ *
++ * This class handles the storage, retries and delays required to obtain a piece
++ * of data..
++ */
++template <class T>
++class LLAvatarListDatum
++{
++public:
++ LLAvatarListDatum()
++ {
++ mMaxPending = 32;
++
++ mRetryDelay = 0.0f;
++ mRequestDelay = 1.0f;
++ mFirstRequestTimeout = 16.0f;
++ mMaxRequestTimeout = 8192.0f;
++
++ mRequestTimer.start();
++ mRequestDelayTimer.start();
++ mStatus = DATA_UNKNOWN;
++
++ }
++
++ /**
++ * @brief Determines whether a data re-request is needed
++ * @returns TRUE if a re-request is needed
++ *
++ * This function takes the decision based on whether the data has
++ * been already retrieved, the last data request was long enough ago,
++ * and the request rate being low enough.
++ *
++ * A FALSE return value doesn't mean that everything is OK, only that
++ * a retry shouldn't be made at this time.
++ */
++ BOOL retryNeeded()
++ {
++ switch(mStatus)
++ {
++ case DATA_UNKNOWN:
++ case DATA_REQUESTING:
++ case DATA_ERROR:
++ // Don't request too many at once
++ if ( mPending >= mMaxPending )
++ {
++ return FALSE;
++ }
++
++ // Don't re-request if the requests would be made too fast
++ if ( mRequestDelayTimer.getElapsedTimeF32() < mRequestDelay )
++ {
++ return FALSE;
++ }
++
++ // Re-request if retry timeout was reached
++ return ( mRequestTimer.getElapsedTimeF32() > mRetryDelay );
++ case DATA_RETRIEVED:
++ return FALSE;
++ }
++
++ return FALSE;
++ }
++
++ /**
++ * @brief Notifies the object that a request was started
++ */
++ void requestStarted()
++ {
++
++ if ( mStatus != DATA_REQUESTING )
++ {
++ mPending++;
++ //llinfos << "Pe
++ }
++
++ mStatus = DATA_REQUESTING;
++
++ if ( mRetryDelay == 0 )
++ {
++ mRetryDelay = mFirstRequestTimeout;
++ }
++ else
++ {
++ if ( mRetryDelay < mMaxRequestTimeout )
++ {
++ mRetryDelay *= 2;
++ }
++ }
++
++ mRequestTimer.start();
++ mRequestDelayTimer.start();
++ }
++
++ /**
++ * @brief retryNeeded and requestStarted combined
++ * @returns TRUE if a re-request is needed
++ */
++ BOOL requestIfNeeded()
++ {
++ BOOL ret = retryNeeded();
++ if ( ret )
++ {
++ requestStarted();
++ }
++
++ return ret;
++ }
++
++ /**
++ * @brief Returns the request status
++ */
++ DATA_STATUS getStatus()
++ {
++ if ( mStatus == DATA_REQUESTING
++ && mRequestTimer.getElapsedTimeF32() > mRetryDelay )
++ {
++ mStatus = DATA_ERROR;
++
++ // Consider this request failed, so the slot is free.
++ mPending--;
++ }
++
++ return mStatus;
++ }
++
++ T& getValue()
++ {
++ return mValue;
++ }
++
++ void setValue(T val)
++ {
++ if ( mStatus != DATA_RETRIEVED )
++ {
++ mPending--;
++ }
++
++ mValue = val;
++ mStatus = DATA_RETRIEVED;
++ }
++
++ void setRequestDelay(F32 delay)
++ {
++ mRequestDelay = delay;
++ }
++
++ void setMaxPending(U32 count)
++ {
++ mMaxPending = count;
++ }
++private:
++ friend class LLFloaterAvatarList;
++ T mValue;
++
++ DATA_STATUS mStatus;
++
++
++ /**
++ * @brief How long to wait before trying to re-request the data
++ */
++ F32 mRetryDelay;
++
++ /**
++ * @brief How long to wait on the first attempt to get data
++ */
++ F32 mFirstRequestTimeout;
++
++ /**
++ * @brief Maximum delay between retries
++ *
++ * On failure, timeout gets doubled until it reaches this value
++ */
++ F32 mMaxRequestTimeout;
++
++ /**
++ * @brief Timer for the current request
++ */
++ LLTimer mRequestTimer;
++
++
++
++ /**
++ * @brief Timer used to separate requests, to avoid flooding the server
++ */
++ static LLTimer mRequestDelayTimer;
++
++ /**
++ * @brief Amount of pending requests
++ */
++ static U32 mPending;
++
++
++ /**
++ * @brief Time between requests of the same type
++ */
++ F32 mRequestDelay;
++
++ /**
++ * @brief Maximum amount of pending requests
++ */
++ U32 mMaxPending;
++};
++
++template <class T> LLTimer LLAvatarListDatum<T>::mRequestDelayTimer;
++template <class T> U32 LLAvatarListDatum<T>::mPending = 0;
++
++//template <class T> F32 LLAvatarListDatum<T>::mRequestDelay;
++
++
++
++/**
++ * @brief A TrustNet score
++ */
++struct LLAvListTrustNetScore
++{
++ F32 Score;
++ std::string Type;
++
++ LLAvListTrustNetScore(std::string type = "<uninitialized>", F32 score = 0.0f);
++};
++
++/**
++ * @brief Avatar payment information
++ */
++struct LLAvatarInfo
++{
++ PAYMENT_TYPE Payment;
++ ACCOUNT_TYPE Account;
++ struct tm BirthDate;
++
++ LLAvatarInfo();
++ LLAvatarInfo(PAYMENT_TYPE payment, ACCOUNT_TYPE account, struct tm birth);
++ S32 getAge();
++};
++
++/**
++ * @brief Misc data about the avatar
++ * This class holds replies from third party databases. There's no fixed format,
++ * and interpretation is left to the end user.
++ */
++struct LLMiscDBInfo
++{
++ std::string data;
++
++ LLMiscDBInfo(std::string d)
++ {
++ data = d;
++ }
++
++ LLMiscDBInfo()
++ {
++ data = "";
++ }
++};
++
++
++/**
++ * @brief This class is used to hold data about avatars.
++ * We cache data about avatars to avoid repeating requests in this class.
++ * Instances are kept in a map<LLAvatarListEntry>. We keep track of the
++ * frame where the avatar was last seen. Avatars that are old enough are
++ * not added to the visible list, but still kept around to cache the account
++ * info data (like the birth date and the payment data)
++ */
++class LLAvatarListEntry {
++public:
++
++
++ /**
++ * @brief Initializes a list entry
++ * @param id Avatar's key
++ * @param name Avatar's name
++ * @param position Avatar's current position
++ * @param isLinden TRUE if the avatar is a Linden
++ * @param isCoarse TRUE if the avatar is only in the coarse location list
++ * @param regionname if the region is not the same as the current agent region, else empty string
++ */
++ LLAvatarListEntry(const LLUUID& id = LLUUID::null, const std::string &name = "", const LLVector3d &position = LLVector3d::zero, BOOL isLinden = FALSE, BOOL isCoarse = FALSE, std::string regionname = std::string() ) :
++ mID(id), mName(name), mPosition(position), mMarked(FALSE), mFocused(FALSE), mIsLinden(isLinden), mIsCoarse(isCoarse), mRegionName(regionname), mActivityType(ACTIVITY_NEW), mAccountTitle(""),
++ mUpdateTimer(), mActivityTimer(), mFrame(0)
++ {
++ mTrustNetScore.setRequestDelay(0.1f);
++ mTrustNetScore.setMaxPending(8);
++ mFrame = gFrameCount;
++ mEnteredTimer.start();
++ }
++
++ /**
++ * Update world position.
++ * Affects age.
++ */
++ void setPosition(LLVector3d position);
++
++ LLVector3d getPosition();
++
++ /**
++ * @brief Returns the age of this entry in frames
++ *
++ * This is only used for determining whether the avatar is still around.
++ * @see getEntryAgeSeconds
++ */
++ U32 getEntryAgeFrames();
++
++ /**
++ * @brief Returns the age of this entry in seconds
++ */
++ F32 getEntryAgeSeconds();
++
++ /**
++ * @brief Returns time when avatar entered the list
++ */
++ F32 getEntryEnteredSeconds();
++
++ /**
++ * @brief Returns the name of the avatar
++ */
++ std::string getName();
++
++ void setName(std::string name);
++
++ LLUUID getID();
++
++ void setID(LLUUID id);
++
++ /**
++ * @brief Whether the avatar is a Linden
++ */
++ BOOL getIsLinden();
++
++ /**
++ * @brief whether the avatar entry was taken from the coarse location update
++ */
++ BOOL getIsCoarse() { return mIsCoarse; }
++
++ /**
++ * @brief returns the agents region name or "" if same as main agent
++ */
++ std::string &getRegionName() { return mRegionName; }
++
++ /**
++ * @brief returns true if agent is on same region
++ */
++ BOOL getIsSameRegion() { return mRegionName.empty(); }
++
++ /**
++ * @brief returns reference to the HUDObject-pointer for this avatar if any
++ */
++ LLPointer<LLHUDObject> &getHudObject() { return mHudObject; }
++
++ /**
++ * @brief Sets a custom title for the account
++ * @note Changes account type to ACCOUNT_CUSTOM
++ */
++ void setAccountCustomTitle(std::string &title);
++
++ /**
++ * @brief Gets the custom title for the account
++ */
++ std::string getAccountCustomTitle();
++
++ /**
++ * @brief Sets the activity type for this avatar
++ *
++ * Has no effect if the specified type is lower priority than the
++ * current one.
++ */
++ void setActivity(ACTIVITY_TYPE activity);
++
++ /**
++ * @brief Returns the activity type
++ */
++ ACTIVITY_TYPE getActivity();
++
++ /**
++ * @brief Sets the 'focus' status on this entry (camera focused on this avatar)
++ */
++ void setFocus(BOOL value) { mFocused = value; }
++
++ BOOL isFocused() { return mFocused; }
++
++
++ BOOL isMarked();
++
++ /**
++ * @brief Returns whether the item is dead and shouldn't appear in the list
++ * @returns TRUE if dead
++ */
++ BOOL isDead();
++
++ void toggleMark();
++private:
++ friend class LLFloaterAvatarList;
++
++ LLUUID mID;
++ std::string mName;
++ LLVector3d mPosition;
++ BOOL mMarked;
++ BOOL mFocused;
++ BOOL mIsLinden;
++ BOOL mIsCoarse;
++ std::string mRegionName;
++ LLPointer<LLHUDObject> mHudObject; /* holds the text on screen, if we don't keep this it won't reach a stable position on-screen but gets recreated for each update */
++
++
++ ACTIVITY_TYPE mActivityType;
++
++ std::string mAccountTitle;
++
++ LLAvatarListDatum<LLAvListTrustNetScore> mTrustNetScore;
++ LLAvatarListDatum<LLAvatarInfo> mAvatarInfo;
++ LLAvatarListDatum<LLMiscDBInfo> mMiscInfo;
++
++ /**
++ * @brief Timer to keep track of whether avatars are still there
++ */
++ LLTimer mUpdateTimer;
++
++ /**
++ * @brief Timer for avatar activities
++ */
++ LLTimer mActivityTimer;
++
++ /**
++ * @brief Last frame when this avatar was updated
++ */
++ U32 mFrame;
++
++ /**
++ * @brief Time when avatar entered the list
++ */
++ LLTimer mEnteredTimer;
++};
++
++
++/**
++ * @brief Avatar List
++ * Implements an avatar scanner in the client.
++ *
++ * This is my first attempt to modify the SL source. This code is intended
++ * to have a dual purpose: doing the task, and providing an example of how
++ * to do it. For that reason, it's going to be commented as exhaustively
++ * as possible.
++ *
++ * Since I'm very new to C++ any suggestions on coding, style, etc are very
++ * welcome.
++ */
++class LLFloaterAvatarList : public LLFloater
++{
++public:
++ /**
++ * @brief Creates and initializes the LLFloaterAvatarList
++ * Here the interface is created, and callbacks are initialized.
++ */
++ LLFloaterAvatarList();
++ ~LLFloaterAvatarList();
++
++ void show();
++
++ /**
++ * @brief Hide when user closes the list.
++ */
++ virtual void onClose(bool app_quitting) { setVisible(FALSE); }
++
++ /**
++ * @brief Toggles interface visibility
++ * There is only one instance of the avatar scanner at any time.
++ */
++ static void toggle(void*);
++
++ /**
++ * @brief Returns floater visibility status
++ */
++ static BOOL visible(void*);
++
++ /**
++ * @brief Updates the internal avatar list with the currently present avatars.
++ */
++ void updateAvatarList();
++
++ /**
++ * @brief Refresh avatar list (display)
++ */
++ void refreshAvatarList();
++
++ /**
++ * @brief Process the reply to a request for avatar properties
++ */
++ static void processAvatarPropertiesReply(LLMessageSystem *msg, void**);
++
++ /**
++ * @brief Returns TRUE if the avatar is in the list of known avatars
++ * @returns TRUE if the avatar is in the list
++ */
++ BOOL avatarIsInList(LLUUID avatar);
++
++ /**
++ * @brief Returns the entry for an avatar, if preset
++ * @returns Pointer to avatar entry, NULL if not found.
++ */
++ LLAvatarListEntry* getAvatarEntry(LLUUID avatar);
++
++ /**
++ * @brief Requests a TrustNet score from the Adapter
++ * @param avatar Avatar for which to request the score
++ * @param name Avatar's name
++ * @param type Score type ("behavior", etc)
++ */
++ void requestTrustNetScore(LLUUID avatar, const std::string name, const std::string type);
++
++ /**
++ * @brief Requests information about the avatar from the database
++ * @param avatar Avatar about whom we need information
++ * @param name Avatar's name
++ */
++ void requestMiscInfo(LLUUID avatar, const std::string name);
++
++ /**
++ * @brief Handles IM messages to process the ones that are replies to database requests
++ * @param from_id Key of the avatar sending the message
++ * @param message Content
++ * @returns TRUE if the message was handled. This will suppress further processing in llviewermessage.cpp
++ */
++ static BOOL handleIM(LLUUID from_id, const std::string message);
++
++ /**
++ * @brief Process a reply from the TrustNet Adapter
++ * This handles replies from the TrustNet adapter, such as score results.
++ */
++ static void processTrustNetReply(char *reply);
++
++ /**
++ * @brief Returns a string with the selected names in the list
++ */
++ std::string getSelectedNames(const std::string& separator = ", ");
++
++ /** @brief render a debug beacon for the coarse avatars */
++ void renderDebugBeacons();
++
++ /** @brief if avatar beacons shall be rendered */
++ static bool getRenderAvatarBeacons(void *data) { return sRenderAvatarBeacons; }
++
++ /** @brief set if avatar beacons shall be rendered */
++ static void toggleRenderAvatarBeacons(void *data) { sRenderAvatarBeacons = !sRenderAvatarBeacons; }
++
++ /** @brief set if avatar beacons shall be rendered */
++ static void setRenderAvatarBeacons(bool do_render ) { sRenderAvatarBeacons = do_render; }
++
++private:
++ // when a line editor loses keyboard focus, it is committed.
++ // commit callbacks are named onCommitWidgetName by convention.
++ //void onCommitBaz(LLUICtrl* ctrl, void *userdata);
++
++ enum AVATARS_COLUMN_ORDER
++ {
++ LIST_AVATAR_ICON,
++ LIST_AVATAR_NAME,
++ LIST_DISTANCE,
++ LIST_AGE,
++ /*LIST_SCORE,*/
++ LIST_PAYMENT,
++ LIST_ACTIVITY,
++ LIST_ENTERED,
++ };
++
++ typedef void (*avlist_command_t)(const LLUUID &avatar, const std::string &name);
++
++ void speakText(S32 channel, EChatType type, std::string text);
++
++ /**
++ * @brief Removes focus status from all avatars in list
++ */
++ void removeFocusFromAll();
++
++ /**
++ * @brief Focus camera on previous avatar
++ * @param marked_only Whether to choose only marked avatars
++ */
++ void focusOnPrev(BOOL marked_only);
++
++ /**
++ * @brief Focus camera on next avatar
++ * @param marked_only Whether to choose only marked avatars
++ */
++ void focusOnNext(BOOL marked_only);
++
++ /**
++ * @brief Updates the internal avatar list from the coarse location list if not already present
++ */
++ void updateFromCoarse();
++
++ /**
++ * @brief Purge hud object map from entries no longer in the list
++ */
++ void purgeAvatarHUDMap();
++
++ /**
++ * @brief Handler for the "refresh" button click.
++ * I am unsure whether this is actually necessary at the time.
++ *
++ * LL: By convention, button callbacks are named onClickButtonLabel
++ * @param userdata Pointer to user data (LLFloaterAvatarList instance)
++ */
++ //static void onClickRefresh(void* userdata);
++
++ static void onClickProfile(void *userdata);
++ static void onClickIM(void *userdata);
++ static void onClickTrack(void *userdata);
++ static void onClickMark(void *userdata);
++
++ static void onClickGohomerMark(void *userdata);
++ static void onClickGohomerWarn(void *userdata);
++ static void onClickGohomerEject(void *userdata);
++ static void onClickGohomerSendAway(void *userdata);
++ static void onClickGohomerSendHome(void *userdata);
++ static void onClickGohomerSendHomeByKey(void *userdata);
++
++ static void onClickGohomerOff(void *userdata);
++
++
++ static void onClickPrevInList(void *userdata);
++ static void onClickNextInList(void *userdata);
++ static void onClickPrevMarked(void *userdata);
++ static void onClickNextMarked(void *userdata);
++ static void onClickGetKey(void *userdata);
++
++ static void onClickTrustNetRate(void *userdata);
++ static void onClickTrustNetExplain(void *userdata);
++ static void onClickTrustNetWebsite(void *userdata);
++ static void onClickTrustNetGetPassword(void *userdata);
++ static void onClickTrustNetRenew(void *userdata);
++
++ static void onDoubleClick(void *userdata);
++
++ static void onClickFreeze(void *userdata);
++ static void onClickEject(void *userdata);
++// static void onClickBan(void *userdata);
++// static void onClickUnban(void *userdata);
++ static void onClickMute(void *userdata);
++// static void onClickUnmute(void *userdata);
++ static void onClickAR(void *userdata);
++ static void onClickTeleport(void *userdata);
++ static void onClickEjectFromEstate(void *userdata);
++
++ static void callbackFreeze(S32 option, void *userdata);
++// static void callbackUnfreeze(S32 option, void *userdata);
++ static void callbackEject(S32 option, void *userdata);
++// static void callbackBan(S32 option, void *userdata);
++ static void callbackMute(S32 option, void *userdata);
++// static void callbackUnmute(void *userdata);
++ static void callbackAR(void *userdata);
++ static void callbackEjectFromEstate(S32 option, void *userdata);
++
++ void doCommand(avlist_command_t cmd);
++
++ /**
++ * @brief Cleanup avatar list, removing dead entries from it.
++ * This lets dead entries remain for some time. This makes it possible
++ * to keep people passing by in the list long enough that it's possible
++ * to do something to them.
++ */
++ void expireAvatarList();
++
++ /**
++ * @brief Perform a Luskwood Gohomer command on the selected users
++ * @param cmd Command (for example "gowarn")
++ */
++ void luskwoodCommand(std::string cmd);
++
++ /**
++ * @brief Handle the results of a gohomer confirmation dialog
++ * @param option Option selected (0=ok, 1=cancel)
++ * @param data this
++ */
++ static void handleLuskwoodDialog(S32 option, void* data);
++
++ /**
++ * @brief Handle the results of a gohomer turn off dialog
++ * @param option Option selected (0=ok, 1=cancel)
++ * @param data this
++ */
++ static void handleLuskwoodGohomerOffDialog(S32 option, void* data);
++
++ /**
++ * @brief Process the AR queue
++ * This generates AR reports for the queued avatars
++ */
++ void processARQueue();
++
++private:
++ /**
++ * @brief Pointer to the avatar scroll list
++ */
++ LLScrollListCtrl* mAvatarList;
++ std::map<LLUUID, LLAvatarListEntry> mAvatars;
++
++ /**
++ * @brief Queue of abuse reports
++ */
++ std::queue<LLUUID> mARQueue;
++
++ /**
++ * @brief List of AR screens opened
++ * We don't open them on creation to capture a clean screenshot. They're
++ * opened only after finishing the process.
++ */
++ std::queue<LLFloaterReporter*> mARReporterQueue;
++
++ /**
++ * @brief Last time during which an AR was submitted
++ * Used to give the camera some time to move between avatars. Perhaps this way
++ * of doing things isn't ideal, though.
++ */
++ S32 mARLastFrame;
++
++
++ /**
++ * @brief Request information about the specified avatar
++ * @param avid Avatar id to request info about
++ */
++ void sendAvatarPropertiesRequest(LLUUID avid);
++
++ void checkTrackingStatus();
++
++ /**
++ * @brief Returns the color for the specified avatar entry
++ * @param ent Avatar entry
++ * @param distance Distance from the user
++ */
++ //LLColor4 getAvatarColor(LLAvatarListEntry *ent, F32 distance);
++ LLColor4 getAvatarColor(LLAvatarListEntry *ent, F32 distance, e_coloring_type type);
++
++ /**
++ * @brief Replace variables in string
++ * @param str String to replace variables in
++ * @param avatar Value for $KEY
++ * @param name Value for $NAME
++ */
++ static void replaceVars(std::string &str, LLUUID avatar, const std::string& name);
++
++ // tracking data
++ BOOL mTracking; // tracking?
++ BOOL mTrackByLocation; // TRUE if tracking by known position, FALSE for tracking a friend
++ LLUUID mTrackedAvatar; // who we're tracking
++
++ /**
++ * @brief Used to delay avatar data requests
++ */
++ LLTimer mDataRequestTimer;
++
++ /**
++ * @brief Used to delay trustnet requests
++ */
++ LLTimer mTrustNetTimer;
++
++ /**
++ * @brief Luskwood command to execute
++ */
++ std::string mLuskwoodCommand;
++
++ /**
++ * @brief Avatar the camera is focused on
++ */
++ LLUUID mFocusedAvatar;
++
++ /** @brief holds state of avatar beacon render setup */
++ static bool sRenderAvatarBeacons;
++
++
++ std::map< LLUUID, LLPointer<LLHUDObject> > mHudObjectMap;
++};
++
++/**
++ * Pointer to global LLFloaterAvatarList instance.
++ * This is initialized in llviewerwindow.cpp
++ * @see llviewerwindow.cpp
++ */
++extern LLFloaterAvatarList* gFloaterAvatarList;
+diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
+index 5451ca0..754c839 100644
+--- a/indra/newview/llfloaterpreference.cpp
++++ b/indra/newview/llfloaterpreference.cpp
+@@ -60,6 +60,7 @@
+ #include "llpanelLCD.h"
+ #include "llpanelmsgs.h"
+ #include "llpanelweb.h"
++#include "llpaneldatabase.h"
+ #include "llpanelskins.h"
+ #include "llprefschat.h"
+ #include "llprefsvoice.h"
+@@ -190,6 +191,10 @@ LLPreferenceCore::LLPreferenceCore(LLTabContainer* tab_container, LLButton * def
+ mTabContainer->addTabPanel(mSkinsPanel, mSkinsPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mSkinsPanel->setDefaultBtn(default_btn);
+
++ mDBPanel = new LLPanelDatabase();
++ mTabContainer->addTabPanel(mDBPanel, mDBPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
++ mDBPanel->setDefaultBtn(default_btn);
++
+ if (!mTabContainer->selectTab(gSavedSettings.getS32("LastPrefTab")))
+ {
+ mTabContainer->selectFirstTab();
+@@ -269,6 +274,7 @@ void LLPreferenceCore::apply()
+ LLFloaterHardwareSettings::instance()->apply();
+
+ mWebPanel->apply();
++ mDBPanel->apply();
+ #if LL_LCD_COMPILE
+ // only add this option if we actually have a logitech keyboard / speaker set
+ if (gLcdScreen->Enabled())
+@@ -297,6 +303,7 @@ void LLPreferenceCore::cancel()
+ LLFloaterHardwareSettings::instance()->cancel();
+
+ mWebPanel->cancel();
++ mDBPanel->cancel();
+ #if LL_LCD_COMPILE
+ // only add this option if we actually have a logitech keyboard / speaker set
+ if (gLcdScreen->Enabled())
+diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
+index bfa2499..e54b7f6 100644
+--- a/indra/newview/llfloaterpreference.h
++++ b/indra/newview/llfloaterpreference.h
+@@ -49,6 +49,7 @@ class LLPanelAudioPrefs;
+ class LLPanelDebug;
+ class LLPanelNetwork;
+ class LLPanelWeb;
++class LLPanelDatabase;
+ class LLMessageSystem;
+ class LLPrefsChat;
+ class LLPrefsVoice;
+@@ -91,6 +92,7 @@ private:
+ LLPanelWeb *mWebPanel;
+ LLPanelMsgs *mMsgPanel;
+ LLPanelLCD *mLCDPanel;
++ LLPanelDatabase *mDBPanel;
+ };
+
+ // Floater to control preferences (display, audio, bandwidth, general.
+diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp
+index 7e9632d..23f9ec5 100644
+--- a/indra/newview/llfloaterreporter.cpp
++++ b/indra/newview/llfloaterreporter.cpp
+@@ -540,7 +540,7 @@ void LLFloaterReporter::showFromMenu(EReportType report_type)
+
+
+ // static
+-void LLFloaterReporter::showFromObject(const LLUUID& object_id)
++LLFloaterReporter* LLFloaterReporter::showFromObject(const LLUUID& object_id, bool show)
+ {
+ LLFloaterReporter* f = createNewAbuseReporter();
+ f->center();
+@@ -557,7 +557,16 @@ void LLFloaterReporter::showFromObject(const LLUUID& object_id)
+ // Need to deselect on close
+ f->mDeselectOnClose = TRUE;
+
+- f->open(); /* Flawfinder: ignore */
++ if ( show )
++ {
++ f->open(); /* Flawfinder: ignore */
++ }
++ else
++ {
++ gDialogVisible = FALSE;
++ }
++
++ return f;
+ }
+
+
+diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h
+index ff7f112..1603dad 100644
+--- a/indra/newview/llfloaterreporter.h
++++ b/indra/newview/llfloaterreporter.h
+@@ -92,7 +92,7 @@ public:
+ // Enables all buttons
+ static void showFromMenu(EReportType report_type);
+
+- static void showFromObject(const LLUUID& object_id);
++ static LLFloaterReporter* showFromObject(const LLUUID& object_id, bool show = true);
+
+ static void onClickSend (void *userdata);
+ static void onClickCancel (void *userdata);
+diff --git a/indra/newview/llpaneldatabase.cpp b/indra/newview/llpaneldatabase.cpp
+new file mode 100644
+index 0000000..f510130
+--- /dev/null
++++ b/indra/newview/llpaneldatabase.cpp
+@@ -0,0 +1,149 @@
++/**
++ * @file llpaneldatabase.cpp
++ * @brief Database preferences panel
++ * @author Dale Glass
++ *
++ * Copyright (c) 2003-2007, Linden Research, Inc.
++ *
++ * The source code in this file ("Source Code") is provided by Linden Lab
++ * to you under the terms of the GNU General Public License, version 2.0
++ * ("GPL"), unless you have obtained a separate licensing agreement
++ * ("Other License"), formally executed by you and Linden Lab. Terms of
++ * the GPL can be found in doc/GPL-license.txt in this distribution, or
++ * online at http://secondlife.com/developers/opensource/gplv2
++ *
++ * There are special exceptions to the terms and conditions of the GPL as
++ * it is applied to this Source Code. View the full text of the exception
++ * in the file doc/FLOSS-exception.txt in this software distribution, or
++ * online at http://secondlife.com/developers/opensource/flossexception
++ *
++ * By copying, modifying or distributing this software, you acknowledge
++ * that you have read and understood your obligations described above,
++ * and agree to abide by those obligations.
++ *
++ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
++ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
++ * COMPLETENESS OR PERFORMANCE.
++ */
++
++#include "llviewerprecompiledheaders.h"
++
++#include "llpaneldatabase.h"
++
++#include "llscrolllistctrl.h"
++#include "llviewerwindow.h"
++#include "llviewercontrol.h"
++#include "lluictrlfactory.h"
++#include "llfloateravatarpicker.h"
++#include "llagentdata.h"
++#include "llnotify.h"
++
++//-----------------------------------------------------------------------------
++LLPanelDatabase::LLPanelDatabase() :
++ LLPanel("Messages Panel")
++{
++ LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_database.xml");
++ childSetAction("change_avatar_btn", onClickChangeAvatar, this);
++};
++
++//-----------------------------------------------------------------------------
++// postBuild()
++//-----------------------------------------------------------------------------
++
++void LLPanelDatabase::refresh()
++{
++ llinfos << "Loading settings" << llendl;
++ //mAvatarName = gSavedPerAccountSettings.getString("DBAvatarName");
++ //mAvatarKey.set(gSavedPerAccountSettings.getString("DBAvatarKey"));
++ //mURL = gSavedSettings.getString("DBURL");;
++ //mUsername = gSavedSettings.getString("DBURLUsername");;
++ //mPassword = gSavedSettings.getString("DBURLPassword");;
++ //mSendPattern = gSavedSettings.getString("DBSendPattern");;
++ //mPositivePattern = gSavedSettings.getString("DBPositivePattern");;
++ //mNegativePattern = gSavedSettings.getString("DBNegativePattern");;
++ //mDeniedPattern = gSavedSettings.getString("DBDeniedPattern");;
++
++}
++
++BOOL LLPanelDatabase::postBuild()
++{
++ refresh();
++
++ llinfos << "Setting settings in window" << llendl;
++ childSetText("db_avatar" ,mAvatarName );
++ childSetText("db_url" ,mURL );
++ childSetText("db_url_username" ,mUsername );
++ childSetText("db_url_password" ,mPassword );
++ childSetText("send_pattern" ,mSendPattern );
++ childSetText("positive_pattern",mPositivePattern );
++ childSetText("negative_pattern",mNegativePattern );
++ childSetText("denied_pattern" ,mDeniedPattern );
++
++ return TRUE;
++}
++
++
++void LLPanelDatabase::draw()
++{
++ LLPanel::draw();
++}
++
++void LLPanelDatabase::apply()
++{
++ //llinfos << "Saving settings" << llendl;
++
++ //gSavedPerAccountSettings.setString("DBAvatarName", childGetText("db_avatar").c_str());
++ //gSavedPerAccountSettings.setString("DBAvatarKey", mAvatarKey.asString());
++ //gSavedSettings.setString("DBURL", childGetText("db_url").c_str());
++ //gSavedSettings.setString("DBURLUsername", childGetText("db_url_username").c_str());
++ //gSavedSettings.setString("DBURLPassword", childGetText("db_url_password").c_str());
++
++ //gSavedSettings.setString("DBSendPattern", childGetText("send_pattern").c_str());
++ //gSavedSettings.setString("DBPositivePattern", childGetText("positive_pattern").c_str());
++ //gSavedSettings.setString("DBNegativePattern", childGetText("negative_pattern").c_str());
++ //gSavedSettings.setString("DBDeniedPattern", childGetText("denied_pattern").c_str());
++
++
++}
++
++void LLPanelDatabase::cancel()
++{
++
++}
++
++//static
++void LLPanelDatabase::onClickChangeAvatar(void *userdata)
++{
++ LLFloaterAvatarPicker::show(onPickAvatar, userdata, FALSE, TRUE);
++}
++
++//static
++void LLPanelDatabase::onPickAvatar(const std::vector<std::string>& names,
++ const std::vector<LLUUID>& ids,
++ void* user_data)
++{
++ if (names.empty()) return;
++ if (ids.empty()) return;
++
++ LLPanelDatabase *self = (LLPanelDatabase*)user_data;
++
++#ifndef LL_DEBUG
++ // TODO: LL_DEBUG isn't the right one, what is it?
++ //
++ // Using yourself as the database avatar should work, and be useful
++ // for debugging, but it's not something a normal user should be able
++ // to do.
++
++// if(ids[0] == gAgentID)
++// {
++// LLNotifyBox::showXml("AddSelfDatabase");
++// return;
++// }
++#endif
++
++ self->childSetText("db_avatar", names[0]);
++ self->mAvatarName = names[0];
++ self->mAvatarKey = ids[0];
++}
++
++
+diff --git a/indra/newview/llpaneldatabase.h b/indra/newview/llpaneldatabase.h
+new file mode 100644
+index 0000000..e08a453
+--- /dev/null
++++ b/indra/newview/llpaneldatabase.h
+@@ -0,0 +1,65 @@
++/**
++ * @file llprefschat.h
++ * @brief Database preferences panel
++ *
++ * Copyright (c) 2003-2007, Linden Research, Inc.
++ *
++ * The source code in this file ("Source Code") is provided by Linden Lab
++ * to you under the terms of the GNU General Public License, version 2.0
++ * ("GPL"), unless you have obtained a separate licensing agreement
++ * ("Other License"), formally executed by you and Linden Lab. Terms of
++ * the GPL can be found in doc/GPL-license.txt in this distribution, or
++ * online at http://secondlife.com/developers/opensource/gplv2
++ *
++ * There are special exceptions to the terms and conditions of the GPL as
++ * it is applied to this Source Code. View the full text of the exception
++ * in the file doc/FLOSS-exception.txt in this software distribution, or
++ * online at http://secondlife.com/developers/opensource/flossexception
++ *
++ * By copying, modifying or distributing this software, you acknowledge
++ * that you have read and understood your obligations described above,
++ * and agree to abide by those obligations.
++ *
++ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
++ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
++ * COMPLETENESS OR PERFORMANCE.
++ */
++
++#ifndef LLPREFSDB_H
++#define LLPREFSDB_H
++
++#include "llpanel.h"
++
++class LLPanelDatabase : public LLPanel
++{
++public:
++ LLPanelDatabase();
++ virtual ~LLPanelDatabase() {};
++
++ virtual BOOL postBuild();
++ virtual void draw();
++ virtual void refresh();
++
++ void apply();
++ void cancel();
++
++ static void onClickChangeAvatar(void *userdata);
++ static void onPickAvatar(const std::vector<std::string>& names,
++ const std::vector<LLUUID>& ids,
++ void* user_data);
++
++protected:
++ std::string mAvatarName;
++ LLUUID mAvatarKey;
++ std::string mURL;
++ std::string mUsername;
++ std::string mPassword;
++ std::string mSendPattern;
++ std::string mPositivePattern;
++ std::string mNegativePattern;
++ std::string mDeniedPattern;
++
++};
++
++
++#endif // LL_PREFSDB_H
+diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
+index 5f25dc3..95e9f80 100644
+--- a/indra/newview/llstartup.cpp
++++ b/indra/newview/llstartup.cpp
+@@ -3034,7 +3034,7 @@ void register_viewer_callbacks(LLMessageSystem* msg)
+ msg->setHandlerFunc("ParcelDwellReply",
+ LLViewerParcelMgr::processParcelDwellReply);
+
+- msg->setHandlerFunc("AvatarPropertiesReply",
++ msg->addHandlerFunc("AvatarPropertiesReply",
+ LLPanelAvatar::processAvatarPropertiesReply);
+ msg->setHandlerFunc("AvatarInterestsReply",
+ LLPanelAvatar::processAvatarInterestsReply);
+diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp
+index 8202191..1d4c1b2 100644
+--- a/indra/newview/lltoolbar.cpp
++++ b/indra/newview/lltoolbar.cpp
+@@ -49,6 +49,7 @@
+ #include "llvoavatar.h"
+ #include "lltooldraganddrop.h"
+ #include "llinventoryview.h"
++#include "llfloateravatarlist.h"
+ #include "llfloaterchatterbox.h"
+ #include "llfloaterfriends.h"
+ #include "llfloatersnapshot.h"
+@@ -154,6 +155,9 @@ BOOL LLToolBar::postBuild()
+ childSetAction("inventory_btn", onClickInventory, this);
+ childSetControlName("inventory_btn", "ShowInventory");
+
++ childSetAction("avatar_list_btn", onClickAvatarList, this);
++ childSetControlName("inventory_btn", "ShowAvatarList");
++
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+@@ -527,3 +531,8 @@ void LLToolBar::onClickInventory(void*)
+ handle_inventory(NULL);
+ }
+
++// static
++void LLToolBar::onClickAvatarList(void*)
++{
++ LLFloaterAvatarList::toggle(NULL);
++}
+diff --git a/indra/newview/lltoolbar.h b/indra/newview/lltoolbar.h
+index 720b9c0..b8f8c78 100644
+--- a/indra/newview/lltoolbar.h
++++ b/indra/newview/lltoolbar.h
+@@ -82,6 +82,7 @@ public:
+ static void onClickRadar(void* data);
+ static void onClickMap(void* data);
+ static void onClickInventory(void* data);
++ static void onClickAvatarList(void* data);
+
+ static F32 sInventoryAutoOpenTime;
+
+diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
+index 023c758..fb198d1 100644
+--- a/indra/newview/llviewermenu.cpp
++++ b/indra/newview/llviewermenu.cpp
+@@ -201,6 +201,7 @@
+ #include "pipeline.h"
+ #include "llappviewer.h"
+ #include "roles_constants.h"
++#include "llfloateravatarlist.h"
+ #include "llviewerjoystick.h"
+ #include "llwlanimator.h"
+ #include "llwlparammanager.h"
+@@ -5269,6 +5270,10 @@ class LLShowFloater : public view_listener_t
+ {
+ LLFloaterAbout::show(NULL);
+ }
++ else if (floater_name == "avatar list")
++ {
++ LLFloaterAvatarList::toggle(NULL);
++ }
+ else if (floater_name == "active speakers")
+ {
+ LLFloaterActiveSpeakers::toggleInstance(LLSD());
+@@ -7415,6 +7420,15 @@ static void addMenu(view_listener_t *menu, const std::string& name)
+ menu->registerListener(gMenuHolder, name);
+ }
+
++class LLViewAvatarList : public view_listener_t
++{
++ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
++ {
++ llinfos << "LLViewAvatarList::handleEvent()" << llendl;
++ return true;
++ }
++};
++
+ void initialize_menus()
+ {
+ // File menu
+diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
+index 461a598..03b0a97 100644
+--- a/indra/newview/llviewermessage.cpp
++++ b/indra/newview/llviewermessage.cpp
+@@ -135,6 +135,9 @@
+ #include "llviewerdisplay.h"
+ #include "llkeythrottle.h"
+
++#include "llpanelavatar.h"
++#include "llfloateravatarlist.h"
++
+ #include <boost/tokenizer.hpp>
+
+ #if LL_WINDOWS // For Windows specific error handler
+diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
+index 340f279..059a877 100644
+--- a/indra/newview/llviewerobject.h
++++ b/indra/newview/llviewerobject.h
+@@ -598,8 +598,9 @@ protected:
+ TPACKETID mLatestRecvPacketID; // Latest time stamp on message from simulator
+ // extra data sent from the sim...currently only used for tree species info
+ U8* mData;
+-
++public:
+ LLPointer<LLViewerPartSourceScript> mPartSourcep; // Particle source associated with this object.
++protected:
+ LLAudioSourceVO* mAudioSourcep;
+ F32 mAudioGain;
+
+diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
+index 1a9a208..e20bce9 100644
+--- a/indra/newview/llviewerwindow.cpp
++++ b/indra/newview/llviewerwindow.cpp
+@@ -106,6 +106,7 @@
+ #include "llfloatersnapshot.h"
+ #include "llfloatertools.h"
+ #include "llfloaterworldmap.h"
++#include "llfloateravatarlist.h"
+ #include "llfocusmgr.h"
+ #include "llframestatview.h"
+ #include "llgesturemgr.h"
+@@ -1866,9 +1867,14 @@ void LLViewerWindow::initWorldUI()
+ gMorphView->setVisible(FALSE);
+
+ // *Note: this is where gFloaterMute used to be initialized.
+-
++ gFloaterAvatarList = NULL;
+ LLWorldMapView::initClass();
+
++ gFloaterAvatarList = new LLFloaterAvatarList();
++ gFloaterAvatarList->setVisible(FALSE);
++
++ LLWorldMapView::initClass();
++
+ adjust_rect_centered_partial_zoom("FloaterWorldMapRect2", full_window);
+
+ gFloaterWorldMap = new LLFloaterWorldMap();
+diff --git a/indra/newview/llwebbrowserctrl.h b/indra/newview/llwebbrowserctrl.h
+index e4ecea7..13332cf 100644
+--- a/indra/newview/llwebbrowserctrl.h
++++ b/indra/newview/llwebbrowserctrl.h
+@@ -32,6 +32,8 @@
+ #ifndef LL_LLWEBBROWSERCTRL_H
+ #define LL_LLWEBBROWSERCTRL_H
+
++#include "lluictrlfactory.h"
++
+ ////////////////////////////////////////////////////////////////////////////////
+ // data class that is passed with an event
+ class LLWebBrowserCtrlEvent
+diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
+index 13d8d09..9cc95d1 100644
+--- a/indra/newview/pipeline.cpp
++++ b/indra/newview/pipeline.cpp
+@@ -78,6 +78,7 @@
+ #include "llviewerimagelist.h"
+ #include "llviewerobject.h"
+ #include "llviewerobjectlist.h"
++#include "llviewerpartsource.h"
+ #include "llviewerparcelmgr.h"
+ #include "llviewerregion.h" // for audio debugging.
+ #include "llviewerwindow.h" // For getSpinAxis
+@@ -92,6 +93,7 @@
+ #include "llvopartgroup.h"
+ #include "llworld.h"
+ #include "llcubemap.h"
++#include "llfloateravatarlist.h"
+ #include "lldebugmessagebox.h"
+ #include "llviewershadermgr.h"
+ #include "llviewerjoystick.h"
+@@ -2009,6 +2011,27 @@ void renderSoundHighlights(LLDrawable* drawablep)
+ }
+ }
+
++/**
++ * @brief Add particle sources to avatar list
++ * This tells the avatar list floater who is emitting particles
++ */
++void addParticleSourcesToList(LLDrawable *drawablep)
++{
++ if ( NULL != gFloaterAvatarList )
++ {
++ LLViewerObject *vobj = drawablep->getVObj();
++ if (vobj && vobj->isParticleSource())
++ {
++ LLUUID id = vobj->mPartSourcep->getOwnerUUID();
++ LLAvatarListEntry *ent = gFloaterAvatarList->getAvatarEntry(id);
++ if ( NULL != ent )
++ {
++ ent->setActivity(ACTIVITY_PARTICLES);
++ }
++ }
++ }
++}
++
+ void LLPipeline::postSort(LLCamera& camera)
+ {
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+@@ -2109,6 +2132,8 @@ void LLPipeline::postSort(LLCamera& camera)
+ std::sort(sCull->beginAlphaGroups(), sCull->endAlphaGroups(), LLSpatialGroup::CompareDepthGreater());
+ }
+
++ forAllVisibleDrawables(addParticleSourcesToList);
++
+ // only render if the flag is set. The flag is only set if we are in edit mode or the toggle is set in the menus
+ if (gSavedSettings.getBOOL("BeaconAlwaysOn"))
+ {
+@@ -2157,6 +2182,23 @@ void LLPipeline::postSort(LLCamera& camera)
+ }
+ }
+
++ // Avatar list support
++ if ( gFloaterAvatarList && gAudiop )
++ {
++ LLAudioEngine::source_map::iterator iter;
++ for (iter = gAudiop->mAllSources.begin(); iter != gAudiop->mAllSources.end(); ++iter)
++ {
++ LLAudioSource *sourcep = iter->second;
++ LLUUID uuid = sourcep->getOwnerID();
++ LLAvatarListEntry *ent = gFloaterAvatarList->getAvatarEntry(uuid);
++
++ if ( ent )
++ {
++ ent->setActivity(ACTIVITY_SOUND);
++ }
++ }
++ }
++
+ // If managing your telehub, draw beacons at telehub and currently selected spawnpoint.
+ if (LLFloaterTelehub::renderBeacons())
+ {
+diff --git a/indra/newview/skins/default/xui/en-us/alerts.xml b/indra/newview/skins/default/xui/en-us/alerts.xml
+index ff7a99c..18c0f9c 100644
+--- a/indra/newview/skins/default/xui/en-us/alerts.xml
++++ b/indra/newview/skins/default/xui/en-us/alerts.xml
+@@ -1374,6 +1374,23 @@ chat, or interact with the world.
+ Cancel
+ </option>
+ </alert>
++ <alert modal="true" name="AvatarListFreezeAvatars">
++ <message name="message">
++ Freeze these avatars?
++ [NAMES]
++ They will temporarily be unable to move,
++ chat, or interact with the world.
++ </message>
++ <option name="Freeze">
++ Freeze
++ </option>
++ <option name="Unfreeze">
++ Unfreeze
++ </option>
++ <option name="Cancel">
++ Cancel
++ </option>
++ </alert>
+ <alert modal="true" name="EjectAvatar">
+ <message name="message">
+ Eject this avatar from your land?
+@@ -1402,6 +1419,52 @@ chat, or interact with the world.
+ Cancel
+ </option>
+ </alert>
++ <alert modal="true" name="AvatarListEjectAvatars">
++ <message name="message">
++ Eject these avatars from your land?
++ [NAMES]
++ </message>
++ <option name="Eject">
++ Eject
++ </option>
++ <option name="EjectandBan">
++ Eject and Ban
++ </option>
++ <option name="Cancel">
++ Cancel
++ </option>
++ </alert>
++ <alert modal="true" name="AvatarListEjectAvatarsFromEstate">
++ <message name="message">
++ Eject these avatars from this estate?
++ [NAMES]
++ </message>
++ <option name="Eject">
++ Eject
++ </option>
++ <option name="EjectandBan">
++ Eject and Ban
++ </option>
++ <option name="Cancel">
++ Cancel
++ </option>
++ </alert>
++
++ <alert modal="true" name="AvatarListMuteAvatars">
++ <message name="message">
++ Mute these avatars?
++ [NAMES]
++ </message>
++ <option name="Mute">
++ Mute
++ </option>
++ <option name="Unmute">
++ Unmute
++ </option>
++ <option name="Cancel">
++ Cancel
++ </option>
++ </alert>
+ <alert modal="true" name="EjectAvatarNoBan">
+ <message name="message">
+ Eject this avatar from your land?
+@@ -4911,6 +4974,78 @@ they are part of an attachment.
+ No
+ </option>
+ </alert>
++ <!-- Luskwood functionality -->
++ <alert modal="true" name="LuskwoodGohomerWarn">
++ <message name="message">
++ Warn this avatar?
++
++ This will show them a dialog asking to confirm that they understand
++ their behavior was found to be undesirable, and that they face
++ ejection or banning if it continues.
++ </message>
++ <option name="Warn">
++ Warn
++ </option>
++ <option default="true" name="Cancel">
++ Cancel
++ </option>
++ </alert>
++ <alert modal="true" name="LuskwoodGohomerEject">
++ <message name="message">
++ Eject this avatar?
++
++ This avatar will be ejected from the area, but will be able to
++ return immediately.
++ </message>
++ <option name="Eject">
++ Eject
++ </option>
++ <option default="true" name="Cancel">
++ Cancel
++ </option>
++ </alert>
++ <alert modal="true" name="LuskwoodGohomerKeepAway">
++ <message name="message">
++ Eject this avatar and ban them for 2 hours?
++
++ This avatar will be ejected from the area, and will be added to
++ the parcel's ban list for 2 hours.
++ </message>
++ <option name="KeepAway">
++ Keep Away
++ </option>
++ <option default="true" name="Cancel">
++ Cancel
++ </option>
++ </alert>
++ <alert modal="true" name="LuskwoodGohomerSendHome">
++ <message name="message">
++ Send this avatar home and ban them for 4 hours?
++
++ This avatar will be sent home, and will be added to the parcel's
++ ban list for 4 hours.
++ </message>
++ <option name="KeepAway">
++ Send Home
++ </option>
++ <option default="true" name="Cancel">
++ Cancel
++ </option>
++ </alert>
++ <alert modal="true" name="LuskwoodGohomerOff">
++ <message name="message">
++ Turn off the gohomer?
++
++ This will make it stop trying to send the last ejected avatar home,
++ but won't remove them from the ban list.
++ </message>
++ <option name="TurnOff">
++ Turn Off
++ </option>
++ <option default="true" name="Cancel">
++ Cancel
++ </option>
++ </alert>
+ <alert modal="true" name="BadURL">
+ <message name="message">
+ Second Life doesn't know how to handle the link:
+diff --git a/indra/newview/skins/default/xui/en-us/floater_avatar_scanner.xml b/indra/newview/skins/default/xui/en-us/floater_avatar_scanner.xml
+new file mode 100644
+index 0000000..7156aa4
+--- /dev/null
++++ b/indra/newview/skins/default/xui/en-us/floater_avatar_scanner.xml
+@@ -0,0 +1,413 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes"?>
++<floater
++ name="avatar list"
++ title="Avatar List"
++ can_resize="true"
++ can_minimize="true"
++ can_close="true"
++ can_drag_on_left="false"
++ rect_control="FloaterAvatarListRect"
++ min_width="400"
++ min_height="300"
++ >
++ <!-- <text
++ name="help_label"
++ bottom="-30"
++ left="15"
++ font="SansSerifSmall"
++ follows="top|left"
++ >
++ </text> -->
++
++ <scroll_list
++ name="avatar_list"
++ left="10"
++ right="-10"
++ top="-20"
++ bottom="150"
++ column_padding="0"
++ can_resize="true"
++ follows="left|top|bottom|right"
++ draw_heading="true"
++ multi_select="true"
++ search_column="1"
++ tool_tip="Hold shift or control while clicking to select multiple avatars"
++ >
++ <!-- Icons relating to the avatar, currently: tracking, and marked -->
++ <column name="avatar_icon" label="Icon" width="20" />
++ <column name="avatar_name" label="Name" dynamicwidth="true" />
++ <column name="distance" label="Distance" width="70"/>
++ <column name="age" label="Age" width="50"/>
++ <!-- <column name="payment_data" label="Payment" width="80"/> -->
++ <!-- TrustNet Score -->
++ <!-- <column name="score" label="Score" width="10"/> -->
++ <!-- Payment data icons -->
++ <column name="payment_data" label="" width="20" />
++ <!-- What the avatar is doing: producing sounds, rezzing, particles, etc -->
++ <column name="activity" label="" width="20" />
++ <!-- Time when avatar entered -->
++ <column name="entered" label="Entered" width="60"/>
++ </scroll_list>
++<!-- <panel
++ name="scanner_options"
++ left="10"
++ right="-10"
++ height="45"
++ bottom="140"
++ border="true"
++ mouse_opaque="true"
++ bevel_style="in"
++ background_visible="true"
++ background_opaque="true"
++ follows="left|right|bottom"
++ bg_opaque_color="0,0,0,0.3"
++ bg_alpha_color="blue"
++ can_resize="false"
++ >
++
++ </panel> -->
++
++ <tab_container border="false" bottom_delta="-130" height="120" left="10" mouse_opaque="false"
++ name="actions_tab_container" tab_position="top" follows="left|right|bottom">
++ <panel border="true" bottom_delta="-130" follows="left|top|right|bottom" height="255"
++ label="Avatar" left="1" mouse_opaque="true"
++ name="actions_tab" width="398">
++ <!-- upper row -->
++ <button
++ name="profile_btn"
++ label="Profile"
++ tool_tip="Show picture, groups, and other information"
++ left="10"
++ bottom_delta="-190"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="im_btn"
++ label="IM"
++ tool_tip="Open Instant Message session"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++
++
++ <!--middle row -->
++ <button
++ name="track_btn"
++ label="Track"
++ tool_tip="Track this avatar's position"
++ left="10"
++ bottom_delta="-24"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="mark_btn"
++ label="Mark"
++ tool_tip="Mark this avatar in the list"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="get_key_btn"
++ label="Get Key"
++ tool_tip="Copies avatar's key to the clipboard"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="teleport_btn"
++ label="Teleport"
++ tool_tip="Teleport to avatar's position"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <!-- lower row-->
++ <button
++ name="prev_in_list_btn"
++ label="Prev"
++ tool_tip="Focus camera on previous avatar in list"
++ left="10"
++ bottom_delta="-24"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="next_in_list_btn"
++ label="Next"
++ tool_tip="Focus camera on next avatar in list"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="prev_marked_btn"
++ label="Prev Marked"
++ tool_tip="Focus camera on previous marked avatar"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="next_marked_btn"
++ label="Next Marked"
++ tool_tip="Focus camera on next marked avatar"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++
++ </panel>
++ <panel border="true" bottom_delta="-130" follows="left|top|right|bottom" height="255"
++ label="Moderation" left="1" mouse_opaque="true"
++ name="land_tab" width="398">
++
++ <!-- Upper row -->
++ <button
++ name="freeze_btn"
++ label="Freeze"
++ tool_tip="Freeze the avatar, preventing it from moving"
++ left="10"
++ bottom_delta="-190"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="eject_btn"
++ label="Eject"
++ tool_tip="Eject the avatar from the parcel"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++
++ <button
++ name="mute_btn"
++ label="Mute"
++ tool_tip="Mute this avatar"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++
++
++ <button
++ name="ar_btn"
++ label="AR"
++ tool_tip="Report abuse on this avatar"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++
++ <!-- middle row -->
++ <button
++ name="estate_eject_btn"
++ label="Eject from estate"
++ tool_tip="Eject this avatar from the estate"
++ left="10"
++ bottom_delta="-24"
++ width="170"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ </panel>
++
++ <panel border="true" bottom_delta="-130" follows="left|top|right|bottom" height="255"
++ label="TrustNet" left="1" mouse_opaque="true"
++ name="trustnet_tab" width="398">
++ <!--TrustNet tools -->
++ <!-- upper row -->
++ <button
++ name="tn_rate_btn"
++ label="Rate"
++ tool_tip="Give a TrustNet rating to this avatar"
++ left="10"
++ bottom_delta="-190"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="tn_explain_btn"
++ label="Explain"
++ tool_tip="Explain this avatar's score"
++ left="100"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <!--lower row-->
++ <button
++ name="tn_website_btn"
++ label="Web"
++ tool_tip="Open a web browser with the TrustNet website"
++ left="10"
++ bottom_delta="-24"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="tn_password_btn"
++ label="Password"
++ tool_tip="Get a password for the TrustNet website. A new password is generated every time."
++ left="100"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="tn_renew_btn"
++ label="Renew"
++ tool_tip="Renew the TrustNet subscription"
++ left="190"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++
++ </panel>
++ <panel border="true" bottom_delta="-130" follows="left|top|right|bottom" height="255"
++ label="Luskwood" left="1" mouse_opaque="true"
++ name="luskwood_tab" width="398">
++ <!--Luskwood tools, upper row-->
++ <button
++ name="gowarn_btn"
++ label="Warn"
++ tool_tip="Give a warning to this avatar"
++ left="10"
++ bottom_delta="-190"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="goeject_btn"
++ label="Eject"
++ tool_tip="Eject this avatar"
++ left="100"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="goaway_btn"
++ label="Keep away"
++ tool_tip="Eject, and keep this avatar away for 2 hours"
++ left="190"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="gohome_btn"
++ label="Send home"
++ tool_tip="Send home, and keep this avatar away for 4 hours"
++ left="280"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <!-- Middle row -->
++ <button
++ name="gokey_btn"
++ label="Ban by key"
++ tool_tip="Turn off the send home function"
++ left="10"
++ bottom_delta="-24"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ <button
++ name="gohomeoff_btn"
++ label="Off"
++ tool_tip="Turn off the send home function"
++ left_delta="90"
++ bottom_delta="0"
++ width="80"
++ height="20"
++ font="SansSerifSmall"
++ follows="bottom|left"
++ />
++ </panel>
++ <panel border="true" bottom_delta="-150" follows="left|top|right|bottom" height="255"
++ label="Options" left="1" mouse_opaque="true"
++ name="options_tab" width="398">
++ <check_box height="16" label="Update"
++ left="10" bottom_delta="-190" name="update_enabled_cb"
++ width="200" follows="bottom|left"
++ hidden="false" mouse_opaque="true" font="SansSerifSmall"
++ initial_value="true" enabled="true" radio_style="false"
++ tool_tip="Set whether the avatar list should update" />
++
++ <check_box height="16" label="Get avatar info"
++ left="20" bottom_delta="-21" name="fetch_avdata_enabled_cb"
++ width="200" follows="bottom|left"
++ hidden="false" mouse_opaque="true" font="SansSerifSmall"
++ initial_value="true" enabled="true" radio_style="false"
++ tool_tip="Set whether avatar information should be retrieved" />
++ </panel>
++ </tab_container>
++
++</floater>
+diff --git a/indra/newview/skins/default/xui/en-us/menu_viewer.xml b/indra/newview/skins/default/xui/en-us/menu_viewer.xml
+index d2c4923..9971454 100644
+--- a/indra/newview/skins/default/xui/en-us/menu_viewer.xml
++++ b/indra/newview/skins/default/xui/en-us/menu_viewer.xml
+@@ -221,6 +221,13 @@
+ mouse_opaque="true" name="Preferences..." shortcut="control|P" width="153">
+ <on_click function="ShowFloater" userdata="preferences" />
+ </menu_item_call>
++ <menu_item_separator bottom="-46" enabled="true" height="8" hidden="false" label="-----------"
++ left="0" mouse_opaque="true" name="separator6" width="211" />
++ <menu_item_check bottom="-65" enabled="true" height="19" hidden="false" label="Avatar List" left="0" mouse_opaque="true" name="Avatar List" width="211">
++ <on_click function="ShowFloater" userdata="avatar list" />
++ <on_check function="FloaterVisible" userdata="avatar list" />
++ </menu_item_check>
++
+ </menu>
+ <menu bottom="-1" create_jump_keys="true" drop_shadow="true" enabled="true"
+ height="537" label="View" left="80" mouse_opaque="false" name="View"
+@@ -398,6 +405,12 @@
+ mouse_opaque="true" name="Set UI Size to Default" width="188">
+ <on_click function="View.DefaultUISize" userdata="" />
+ </menu_item_call>
++ <menu_item_separator bottom="-46" enabled="true" height="8" hidden="false" label="-----------"
++ left="0" mouse_opaque="true" name="separator6" width="211" />
++ <menu_item_check bottom="-65" enabled="true" height="19" hidden="false" label="Avatar List" left="0" mouse_opaque="true" name="Avatar List" width="211">
++ <on_click function="ShowFloater" userdata="avatar list" />
++ <on_check function="FloaterVisible" userdata="avatar list" />
++ </menu_item_check>
+ </menu>
+ <menu bottom="-18" create_jump_keys="true" drop_shadow="true" enabled="true"
+ height="339" label="World" left="0" mouse_opaque="false" name="World"
+diff --git a/indra/newview/skins/default/xui/en-us/notify.xml b/indra/newview/skins/default/xui/en-us/notify.xml
+index 63396af..cf0056b 100644
+--- a/indra/newview/skins/default/xui/en-us/notify.xml
++++ b/indra/newview/skins/default/xui/en-us/notify.xml
+@@ -915,6 +915,12 @@ You can find example sculpted textures in the inventory library.
+ from this list.
+ </message>
+ </notify>
++ <!--Database preferences panel -->
++ <notify name="AddSelfDatabase" tip="false">
++ <message name="message">
++ You cannot use yourself as the database avatar.
++ </message>
++ </notify>
+ <notify name="VoiceInviteP2P" tip="false" unique="true">
+ <message name="message">
+ [NAME] is inviting you to a Voice Chat call.
+diff --git a/indra/newview/skins/default/xui/en-us/panel_preferences_database.xml b/indra/newview/skins/default/xui/en-us/panel_preferences_database.xml
+new file mode 100644
+index 0000000..314b02d
+--- /dev/null
++++ b/indra/newview/skins/default/xui/en-us/panel_preferences_database.xml
+@@ -0,0 +1,166 @@
++<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
++<panel border="true" bottom="-409" enabled="true" follows="left|top|right|bottom"
++ height="408" hidden="false" label="Database" left="102" mouse_opaque="true"
++ name="avatar_db" width="517">
++
++ <!-- Options section -->
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom="-30" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" hidden="false"
++ left="12" mouse_opaque="false" name="text_box" v_pad="0" width="300">
++ Options:
++ </text>
++
++ <check_box bottom_delta="0" control_name="DBEnabled" enabled="true"
++ follows="left|top" font="SansSerifSmall" height="16" hidden="false"
++ initial_value="false" label="Database enabled" left="148"
++ mouse_opaque="true" name="database_enabled_cb" radio_style="false"
++ width="256" />
++
++ <radio_group bottom_delta="-110" control_name="DBRetrievalMode" draw_border="true" enabled="true"
++ follows="left|top" height="100" hidden="false" left="148"
++ mouse_opaque="true" name="Database access type" width="321">
++ <radio_item type="string" length="1" bottom="-20" enabled="true" follows="left|top" height="16" hidden="false"
++ left="3" mouse_opaque="true" name="UseAvatar" width="315">
++ Avatar:
++ </radio_item>
++ <radio_item type="string" length="1" bottom_delta="-10" enabled="false" follows="left|top" height="16" hidden="false"
++ left="3" mouse_opaque="true" name="UseURL" width="315">
++ URL:
++ </radio_item>
++ </radio_group>
++
++ <!-- HACK: apparently line_editor can't go inside a radio_group, so we make it appear
++ inside while not actually being in there -->
++ <button bottom_delta="78" follows="right|bottom" font="SansSerif" halign="center"
++ height="20" label="Change Avatar" label_selected="Change Avatar" left="260"
++ mouse_opaque="true" name="change_avatar_btn" width="100" />
++
++ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="0"
++ follows="left|top|right" font="SansSerifSmall" height="16" hidden="false"
++ left="348" max_length="254" mouse_opaque="true" name="db_avatar"
++ select_on_focus="false" width="130" control_name="DBAvatar" enabled="false"/>
++
++ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="-20"
++ follows="left|top|right" font="SansSerifSmall" height="16" hidden="false"
++ left="248" max_length="254" mouse_opaque="true" name="db_url"
++ select_on_focus="false" width="230" enabled="false" control_name="DBURL" />
++
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-20" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" hidden="false"
++ left="170" mouse_opaque="false" name="text_box" v_pad="0" width="300">
++ Username:
++ </text>
++
++ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="0"
++ follows="left|top|right" font="SansSerifSmall" height="16" hidden="false"
++ left="248" max_length="254" mouse_opaque="true" name="db_url_username"
++ select_on_focus="false" width="230" enabled="false" control_name="DBURLUsername" />
++
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-20" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" hidden="false"
++ left="170" mouse_opaque="false" name="text_box" v_pad="0" width="300">
++ Password:
++ </text>
++
++ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="0"
++ follows="left|top|right" font="SansSerifSmall" height="16" hidden="false"
++ left="248" max_length="254" mouse_opaque="true" name="db_url_password"
++ select_on_focus="false" width="230" enabled="false" control_name="DBURLPassword" />
++
++
++ <!--Patterns section-->
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-30" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" hidden="false"
++ left="12" mouse_opaque="false" name="text_box" v_pad="0" width="300">
++ Patterns:
++ </text>
++
++
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-20" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" hidden="false"
++ left="148" mouse_opaque="false" name="text_box2" v_pad="0" width="300">
++ Send:
++ </text>
++
++ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="0"
++ follows="left|top|right" font="SansSerifSmall" height="16" hidden="false"
++ left="248" max_length="254" mouse_opaque="true" name="send_pattern"
++ select_on_focus="false" width="230" control_name="DBSendPattern" >
++ $NAME
++ </line_editor>
++
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-20" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" hidden="false"
++ left="148" mouse_opaque="false" name="text_box2" v_pad="0" width="300">
++ Positive contains:
++ </text>
++
++ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="0"
++ follows="left|top|right" font="SansSerifSmall" height="16" hidden="false"
++ left="248" max_length="254" mouse_opaque="true" name="positive_pattern"
++ select_on_focus="false" width="230" control_name="DBPositivePattern" />
++
++
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-20" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" hidden="false"
++ left="148" mouse_opaque="false" name="text_box2" v_pad="0" width="300">
++ Negative contains:
++ </text>
++
++ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="0"
++ follows="left|top|right" font="SansSerifSmall" height="16" hidden="false"
++ left="248" max_length="254" mouse_opaque="true" name="negative_pattern"
++ select_on_focus="false" width="230" control_name="DBNegativePattern" />
++
++
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-20" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" hidden="false"
++ left="148" mouse_opaque="false" name="text_box2" v_pad="0" width="300">
++ Denied contains:
++ </text>
++
++ <line_editor bevel_style="in" border_style="line" border_thickness="1" bottom_delta="0"
++ follows="left|top|right" font="SansSerifSmall" height="16" hidden="false"
++ left="248" max_length="254" mouse_opaque="true" name="denied_pattern"
++ select_on_focus="false" width="230" control_name="DBDeniedPattern" />
++
++ <!--Timeouts section-->
++ <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
++ bottom_delta="-30" drop_shadow_visible="true" enabled="true" follows="left|top"
++ font="SansSerifSmall" h_pad="0" halign="left" height="10" hidden="false"
++ left="12" mouse_opaque="false" name="text_box" v_pad="0" width="300">
++ Timeouts:
++ </text>
++
++ <spinner bottom_delta="-20" control_name="DBMaxConcurrentRequests" decimal_digits="0" enabled="true"
++ follows="left|top" height="16" hidden="false" increment="1"
++ initial_val="4" label="Max concurrent requests:" label_width="138" left="148"
++ max_val="32" min_val="1" mouse_opaque="true" name="max_concurrent_requests"
++ width="202" />
++
++ <spinner bottom_delta="-20" control_name="DBDelayBetweenRequests" decimal_digits="0" enabled="true"
++ follows="left|top" height="16" hidden="false" increment="1"
++ initial_val="1" label="Delay between requests:" label_width="138" left="148"
++ max_val="32" min_val="0" mouse_opaque="true" name="delay_between_requests"
++ width="202" />
++
++ <spinner bottom_delta="-20" control_name="DBRequestTimeout" decimal_digits="0" enabled="true"
++ follows="left|top" height="16" hidden="false" increment="4"
++ initial_val="64" label="Request timeout:" label_width="138" left="148"
++ max_val="65536" min_val="16" mouse_opaque="true" name="request_timeout"
++ width="202" />
++
++ <spinner bottom_delta="-20" control_name="DBGiveUpAfter" decimal_digits="0" enabled="true"
++ follows="left|top" height="16" hidden="false" increment="64"
++ initial_val="4096" label="Give up after:" label_width="138" left="148"
++ max_val="65536" min_val="64" mouse_opaque="true" name="give_up_after"
++ width="202" />
++</panel>
+\ No newline at end of file
+diff --git a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml
+index 9c57be0..b264e6e 100644
+--- a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml
++++ b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml
+@@ -18,6 +18,12 @@
+ list_position="above"
+ width="50" follows="left|right" user_resize="false"/>
+ <icon image_name="spacer24.tga" width="2" height="2" follows="left|right" auto_resize="false" color="0,0,0,0"/>
++ <button bottom="0" font="SansSerif" height="24" label="Avatars" left="0"
++ name="avatar_list_btn" tool_tip="List of nearby avatars" width="50"
++ image_selected="toolbar_btn_selected.tga"
++ image_unselected="toolbar_btn_enabled.tga" scale_image="true"
++ follows="left|right" user_resize="false" />
++ <icon image_name="spacer24.tga" width="2" height="2" follows="left|right" auto_resize="false" color="0,0,0,0"/>
+ <button bottom="0" font="SansSerif" height="24" label="Fly"
+ image_overlay="icn_toolbar_fly.tga" image_overlay_alignment="left"
+ image_selected="toolbar_btn_selected.tga"
+@@ -68,3 +74,4 @@
+ </layout_stack>
+
+ </panel>
++
+--
+tg: (264f6be..) topic/features/jira-backported/avatar_list (depends on: master)
diff --git a/debian/patches/topic/features/jira-sent/24_always_test_vectorize.diff b/debian/patches/topic/features/jira-sent/24_always_test_vectorize.diff
new file mode 100644
index 0000000..c83b23e
--- /dev/null
+++ b/debian/patches/topic/features/jira-sent/24_always_test_vectorize.diff
@@ -0,0 +1,39 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/features/jira-sent/24_always_test_vectorize
+
+Always test to see if CPU has sse/altivect expernsions, regardless of compile host.
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/llappviewer.cpp | 8 --------
+ 1 files changed, 0 insertions(+), 8 deletions(-)
+
+diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
+index 09d1141..e5db7e9 100644
+--- a/indra/newview/llappviewer.cpp
++++ b/indra/newview/llappviewer.cpp
+@@ -416,7 +416,6 @@ static void settings_modify()
+ gDebugGL = gSavedSettings.getBOOL("RenderDebugGL");
+ gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline");
+
+-#if LL_VECTORIZE
+ if (gSysCPU.hasAltivec())
+ {
+ gSavedSettings.setBOOL("VectorizeEnable", TRUE );
+@@ -442,13 +441,6 @@ static void settings_modify()
+ gSavedSettings.setU32("VectorizeProcessor", 0 );
+ gSavedSettings.setBOOL("VectorizeSkin", FALSE);
+ }
+-#else
+- // This build target doesn't support SSE, don't test/run.
+- gSavedSettings.setBOOL("VectorizePerfTest", FALSE );
+- gSavedSettings.setBOOL("VectorizeEnable", FALSE );
+- gSavedSettings.setU32("VectorizeProcessor", 0 );
+- gSavedSettings.setBOOL("VectorizeSkin", FALSE);
+-#endif
+ }
+
+ void LLAppViewer::initGridChoice()
+--
+tg: (2c826ba..) topic/features/jira-sent/24_always_test_vectorize (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-backported/VWR-11103-FIx_local_ruler_on_linked_objects.diff b/debian/patches/topic/fixes/jira-backported/VWR-11103-FIx_local_ruler_on_linked_objects.diff
new file mode 100644
index 0000000..44116d1
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-backported/VWR-11103-FIx_local_ruler_on_linked_objects.diff
@@ -0,0 +1,28 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-backported/VWR-11103-FIx_local_ruler_on_linked_objects
+
+<patch description>
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/llselectmgr.cpp | 4 ++++
+ 1 files changed, 4 insertions(+), 0 deletions(-)
+
+diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
+index 0f5a98a..139c210 100644
+--- a/indra/newview/llselectmgr.cpp
++++ b/indra/newview/llselectmgr.cpp
+@@ -5501,6 +5501,10 @@ void LLSelectMgr::updateSelectionCenter()
+
+ std::vector < LLViewerObject *> jointed_objects;
+
++ // Initialize the bounding box to the root prim, so the BBox orientation
++ // matches the root prim's (affecting the orientation of the manipulators).
++ bbox.addBBoxAgent( (mSelectedObjects->getFirstRootObject(TRUE))->getBoundingBoxAgent() );
++
+ for (LLObjectSelection::iterator iter = mSelectedObjects->begin();
+ iter != mSelectedObjects->end(); iter++)
+ {
+--
+tg: (2c826ba..) topic/fixes/jira-backported/VWR-11103-FIx_local_ruler_on_linked_objects (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-backported/VWR-11674_dont_download_muted_sounds.diff b/debian/patches/topic/fixes/jira-backported/VWR-11674_dont_download_muted_sounds.diff
new file mode 100644
index 0000000..37b80a1
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-backported/VWR-11674_dont_download_muted_sounds.diff
@@ -0,0 +1,62 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-backported/VWR-11674_dont_download_muted_sounds
+
+<patch description>
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llaudio/audioengine.cpp | 3 ++-
+ indra/newview/llviewermessage.cpp | 9 ++++++++-
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/indra/llaudio/audioengine.cpp b/indra/llaudio/audioengine.cpp
+index c5bc367..2713de6 100644
+--- a/indra/llaudio/audioengine.cpp
++++ b/indra/llaudio/audioengine.cpp
+@@ -873,7 +873,8 @@ void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_i
+ // Create a new source (since this can't be associated with an existing source.
+ //llinfos << "Localized: " << audio_uuid << llendl;
+
+- if (mMuted)
++ //If we cannot hear it, dont even try to load the sound.
++ if (mMuted || gain == 0.0)
+ {
+ return;
+ }
+diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
+index 461a598..1e4d8d2 100644
+--- a/indra/newview/llviewermessage.cpp
++++ b/indra/newview/llviewermessage.cpp
+@@ -3285,6 +3285,10 @@ void process_sound_trigger(LLMessageSystem *msg, void **)
+ msg->getVector3Fast(_PREHASH_SoundData, _PREHASH_Position, pos_local);
+ msg->getF32Fast(_PREHASH_SoundData, _PREHASH_Gain, gain);
+
++ //If we have sounds muted, don't even try to load or trigger the sound.
++ if(gSavedSettings.getBOOL("MuteSounds") || gain == 0.0)
++ return;
++
+ // adjust sound location to true global coords
+ LLVector3d pos_global = from_region_handle(region_handle);
+ pos_global.mdV[VX] += pos_local.mV[VX];
+@@ -3313,7 +3317,7 @@ void process_sound_trigger(LLMessageSystem *msg, void **)
+
+ void process_preload_sound(LLMessageSystem *msg, void **user_data)
+ {
+- if (!gAudiop)
++ if (!gAudiop || gSavedSettings.getBOOL("MuteSounds"))
+ {
+ return;
+ }
+@@ -3359,6 +3363,9 @@ void process_attached_sound(LLMessageSystem *msg, void **user_data)
+ msg->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain);
+ msg->getU8Fast(_PREHASH_DataBlock, _PREHASH_Flags, flags);
+
++ if(gSavedSettings.getBOOL("MuteSounds") || gain == 0.0)
++ return;
++
+ LLViewerObject *objectp = gObjectList.findObject(object_id);
+ if (!objectp)
+ {
+--
+tg: (2c826ba..) topic/fixes/jira-backported/VWR-11674_dont_download_muted_sounds (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-backported/VWR-3766_llGetInventoryNumber_tooltip_missing_INVENTORY_ANIMATION.diff b/debian/patches/topic/fixes/jira-backported/VWR-3766_llGetInventoryNumber_tooltip_missing_INVENTORY_ANIMATION.diff
new file mode 100644
index 0000000..8348af5
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-backported/VWR-3766_llGetInventoryNumber_tooltip_missing_INVENTORY_ANIMATION.diff
@@ -0,0 +1,26 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-backported/VWR-3766_llGetInventoryNumber_tooltip_missing_INVENTORY_ANIMATION
+
+Apply correct tooltips for script editing, adding some missing defines
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/lscript/lscript_library/lscript_library.cpp | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/indra/lscript/lscript_library/lscript_library.cpp b/indra/lscript/lscript_library/lscript_library.cpp
+index 2ac45f0..9ce0d2a 100644
+--- a/indra/lscript/lscript_library/lscript_library.cpp
++++ b/indra/lscript/lscript_library/lscript_library.cpp
+@@ -221,7 +221,7 @@ void LLScriptLibrary::init()
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llBreakAllLinks", NULL, NULL, "llBreakAllLinks()\nDelinks all tasks in the link set (requires permission PERMISSION_CHANGE_LINKS be set)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetLinkKey", "k", "i", "key llGetLinkKey(integer linknum)\nGet the key of linknumber in link set"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetLinkName", "s", "i", "string llGetLinkName(integer linknum)\nGet the name of linknumber in link set"));
+- addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetInventoryNumber", "i", "i", "integer llGetInventoryNumber(integer type)\nGet the number of items of a given type in the task's inventory.\nValid types: INVENTORY_TEXTURE, INVENTORY_SOUND, INVENTORY_OBJECT, INVENTORY_SCRIPT, INVENTORY_CLOTHING, INVENTORY_BODYPART, INVENTORY_NOTECARD, INVENTORY_LANDMARK, INVENTORY_ALL"));
++ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetInventoryNumber", "i", "i", "integer llGetInventoryNumber(integer type)\nGet the number of items of a given type in the task's inventory.\nValid types: INVENTORY_TEXTURE, INVENTORY_SOUND, INVENTORY_OBJECT, INVENTORY_SCRIPT, INVENTORY_CLOTHING, INVENTORY_BODYPART, INVENTORY_NOTECARD, INVENTORY_LANDMARK, INVENTORY_ANIMATION, INVENTORY_GESTURE, INVENTORY_ALL"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetInventoryName", "s", "ii", "string llGetInventoryName(integer type, integer number)\nGet the name of the inventory item number of type"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetScriptState", NULL, "si", "llSetScriptState(string name, integer run)\nControl the state of a script name."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetEnergy", "f", NULL, "float llGetEnergy()\nReturns how much energy is in the object as a percentage of maximum"));
+--
+tg: (2c826ba..) topic/fixes/jira-backported/VWR-3766_llGetInventoryNumber_tooltip_missing_INVENTORY_ANIMATION (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-backported/VWR5717_text_not_replaced_by_input_with_japanses_im.diff b/debian/patches/topic/fixes/jira-backported/VWR5717_text_not_replaced_by_input_with_japanses_im.diff
new file mode 100644
index 0000000..6da7c9d
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-backported/VWR5717_text_not_replaced_by_input_with_japanses_im.diff
@@ -0,0 +1,70 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-backported/VWR5717_text_not_replaced_by_input_with_japanses_im
+
+Fix to allow Japanese input, taken from VWR-5717
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llui/lllineeditor.cpp | 12 +++++++++---
+ indra/llui/lltexteditor.cpp | 12 +++++++++---
+ 2 files changed, 18 insertions(+), 6 deletions(-)
+
+diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
+index 64b1284..da633dd 100644
+--- a/indra/llui/lllineeditor.cpp
++++ b/indra/llui/lllineeditor.cpp
+@@ -2370,14 +2370,20 @@ BOOL LLLineEditor::hasPreeditString() const
+
+ void LLLineEditor::resetPreedit()
+ {
+- if (hasPreeditString())
++ if (hasSelection())
+ {
+- if (hasSelection())
++ if (hasPreeditString())
+ {
+ llwarns << "Preedit and selection!" << llendl;
+ deselect();
+ }
+-
++ else
++ {
++ deleteSelection();
++ }
++ }
++ if (hasPreeditString())
++ {
+ const S32 preedit_pos = mPreeditPositions.front();
+ mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos);
+ mText.insert(preedit_pos, mPreeditOverwrittenWString);
+diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
+index a40fec8..f14244d 100644
+--- a/indra/llui/lltexteditor.cpp
++++ b/indra/llui/lltexteditor.cpp
+@@ -4410,14 +4410,20 @@ BOOL LLTextEditor::hasPreeditString() const
+
+ void LLTextEditor::resetPreedit()
+ {
+- if (hasPreeditString())
++ if (hasSelection())
+ {
+- if (hasSelection())
++ if (hasPreeditString())
+ {
+ llwarns << "Preedit and selection!" << llendl;
+ deselect();
+ }
+-
++ else
++ {
++ deleteSelection(FALSE);
++ }
++ }
++ if (hasPreeditString())
++ {
+ mCursorPos = mPreeditPositions.front();
+ removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos);
+ insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString);
+--
+tg: (2c826ba..) topic/fixes/jira-backported/VWR5717_text_not_replaced_by_input_with_japanses_im (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-backported/glh_linear.diff b/debian/patches/topic/fixes/jira-backported/glh_linear.diff
new file mode 100644
index 0000000..e948aef
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-backported/glh_linear.diff
@@ -0,0 +1,1640 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/fixes/jira-backported/glh_linear
+
+Adds the missing glh_linear.h header.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/llwindow/glh/glh_linear.h | 1621 +++++++++++++++++++++++++++++++++++++++
+ 1 files changed, 1621 insertions(+), 0 deletions(-)
+
+diff --git a/indra/llwindow/glh/glh_linear.h b/indra/llwindow/glh/glh_linear.h
+new file mode 100644
+index 0000000..04ae1bd
+--- /dev/null
++++ b/indra/llwindow/glh/glh_linear.h
+@@ -0,0 +1,1621 @@
++/*
++ glh - is a platform-indepenedent C++ OpenGL helper library
++
++
++ Copyright (c) 2000 Cass Everitt
++ Copyright (c) 2000 NVIDIA Corporation
++ All rights reserved.
++
++ Redistribution and use in source and binary forms, with or
++ without modification, are permitted provided that the following
++ conditions are met:
++
++ * Redistributions of source code must retain the above
++ copyright notice, this list of conditions and the following
++ disclaimer.
++
++ * Redistributions in binary form must reproduce the above
++ copyright notice, this list of conditions and the following
++ disclaimer in the documentation and/or other materials
++ provided with the distribution.
++
++ * The names of contributors to this software may not be used
++ to endorse or promote products derived from this software
++ without specific prior written permission.
++
++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
++ REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
++ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
++ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
++ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
++ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ POSSIBILITY OF SUCH DAMAGE.
++
++
++ Cass Everitt - cass at r3.nu
++*/
++
++/*
++glh_linear.h
++*/
++
++// Author: Cass W. Everitt
++
++#ifndef GLH_LINEAR_H
++#define GLH_LINEAR_H
++
++#include <memory.h>
++#include <math.h>
++#include <assert.h>
++
++// only supports float for now...
++#define GLH_REAL_IS_FLOAT
++
++#ifdef GLH_REAL_IS_FLOAT
++# define GLH_REAL float
++# define GLH_REAL_NAMESPACE ns_float
++#endif
++
++#define GLH_QUATERNION_NORMALIZATION_THRESHOLD 64
++
++#define GLH_RAD_TO_DEG GLH_REAL(57.2957795130823208767981548141052)
++#define GLH_DEG_TO_RAD GLH_REAL(0.0174532925199432957692369076848861)
++#define GLH_ZERO GLH_REAL(0.0)
++#define GLH_ONE GLH_REAL(1.0)
++#define GLH_TWO GLH_REAL(2.0)
++#define GLH_EPSILON GLH_REAL(10e-6)
++#define GLH_PI GLH_REAL(3.1415926535897932384626433832795)
++
++#define equivalent(a,b) (((a < b + GLH_EPSILON) && (a > b - GLH_EPSILON)) ? true : false)
++
++namespace glh
++{
++
++ inline GLH_REAL to_degrees(GLH_REAL radians) { return radians*GLH_RAD_TO_DEG; }
++ inline GLH_REAL to_radians(GLH_REAL degrees) { return degrees*GLH_DEG_TO_RAD; }
++
++ // forward declarations for friend template functions.
++ template <int N, class T> class vec;
++
++ // forward declarations for friend template functions.
++ template <int N, class T>
++ bool operator == ( const vec<N,T> & v1, const vec<N,T> & v2 );
++
++ // forward declarations for friend template functions.
++ template <int N, class T>
++ bool operator != ( const vec<N,T> & v1, const vec<N,T> & v2 );
++
++ template <int N, class T>
++ class vec
++ {
++ public:
++ int size() const { return N; }
++
++ vec(const T & t = T())
++ { for(int i = 0; i < N; i++) v[i] = t; }
++ vec(const T * tp)
++ { for(int i = 0; i < N; i++) v[i] = tp[i]; }
++
++ const T * get_value() const
++ { return v; }
++
++
++ T dot( const vec<N,T> & rhs ) const
++ {
++ T r = 0;
++ for(int i = 0; i < N; i++) r += v[i]*rhs.v[i];
++ return r;
++ }
++
++ T length() const
++ {
++ T r = 0;
++ for(int i = 0; i < N; i++) r += v[i]*v[i];
++ return T(sqrt(r));
++ }
++
++ T square_norm() const
++ {
++ T r = 0;
++ for(int i = 0; i < N; i++) r += v[i]*v[i];
++ return r;
++ }
++
++ void negate()
++ { for(int i = 0; i < N; i++) v[i] = -v[i]; }
++
++
++ T normalize()
++ {
++ T sum(0);
++ for(int i = 0; i < N; i++)
++ sum += v[i]*v[i];
++ sum = T(sqrt(sum));
++ if (sum > GLH_EPSILON)
++ for(int i = 0; i < N; i++)
++ v[i] /= sum;
++ return sum;
++ }
++
++
++ vec<N,T> & set_value( const T * rhs )
++ { for(int i = 0; i < N; i++) v[i] = rhs[i]; return *this; }
++
++ T & operator [] ( int i )
++ { return v[i]; }
++
++ const T & operator [] ( int i ) const
++ { return v[i]; }
++
++ vec<N,T> & operator *= ( T d )
++ { for(int i = 0; i < N; i++) v[i] *= d; return *this;}
++
++ vec<N,T> & operator *= ( const vec<N,T> & u )
++ { for(int i = 0; i < N; i++) v[i] *= u[i]; return *this;}
++
++ vec<N,T> & operator /= ( T d )
++ { if(d == 0) return *this; for(int i = 0; i < N; i++) v[i] /= d; return *this;}
++
++ vec<N,T> & operator += ( const vec<N,T> & u )
++ { for(int i = 0; i < N; i++) v[i] += u.v[i]; return *this;}
++
++ vec<N,T> & operator -= ( const vec<N,T> & u )
++ { for(int i = 0; i < N; i++) v[i] -= u.v[i]; return *this;}
++
++
++ vec<N,T> operator - () const
++ { vec<N,T> rv = v; rv.negate(); return rv; }
++
++ vec<N,T> operator + ( const vec<N,T> &v) const
++ { vec<N,T> rt(*this); return rt += v; }
++
++ vec<N,T> operator - ( const vec<N,T> &v) const
++ { vec<N,T> rt(*this); return rt -= v; }
++
++ vec<N,T> operator * ( T d) const
++ { vec<N,T> rt(*this); return rt *= d; }
++
++ friend bool operator == <> ( const vec<N,T> &v1, const vec<N,T> &v2 );
++ friend bool operator != <> ( const vec<N,T> &v1, const vec<N,T> &v2 );
++
++
++ //protected:
++ T v[N];
++ };
++
++
++
++ // vector friend operators
++
++ template <int N, class T> inline
++ vec<N,T> operator * ( const vec<N,T> & b, T d )
++ {
++ vec<N,T> rt(b);
++ return rt *= d;
++ }
++
++ template <int N, class T> inline
++ vec<N,T> operator * ( T d, const vec<N,T> & b )
++ { return b*d; }
++
++ template <int N, class T> inline
++ vec<N,T> operator * ( const vec<N,T> & b, const vec<N,T> & d )
++ {
++ vec<N,T> rt(b);
++ return rt *= d;
++ }
++
++ template <int N, class T> inline
++ vec<N,T> operator / ( const vec<N,T> & b, T d )
++ { vec<N,T> rt(b); return rt /= d; }
++
++ template <int N, class T> inline
++ vec<N,T> operator + ( const vec<N,T> & v1, const vec<N,T> & v2 )
++ { vec<N,T> rt(v1); return rt += v2; }
++
++ template <int N, class T> inline
++ vec<N,T> operator - ( const vec<N,T> & v1, const vec<N,T> & v2 )
++ { vec<N,T> rt(v1); return rt -= v2; }
++
++
++ template <int N, class T> inline
++ bool operator == ( const vec<N,T> & v1, const vec<N,T> & v2 )
++ {
++ for(int i = 0; i < N; i++)
++ if(v1.v[i] != v2.v[i])
++ return false;
++ return true;
++ }
++
++ template <int N, class T> inline
++ bool operator != ( const vec<N,T> & v1, const vec<N,T> & v2 )
++ { return !(v1 == v2); }
++
++
++ typedef vec<3,unsigned char> vec3ub;
++ typedef vec<4,unsigned char> vec4ub;
++
++
++
++
++
++ namespace GLH_REAL_NAMESPACE
++ {
++ typedef GLH_REAL real;
++
++ class line;
++ class plane;
++ class matrix4;
++ class quaternion;
++ typedef quaternion rotation;
++
++ class vec2 : public vec<2,real>
++ {
++ public:
++ vec2(const real & t = real()) : vec<2,real>(t)
++ {}
++ vec2(const vec<2,real> & t) : vec<2,real>(t)
++ {}
++ vec2(const real * tp) : vec<2,real>(tp)
++ {}
++
++ vec2(real x, real y )
++ { v[0] = x; v[1] = y; }
++
++ void get_value(real & x, real & y) const
++ { x = v[0]; y = v[1]; }
++
++ vec2 & set_value( const real & x, const real & y)
++ { v[0] = x; v[1] = y; return *this; }
++
++ };
++
++
++ class vec3 : public vec<3,real>
++ {
++ public:
++ vec3(const real & t = real()) : vec<3,real>(t)
++ {}
++ vec3(const vec<3,real> & t) : vec<3,real>(t)
++ {}
++ vec3(const real * tp) : vec<3,real>(tp)
++ {}
++
++ vec3(real x, real y, real z)
++ { v[0] = x; v[1] = y; v[2] = z; }
++
++ void get_value(real & x, real & y, real & z) const
++ { x = v[0]; y = v[1]; z = v[2]; }
++
++ vec3 cross( const vec3 &rhs ) const
++ {
++ vec3 rt;
++ rt.v[0] = v[1]*rhs.v[2]-v[2]*rhs.v[1];
++ rt.v[1] = v[2]*rhs.v[0]-v[0]*rhs.v[2];
++ rt.v[2] = v[0]*rhs.v[1]-v[1]*rhs.v[0];
++ return rt;
++ }
++
++ vec3 & set_value( const real & x, const real & y, const real & z)
++ { v[0] = x; v[1] = y; v[2] = z; return *this; }
++
++ };
++
++
++ class vec4 : public vec<4,real>
++ {
++ public:
++ vec4(const real & t = real()) : vec<4,real>(t)
++ {}
++ vec4(const vec<4,real> & t) : vec<4,real>(t)
++ {}
++
++ vec4(const vec<3,real> & t, real fourth)
++
++ { v[0] = t.v[0]; v[1] = t.v[1]; v[2] = t.v[2]; v[3] = fourth; }
++ vec4(const real * tp) : vec<4,real>(tp)
++ {}
++ vec4(real x, real y, real z, real w)
++ { v[0] = x; v[1] = y; v[2] = z; v[3] = w; }
++
++ void get_value(real & x, real & y, real & z, real & w) const
++ { x = v[0]; y = v[1]; z = v[2]; w = v[3]; }
++
++ vec4 & set_value( const real & x, const real & y, const real & z, const real & w)
++ { v[0] = x; v[1] = y; v[2] = z; v[3] = w; return *this; }
++ };
++
++ inline
++ vec3 homogenize(const vec4 & v)
++ {
++ vec3 rt;
++ assert(v.v[3] != GLH_ZERO);
++ rt.v[0] = v.v[0]/v.v[3];
++ rt.v[1] = v.v[1]/v.v[3];
++ rt.v[2] = v.v[2]/v.v[3];
++ return rt;
++ }
++
++
++
++ class line
++ {
++ public:
++
++ line()
++ { set_value(vec3(0,0,0),vec3(0,0,1)); }
++
++ line( const vec3 & p0, const vec3 &p1)
++ { set_value(p0,p1); }
++
++ void set_value( const vec3 &p0, const vec3 &p1)
++ {
++ position = p0;
++ direction = p1-p0;
++ direction.normalize();
++ }
++
++ bool get_closest_points(const line &line2,
++ vec3 &pointOnThis,
++ vec3 &pointOnThat)
++ {
++
++ // quick check to see if parallel -- if so, quit.
++ if(fabs(direction.dot(line2.direction)) == 1.0)
++ return 0;
++ line l2 = line2;
++
++ // Algorithm: Brian Jean
++ //
++ register real u;
++ register real v;
++ vec3 Vr = direction;
++ vec3 Vs = l2.direction;
++ register real Vr_Dot_Vs = Vr.dot(Vs);
++ register real detA = real(1.0 - (Vr_Dot_Vs * Vr_Dot_Vs));
++ vec3 C = l2.position - position;
++ register real C_Dot_Vr = C.dot(Vr);
++ register real C_Dot_Vs = C.dot(Vs);
++
++ u = (C_Dot_Vr - Vr_Dot_Vs * C_Dot_Vs)/detA;
++ v = (C_Dot_Vr * Vr_Dot_Vs - C_Dot_Vs)/detA;
++
++ pointOnThis = position;
++ pointOnThis += direction * u;
++ pointOnThat = l2.position;
++ pointOnThat += l2.direction * v;
++
++ return 1;
++ }
++
++ vec3 get_closest_point(const vec3 &point)
++ {
++ vec3 np = point - position;
++ vec3 rp = direction*direction.dot(np)+position;
++ return rp;
++ }
++
++ const vec3 & get_position() const {return position;}
++
++ const vec3 & get_direction() const {return direction;}
++
++ //protected:
++ vec3 position;
++ vec3 direction;
++ };
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++ // matrix
++
++
++ class matrix4
++ {
++
++ public:
++
++ matrix4() { make_identity(); }
++
++ matrix4( real r )
++ { set_value(r); }
++
++ matrix4( real * m )
++ { set_value(m); }
++
++ matrix4( real a00, real a01, real a02, real a03,
++ real a10, real a11, real a12, real a13,
++ real a20, real a21, real a22, real a23,
++ real a30, real a31, real a32, real a33 )
++ {
++ element(0,0) = a00;
++ element(0,1) = a01;
++ element(0,2) = a02;
++ element(0,3) = a03;
++
++ element(1,0) = a10;
++ element(1,1) = a11;
++ element(1,2) = a12;
++ element(1,3) = a13;
++
++ element(2,0) = a20;
++ element(2,1) = a21;
++ element(2,2) = a22;
++ element(2,3) = a23;
++
++ element(3,0) = a30;
++ element(3,1) = a31;
++ element(3,2) = a32;
++ element(3,3) = a33;
++ }
++
++
++ void get_value( real * mp ) const
++ {
++ int c = 0;
++ for(int j=0; j < 4; j++)
++ for(int i=0; i < 4; i++)
++ mp[c++] = element(i,j);
++ }
++
++
++ const real * get_value() const
++ { return m; }
++
++ void set_value( real * mp)
++ {
++ int c = 0;
++ for(int j=0; j < 4; j++)
++ for(int i=0; i < 4; i++)
++ element(i,j) = mp[c++];
++ }
++
++ void set_value( real r )
++ {
++ for(int i=0; i < 4; i++)
++ for(int j=0; j < 4; j++)
++ element(i,j) = r;
++ }
++
++ void make_identity()
++ {
++ element(0,0) = 1.0;
++ element(0,1) = 0.0;
++ element(0,2) = 0.0;
++ element(0,3) = 0.0;
++
++ element(1,0) = 0.0;
++ element(1,1) = 1.0;
++ element(1,2) = 0.0;
++ element(1,3) = 0.0;
++
++ element(2,0) = 0.0;
++ element(2,1) = 0.0;
++ element(2,2) = 1.0;
++ element(2,3) = 0.0;
++
++ element(3,0) = 0.0;
++ element(3,1) = 0.0;
++ element(3,2) = 0.0;
++ element(3,3) = 1.0;
++ }
++
++
++ static matrix4 identity()
++ {
++ static matrix4 mident (
++ 1.0, 0.0, 0.0, 0.0,
++ 0.0, 1.0, 0.0, 0.0,
++ 0.0, 0.0, 1.0, 0.0,
++ 0.0, 0.0, 0.0, 1.0 );
++ return mident;
++ }
++
++
++ void set_scale( real s )
++ {
++ element(0,0) = s;
++ element(1,1) = s;
++ element(2,2) = s;
++ }
++
++ void set_scale( const vec3 & s )
++ {
++ element(0,0) = s.v[0];
++ element(1,1) = s.v[1];
++ element(2,2) = s.v[2];
++ }
++
++
++ void set_translate( const vec3 & t )
++ {
++ element(0,3) = t.v[0];
++ element(1,3) = t.v[1];
++ element(2,3) = t.v[2];
++ }
++
++ void set_row(int r, const vec4 & t)
++ {
++ element(r,0) = t.v[0];
++ element(r,1) = t.v[1];
++ element(r,2) = t.v[2];
++ element(r,3) = t.v[3];
++ }
++
++ void set_column(int c, const vec4 & t)
++ {
++ element(0,c) = t.v[0];
++ element(1,c) = t.v[1];
++ element(2,c) = t.v[2];
++ element(3,c) = t.v[3];
++ }
++
++
++ void get_row(int r, vec4 & t) const
++ {
++ t.v[0] = element(r,0);
++ t.v[1] = element(r,1);
++ t.v[2] = element(r,2);
++ t.v[3] = element(r,3);
++ }
++
++ vec4 get_row(int r) const
++ {
++ vec4 v; get_row(r, v);
++ return v;
++ }
++
++ void get_column(int c, vec4 & t) const
++ {
++ t.v[0] = element(0,c);
++ t.v[1] = element(1,c);
++ t.v[2] = element(2,c);
++ t.v[3] = element(3,c);
++ }
++
++ vec4 get_column(int c) const
++ {
++ vec4 v; get_column(c, v);
++ return v;
++ }
++
++ matrix4 inverse() const
++ {
++ matrix4 minv;
++
++ real r1[8], r2[8], r3[8], r4[8];
++ real *s[4], *tmprow;
++
++ s[0] = &r1[0];
++ s[1] = &r2[0];
++ s[2] = &r3[0];
++ s[3] = &r4[0];
++
++ register int i,j,p,jj;
++ for(i=0;i<4;i++)
++ {
++ for(j=0;j<4;j++)
++ {
++ s[i][j] = element(i,j);
++ if(i==j) s[i][j+4] = 1.0;
++ else s[i][j+4] = 0.0;
++ }
++ }
++ real scp[4];
++ for(i=0;i<4;i++)
++ {
++ scp[i] = real(fabs(s[i][0]));
++ for(j=1;j<4;j++)
++ if(real(fabs(s[i][j])) > scp[i]) scp[i] = real(fabs(s[i][j]));
++ if(scp[i] == 0.0) return minv; // singular matrix!
++ }
++
++ int pivot_to;
++ real scp_max;
++ for(i=0;i<4;i++)
++ {
++ // select pivot row
++ pivot_to = i;
++ scp_max = real(fabs(s[i][i]/scp[i]));
++ // find out which row should be on top
++ for(p=i+1;p<4;p++)
++ if(real(fabs(s[p][i]/scp[p])) > scp_max)
++ { scp_max = real(fabs(s[p][i]/scp[p])); pivot_to = p; }
++ // Pivot if necessary
++ if(pivot_to != i)
++ {
++ tmprow = s[i];
++ s[i] = s[pivot_to];
++ s[pivot_to] = tmprow;
++ real tmpscp;
++ tmpscp = scp[i];
++ scp[i] = scp[pivot_to];
++ scp[pivot_to] = tmpscp;
++ }
++
++ real mji;
++ // perform gaussian elimination
++ for(j=i+1;j<4;j++)
++ {
++ mji = s[j][i]/s[i][i];
++ s[j][i] = 0.0;
++ for(jj=i+1;jj<8;jj++)
++ s[j][jj] -= mji*s[i][jj];
++ }
++ }
++ if(s[3][3] == 0.0) return minv; // singular matrix!
++
++ //
++ // Now we have an upper triangular matrix.
++ //
++ // x x x x | y y y y
++ // 0 x x x | y y y y
++ // 0 0 x x | y y y y
++ // 0 0 0 x | y y y y
++ //
++ // we'll back substitute to get the inverse
++ //
++ // 1 0 0 0 | z z z z
++ // 0 1 0 0 | z z z z
++ // 0 0 1 0 | z z z z
++ // 0 0 0 1 | z z z z
++ //
++
++ real mij;
++ for(i=3;i>0;i--)
++ {
++ for(j=i-1;j > -1; j--)
++ {
++ mij = s[j][i]/s[i][i];
++ for(jj=j+1;jj<8;jj++)
++ s[j][jj] -= mij*s[i][jj];
++ }
++ }
++
++ for(i=0;i<4;i++)
++ for(j=0;j<4;j++)
++ minv(i,j) = s[i][j+4] / s[i][i];
++
++ return minv;
++ }
++
++
++ matrix4 transpose() const
++ {
++ matrix4 mtrans;
++
++ for(int i=0;i<4;i++)
++ for(int j=0;j<4;j++)
++ mtrans(i,j) = element(j,i);
++ return mtrans;
++ }
++
++ matrix4 & mult_right( const matrix4 & b )
++ {
++ matrix4 mt(*this);
++ set_value(real(0));
++
++ for(int i=0; i < 4; i++)
++ for(int j=0; j < 4; j++)
++ for(int c=0; c < 4; c++)
++ element(i,j) += mt(i,c) * b(c,j);
++ return *this;
++ }
++
++ matrix4 & mult_left( const matrix4 & b )
++ {
++ matrix4 mt(*this);
++ set_value(real(0));
++
++ for(int i=0; i < 4; i++)
++ for(int j=0; j < 4; j++)
++ for(int c=0; c < 4; c++)
++ element(i,j) += b(i,c) * mt(c,j);
++ return *this;
++ }
++
++ // dst = M * src
++ void mult_matrix_vec( const vec3 &src, vec3 &dst ) const
++ {
++ real w = (
++ src.v[0] * element(3,0) +
++ src.v[1] * element(3,1) +
++ src.v[2] * element(3,2) +
++ element(3,3) );
++
++ assert(w != GLH_ZERO);
++
++ dst.v[0] = (
++ src.v[0] * element(0,0) +
++ src.v[1] * element(0,1) +
++ src.v[2] * element(0,2) +
++ element(0,3) ) / w;
++ dst.v[1] = (
++ src.v[0] * element(1,0) +
++ src.v[1] * element(1,1) +
++ src.v[2] * element(1,2) +
++ element(1,3) ) / w;
++ dst.v[2] = (
++ src.v[0] * element(2,0) +
++ src.v[1] * element(2,1) +
++ src.v[2] * element(2,2) +
++ element(2,3) ) / w;
++ }
++
++ void mult_matrix_vec( vec3 & src_and_dst) const
++ { mult_matrix_vec(vec3(src_and_dst), src_and_dst); }
++
++
++ // dst = src * M
++ void mult_vec_matrix( const vec3 &src, vec3 &dst ) const
++ {
++ real w = (
++ src.v[0] * element(0,3) +
++ src.v[1] * element(1,3) +
++ src.v[2] * element(2,3) +
++ element(3,3) );
++
++ assert(w != GLH_ZERO);
++
++ dst.v[0] = (
++ src.v[0] * element(0,0) +
++ src.v[1] * element(1,0) +
++ src.v[2] * element(2,0) +
++ element(3,0) ) / w;
++ dst.v[1] = (
++ src.v[0] * element(0,1) +
++ src.v[1] * element(1,1) +
++ src.v[2] * element(2,1) +
++ element(3,1) ) / w;
++ dst.v[2] = (
++ src.v[0] * element(0,2) +
++ src.v[1] * element(1,2) +
++ src.v[2] * element(2,2) +
++ element(3,2) ) / w;
++ }
++
++
++ void mult_vec_matrix( vec3 & src_and_dst) const
++ { mult_vec_matrix(vec3(src_and_dst), src_and_dst); }
++
++ // dst = M * src
++ void mult_matrix_vec( const vec4 &src, vec4 &dst ) const
++ {
++ dst.v[0] = (
++ src.v[0] * element(0,0) +
++ src.v[1] * element(0,1) +
++ src.v[2] * element(0,2) +
++ src.v[3] * element(0,3));
++ dst.v[1] = (
++ src.v[0] * element(1,0) +
++ src.v[1] * element(1,1) +
++ src.v[2] * element(1,2) +
++ src.v[3] * element(1,3));
++ dst.v[2] = (
++ src.v[0] * element(2,0) +
++ src.v[1] * element(2,1) +
++ src.v[2] * element(2,2) +
++ src.v[3] * element(2,3));
++ dst.v[3] = (
++ src.v[0] * element(3,0) +
++ src.v[1] * element(3,1) +
++ src.v[2] * element(3,2) +
++ src.v[3] * element(3,3));
++ }
++
++ void mult_matrix_vec( vec4 & src_and_dst) const
++ { mult_matrix_vec(vec4(src_and_dst), src_and_dst); }
++
++
++ // dst = src * M
++ void mult_vec_matrix( const vec4 &src, vec4 &dst ) const
++ {
++ dst.v[0] = (
++ src.v[0] * element(0,0) +
++ src.v[1] * element(1,0) +
++ src.v[2] * element(2,0) +
++ src.v[3] * element(3,0));
++ dst.v[1] = (
++ src.v[0] * element(0,1) +
++ src.v[1] * element(1,1) +
++ src.v[2] * element(2,1) +
++ src.v[3] * element(3,1));
++ dst.v[2] = (
++ src.v[0] * element(0,2) +
++ src.v[1] * element(1,2) +
++ src.v[2] * element(2,2) +
++ src.v[3] * element(3,2));
++ dst.v[3] = (
++ src.v[0] * element(0,3) +
++ src.v[1] * element(1,3) +
++ src.v[2] * element(2,3) +
++ src.v[3] * element(3,3));
++ }
++
++
++ void mult_vec_matrix( vec4 & src_and_dst) const
++ { mult_vec_matrix(vec4(src_and_dst), src_and_dst); }
++
++
++ // dst = M * src
++ void mult_matrix_dir( const vec3 &src, vec3 &dst ) const
++ {
++ dst.v[0] = (
++ src.v[0] * element(0,0) +
++ src.v[1] * element(0,1) +
++ src.v[2] * element(0,2) ) ;
++ dst.v[1] = (
++ src.v[0] * element(1,0) +
++ src.v[1] * element(1,1) +
++ src.v[2] * element(1,2) ) ;
++ dst.v[2] = (
++ src.v[0] * element(2,0) +
++ src.v[1] * element(2,1) +
++ src.v[2] * element(2,2) ) ;
++ }
++
++
++ void mult_matrix_dir( vec3 & src_and_dst) const
++ { mult_matrix_dir(vec3(src_and_dst), src_and_dst); }
++
++
++ // dst = src * M
++ void mult_dir_matrix( const vec3 &src, vec3 &dst ) const
++ {
++ dst.v[0] = (
++ src.v[0] * element(0,0) +
++ src.v[1] * element(1,0) +
++ src.v[2] * element(2,0) ) ;
++ dst.v[1] = (
++ src.v[0] * element(0,1) +
++ src.v[1] * element(1,1) +
++ src.v[2] * element(2,1) ) ;
++ dst.v[2] = (
++ src.v[0] * element(0,2) +
++ src.v[1] * element(1,2) +
++ src.v[2] * element(2,2) ) ;
++ }
++
++
++ void mult_dir_matrix( vec3 & src_and_dst) const
++ { mult_dir_matrix(vec3(src_and_dst), src_and_dst); }
++
++
++ real & operator () (int row, int col)
++ { return element(row,col); }
++
++ const real & operator () (int row, int col) const
++ { return element(row,col); }
++
++ real & element (int row, int col)
++ { return m[row | (col<<2)]; }
++
++ const real & element (int row, int col) const
++ { return m[row | (col<<2)]; }
++
++ matrix4 & operator *= ( const matrix4 & mat )
++ {
++ mult_right( mat );
++ return *this;
++ }
++
++ matrix4 & operator *= ( const real & r )
++ {
++ for (int i = 0; i < 4; ++i)
++ {
++ element(0,i) *= r;
++ element(1,i) *= r;
++ element(2,i) *= r;
++ element(3,i) *= r;
++ }
++ return *this;
++ }
++
++ matrix4 & operator += ( const matrix4 & mat )
++ {
++ for (int i = 0; i < 4; ++i)
++ {
++ element(0,i) += mat.element(0,i);
++ element(1,i) += mat.element(1,i);
++ element(2,i) += mat.element(2,i);
++ element(3,i) += mat.element(3,i);
++ }
++ return *this;
++ }
++
++ friend matrix4 operator * ( const matrix4 & m1, const matrix4 & m2 );
++ friend bool operator == ( const matrix4 & m1, const matrix4 & m2 );
++ friend bool operator != ( const matrix4 & m1, const matrix4 & m2 );
++
++ //protected:
++ real m[16];
++ };
++
++ inline
++ matrix4 operator * ( const matrix4 & m1, const matrix4 & m2 )
++ {
++ matrix4 product;
++
++ product = m1;
++ product.mult_right(m2);
++
++ return product;
++ }
++
++ inline
++ bool operator ==( const matrix4 &m1, const matrix4 &m2 )
++ {
++ return (
++ m1(0,0) == m2(0,0) &&
++ m1(0,1) == m2(0,1) &&
++ m1(0,2) == m2(0,2) &&
++ m1(0,3) == m2(0,3) &&
++ m1(1,0) == m2(1,0) &&
++ m1(1,1) == m2(1,1) &&
++ m1(1,2) == m2(1,2) &&
++ m1(1,3) == m2(1,3) &&
++ m1(2,0) == m2(2,0) &&
++ m1(2,1) == m2(2,1) &&
++ m1(2,2) == m2(2,2) &&
++ m1(2,3) == m2(2,3) &&
++ m1(3,0) == m2(3,0) &&
++ m1(3,1) == m2(3,1) &&
++ m1(3,2) == m2(3,2) &&
++ m1(3,3) == m2(3,3) );
++ }
++
++ inline
++ bool operator != ( const matrix4 & m1, const matrix4 & m2 )
++ { return !( m1 == m2 ); }
++
++
++
++
++
++
++
++
++
++
++
++
++
++ class quaternion
++ {
++ public:
++
++ quaternion()
++ {
++ *this = identity();
++ }
++
++ quaternion( const real v[4] )
++ {
++ set_value( v );
++ }
++
++
++ quaternion( real q0, real q1, real q2, real q3 )
++ {
++ set_value( q0, q1, q2, q3 );
++ }
++
++
++ quaternion( const matrix4 & m )
++ {
++ set_value( m );
++ }
++
++
++ quaternion( const vec3 &axis, real radians )
++ {
++ set_value( axis, radians );
++ }
++
++
++ quaternion( const vec3 &rotateFrom, const vec3 &rotateTo )
++ {
++ set_value( rotateFrom, rotateTo );
++ }
++
++ quaternion( const vec3 & from_look, const vec3 & from_up,
++ const vec3 & to_look, const vec3& to_up)
++ {
++ set_value(from_look, from_up, to_look, to_up);
++ }
++
++ const real * get_value() const
++ {
++ return &q[0];
++ }
++
++ void get_value( real &q0, real &q1, real &q2, real &q3 ) const
++ {
++ q0 = q[0];
++ q1 = q[1];
++ q2 = q[2];
++ q3 = q[3];
++ }
++
++ quaternion & set_value( real q0, real q1, real q2, real q3 )
++ {
++ q[0] = q0;
++ q[1] = q1;
++ q[2] = q2;
++ q[3] = q3;
++ counter = 0;
++ return *this;
++ }
++
++ void get_value( vec3 &axis, real &radians ) const
++ {
++ radians = real(acos( q[3] ) * GLH_TWO);
++ if ( radians == GLH_ZERO )
++ axis = vec3( 0.0, 0.0, 1.0 );
++ else
++ {
++ axis.v[0] = q[0];
++ axis.v[1] = q[1];
++ axis.v[2] = q[2];
++ axis.normalize();
++ }
++ }
++
++ void get_value( matrix4 & m ) const
++ {
++ real s, xs, ys, zs, wx, wy, wz, xx, xy, xz, yy, yz, zz;
++
++ real norm = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3];
++
++ s = (equivalent(norm,GLH_ZERO)) ? GLH_ZERO : ( GLH_TWO / norm );
++
++ xs = q[0] * s;
++ ys = q[1] * s;
++ zs = q[2] * s;
++
++ wx = q[3] * xs;
++ wy = q[3] * ys;
++ wz = q[3] * zs;
++
++ xx = q[0] * xs;
++ xy = q[0] * ys;
++ xz = q[0] * zs;
++
++ yy = q[1] * ys;
++ yz = q[1] * zs;
++ zz = q[2] * zs;
++
++ m(0,0) = real( GLH_ONE - ( yy + zz ));
++ m(1,0) = real ( xy + wz );
++ m(2,0) = real ( xz - wy );
++
++ m(0,1) = real ( xy - wz );
++ m(1,1) = real ( GLH_ONE - ( xx + zz ));
++ m(2,1) = real ( yz + wx );
++
++ m(0,2) = real ( xz + wy );
++ m(1,2) = real ( yz - wx );
++ m(2,2) = real ( GLH_ONE - ( xx + yy ));
++
++ m(3,0) = m(3,1) = m(3,2) = m(0,3) = m(1,3) = m(2,3) = GLH_ZERO;
++ m(3,3) = GLH_ONE;
++ }
++
++ quaternion & set_value( const real * qp )
++ {
++ memcpy(q,qp,sizeof(real) * 4);
++
++ counter = 0;
++ return *this;
++ }
++
++ quaternion & set_value( const matrix4 & m )
++ {
++ real tr, s;
++ int i, j, k;
++ const int nxt[3] = { 1, 2, 0 };
++
++ tr = m(0,0) + m(1,1) + m(2,2);
++
++ if ( tr > GLH_ZERO )
++ {
++ s = real(sqrt( tr + m(3,3) ));
++ q[3] = real ( s * 0.5 );
++ s = real(0.5) / s;
++
++ q[0] = real ( ( m(1,2) - m(2,1) ) * s );
++ q[1] = real ( ( m(2,0) - m(0,2) ) * s );
++ q[2] = real ( ( m(0,1) - m(1,0) ) * s );
++ }
++ else
++ {
++ i = 0;
++ if ( m(1,1) > m(0,0) )
++ i = 1;
++
++ if ( m(2,2) > m(i,i) )
++ i = 2;
++
++ j = nxt[i];
++ k = nxt[j];
++
++ s = real(sqrt( ( m(i,j) - ( m(j,j) + m(k,k) )) + GLH_ONE ));
++
++ q[i] = real ( s * 0.5 );
++ s = real(0.5 / s);
++
++ q[3] = real ( ( m(j,k) - m(k,j) ) * s );
++ q[j] = real ( ( m(i,j) + m(j,i) ) * s );
++ q[k] = real ( ( m(i,k) + m(k,i) ) * s );
++ }
++
++ counter = 0;
++ return *this;
++ }
++
++ quaternion & set_value( const vec3 &axis, real theta )
++ {
++ real sqnorm = axis.square_norm();
++
++ if (sqnorm <= GLH_EPSILON)
++ {
++ // axis too small.
++ x = y = z = 0.0;
++ w = 1.0;
++ }
++ else
++ {
++ theta *= real(0.5);
++ real sin_theta = real(sin(theta));
++
++ if (!equivalent(sqnorm,GLH_ONE))
++ sin_theta /= real(sqrt(sqnorm));
++ x = sin_theta * axis.v[0];
++ y = sin_theta * axis.v[1];
++ z = sin_theta * axis.v[2];
++ w = real(cos(theta));
++ }
++ return *this;
++ }
++
++ quaternion & set_value( const vec3 & rotateFrom, const vec3 & rotateTo )
++ {
++ vec3 p1, p2;
++ real alpha;
++
++ p1 = rotateFrom;
++ p1.normalize();
++ p2 = rotateTo;
++ p2.normalize();
++
++ alpha = p1.dot(p2);
++
++ if(equivalent(alpha,GLH_ONE))
++ {
++ *this = identity();
++ return *this;
++ }
++
++ // ensures that the anti-parallel case leads to a positive dot
++ if(equivalent(alpha,-GLH_ONE))
++ {
++ vec3 v;
++
++ if(p1.v[0] != p1.v[1] || p1.v[0] != p1.v[2])
++ v = vec3(p1.v[1], p1.v[2], p1.v[0]);
++ else
++ v = vec3(-p1.v[0], p1.v[1], p1.v[2]);
++
++ v -= p1 * p1.dot(v);
++ v.normalize();
++
++ set_value(v, GLH_PI);
++ return *this;
++ }
++
++ p1 = p1.cross(p2);
++ p1.normalize();
++ set_value(p1,real(acos(alpha)));
++
++ counter = 0;
++ return *this;
++ }
++
++ quaternion & set_value( const vec3 & from_look, const vec3 & from_up,
++ const vec3 & to_look, const vec3 & to_up)
++ {
++ quaternion r_look = quaternion(from_look, to_look);
++
++ vec3 rotated_from_up(from_up);
++ r_look.mult_vec(rotated_from_up);
++
++ quaternion r_twist = quaternion(rotated_from_up, to_up);
++
++ *this = r_twist;
++ *this *= r_look;
++ return *this;
++ }
++
++ quaternion & operator *= ( const quaternion & qr )
++ {
++ quaternion ql(*this);
++
++ w = ql.w * qr.w - ql.x * qr.x - ql.y * qr.y - ql.z * qr.z;
++ x = ql.w * qr.x + ql.x * qr.w + ql.y * qr.z - ql.z * qr.y;
++ y = ql.w * qr.y + ql.y * qr.w + ql.z * qr.x - ql.x * qr.z;
++ z = ql.w * qr.z + ql.z * qr.w + ql.x * qr.y - ql.y * qr.x;
++
++ counter += qr.counter;
++ counter++;
++ counter_normalize();
++ return *this;
++ }
++
++ void normalize()
++ {
++ real rnorm = GLH_ONE / real(sqrt(w * w + x * x + y * y + z * z));
++ if (equivalent(rnorm, GLH_ZERO))
++ return;
++ x *= rnorm;
++ y *= rnorm;
++ z *= rnorm;
++ w *= rnorm;
++ counter = 0;
++ }
++
++ friend bool operator == ( const quaternion & q1, const quaternion & q2 );
++
++ friend bool operator != ( const quaternion & q1, const quaternion & q2 );
++
++ friend quaternion operator * ( const quaternion & q1, const quaternion & q2 );
++
++ bool equals( const quaternion & r, real tolerance ) const
++ {
++ real t;
++
++ t = (
++ (q[0]-r.q[0])*(q[0]-r.q[0]) +
++ (q[1]-r.q[1])*(q[1]-r.q[1]) +
++ (q[2]-r.q[2])*(q[2]-r.q[2]) +
++ (q[3]-r.q[3])*(q[3]-r.q[3]) );
++ if(t > GLH_EPSILON)
++ return false;
++ return 1;
++ }
++
++ quaternion & conjugate()
++ {
++ q[0] *= -GLH_ONE;
++ q[1] *= -GLH_ONE;
++ q[2] *= -GLH_ONE;
++ return *this;
++ }
++
++ quaternion & invert()
++ {
++ return conjugate();
++ }
++
++ quaternion inverse() const
++ {
++ quaternion r = *this;
++ return r.invert();
++ }
++
++ //
++ // Quaternion multiplication with cartesian vector
++ // v' = q*v*q(star)
++ //
++ void mult_vec( const vec3 &src, vec3 &dst ) const
++ {
++ real v_coef = w * w - x * x - y * y - z * z;
++ real u_coef = GLH_TWO * (src.v[0] * x + src.v[1] * y + src.v[2] * z);
++ real c_coef = GLH_TWO * w;
++
++ dst.v[0] = v_coef * src.v[0] + u_coef * x + c_coef * (y * src.v[2] - z * src.v[1]);
++ dst.v[1] = v_coef * src.v[1] + u_coef * y + c_coef * (z * src.v[0] - x * src.v[2]);
++ dst.v[2] = v_coef * src.v[2] + u_coef * z + c_coef * (x * src.v[1] - y * src.v[0]);
++ }
++
++ void mult_vec( vec3 & src_and_dst) const
++ {
++ mult_vec(vec3(src_and_dst), src_and_dst);
++ }
++
++ void scale_angle( real scaleFactor )
++ {
++ vec3 axis;
++ real radians;
++
++ get_value(axis, radians);
++ radians *= scaleFactor;
++ set_value(axis, radians);
++ }
++
++ static quaternion slerp( const quaternion & p, const quaternion & q, real alpha )
++ {
++ quaternion r;
++
++ real cos_omega = p.x * q.x + p.y * q.y + p.z * q.z + p.w * q.w;
++ // if B is on opposite hemisphere from A, use -B instead
++
++ int bflip;
++ if ( ( bflip = (cos_omega < GLH_ZERO)) )
++ cos_omega = -cos_omega;
++
++ // complementary interpolation parameter
++ real beta = GLH_ONE - alpha;
++
++ if(cos_omega <= GLH_ONE - GLH_EPSILON)
++ return p;
++
++ real omega = real(acos(cos_omega));
++ real one_over_sin_omega = GLH_ONE / real(sin(omega));
++
++ beta = real(sin(omega*beta) * one_over_sin_omega);
++ alpha = real(sin(omega*alpha) * one_over_sin_omega);
++
++ if (bflip)
++ alpha = -alpha;
++
++ r.x = beta * p.q[0]+ alpha * q.q[0];
++ r.y = beta * p.q[1]+ alpha * q.q[1];
++ r.z = beta * p.q[2]+ alpha * q.q[2];
++ r.w = beta * p.q[3]+ alpha * q.q[3];
++ return r;
++ }
++
++ static quaternion identity()
++ {
++ static quaternion ident( vec3( 0.0, 0.0, 0.0 ), GLH_ONE );
++ return ident;
++ }
++
++ real & operator []( int i )
++ {
++ assert(i < 4);
++ return q[i];
++ }
++
++ const real & operator []( int i ) const
++ {
++ assert(i < 4);
++ return q[i];
++ }
++
++ protected:
++
++ void counter_normalize()
++ {
++ if (counter > GLH_QUATERNION_NORMALIZATION_THRESHOLD)
++ normalize();
++ }
++
++ union
++ {
++ struct
++ {
++ real q[4];
++ };
++ struct
++ {
++ real x;
++ real y;
++ real z;
++ real w;
++ };
++ };
++
++ // renormalization counter
++ unsigned char counter;
++ };
++
++ inline
++ bool operator == ( const quaternion & q1, const quaternion & q2 )
++ {
++ return (equivalent(q1.x, q2.x) &&
++ equivalent(q1.y, q2.y) &&
++ equivalent(q1.z, q2.z) &&
++ equivalent(q1.w, q2.w) );
++ }
++
++ inline
++ bool operator != ( const quaternion & q1, const quaternion & q2 )
++ {
++ return ! ( q1 == q2 );
++ }
++
++ inline
++ quaternion operator * ( const quaternion & q1, const quaternion & q2 )
++ {
++ quaternion r(q1);
++ r *= q2;
++ return r;
++ }
++
++
++
++
++
++
++
++
++
++
++ class plane
++ {
++ public:
++
++ plane()
++ {
++ planedistance = 0.0;
++ planenormal.set_value( 0.0, 0.0, 1.0 );
++ }
++
++
++ plane( const vec3 &p0, const vec3 &p1, const vec3 &p2 )
++ {
++ vec3 v0 = p1 - p0;
++ vec3 v1 = p2 - p0;
++ planenormal = v0.cross(v1);
++ planenormal.normalize();
++ planedistance = p0.dot(planenormal);
++ }
++
++ plane( const vec3 &normal, real distance )
++ {
++ planedistance = distance;
++ planenormal = normal;
++ planenormal.normalize();
++ }
++
++ plane( const vec3 &normal, const vec3 &point )
++ {
++ planenormal = normal;
++ planenormal.normalize();
++ planedistance = point.dot(planenormal);
++ }
++
++ void offset( real d )
++ {
++ planedistance += d;
++ }
++
++ bool intersect( const line &l, vec3 &intersection ) const
++ {
++ vec3 pos, dir;
++ vec3 pn = planenormal;
++ real pd = planedistance;
++
++ pos = l.get_position();
++ dir = l.get_direction();
++
++ if(dir.dot(pn) == 0.0) return 0;
++ pos -= pn*pd;
++ // now we're talking about a plane passing through the origin
++ if(pos.dot(pn) < 0.0) pn.negate();
++ if(dir.dot(pn) > 0.0) dir.negate();
++ vec3 ppos = pn * pos.dot(pn);
++ pos = (ppos.length()/dir.dot(-pn))*dir;
++ intersection = l.get_position();
++ intersection += pos;
++ return 1;
++ }
++ void transform( const matrix4 &matrix )
++ {
++ matrix4 invtr = matrix.inverse();
++ invtr = invtr.transpose();
++
++ vec3 pntOnplane = planenormal * planedistance;
++ vec3 newPntOnplane;
++ vec3 newnormal;
++
++ invtr.mult_dir_matrix(planenormal, newnormal);
++ matrix.mult_vec_matrix(pntOnplane, newPntOnplane);
++
++ newnormal.normalize();
++ planenormal = newnormal;
++ planedistance = newPntOnplane.dot(planenormal);
++ }
++
++ bool is_in_half_space( const vec3 &point ) const
++ {
++
++ if(( point.dot(planenormal) - planedistance) < 0.0)
++ return 0;
++ return 1;
++ }
++
++
++ real distance( const vec3 & point ) const
++ {
++ return planenormal.dot(point - planenormal*planedistance);
++ }
++
++ const vec3 &get_normal() const
++ {
++ return planenormal;
++ }
++
++
++ real get_distance_from_origin() const
++ {
++ return planedistance;
++ }
++
++
++ friend bool operator == ( const plane & p1, const plane & p2 );
++
++
++ friend bool operator != ( const plane & p1, const plane & p2 );
++
++ //protected:
++ vec3 planenormal;
++ real planedistance;
++ };
++
++ inline
++ bool operator == (const plane & p1, const plane & p2 )
++ {
++ return ( p1.planedistance == p2.planedistance && p1.planenormal == p2.planenormal);
++ }
++
++ inline
++ bool operator != ( const plane & p1, const plane & p2 )
++ { return ! (p1 == p2); }
++
++
++
++ } // "ns_##GLH_REAL"
++
++ // make common typedefs...
++#ifdef GLH_REAL_IS_FLOAT
++ typedef GLH_REAL_NAMESPACE::vec2 vec2f;
++ typedef GLH_REAL_NAMESPACE::vec3 vec3f;
++ typedef GLH_REAL_NAMESPACE::vec4 vec4f;
++ typedef GLH_REAL_NAMESPACE::quaternion quaternionf;
++ typedef GLH_REAL_NAMESPACE::quaternion rotationf;
++ typedef GLH_REAL_NAMESPACE::line linef;
++ typedef GLH_REAL_NAMESPACE::plane planef;
++ typedef GLH_REAL_NAMESPACE::matrix4 matrix4f;
++#endif
++
++
++
++
++} // namespace glh
++
++
++
++#endif
++
+--
+tg: (2c826ba..) topic/fixes/jira-backported/glh_linear (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/50_get_cpu_clock_count_for_more_than_just_i386.diff b/debian/patches/topic/fixes/jira-sent/50_get_cpu_clock_count_for_more_than_just_i386.diff
new file mode 100644
index 0000000..a9be482
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/50_get_cpu_clock_count_for_more_than_just_i386.diff
@@ -0,0 +1,40 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/50_get_cpu_clock_count_for_more_than_just_i386
+
+Fix CPU clock count for non i386 based processors eg PPC.
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llcommon/llfasttimer.cpp | 16 ++--------------
+ 1 files changed, 2 insertions(+), 14 deletions(-)
+
+diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
+index 3304528..804d124 100644
+--- a/indra/llcommon/llfasttimer.cpp
++++ b/indra/llcommon/llfasttimer.cpp
+@@ -91,20 +91,8 @@ U64 get_cpu_clock_count()
+ #endif // LL_WINDOWS
+
+
+-#if (LL_LINUX || LL_SOLARIS) && (defined(__i386__) || defined(__amd64__))
+-U64 get_cpu_clock_count()
+-{
+- U64 x;
+- __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
+- return x;
+-}
+-#endif
+-
+-#if LL_DARWIN || (LL_SOLARIS && defined(__sparc__))
+-//
+-// Mac implementation of CPU clock
+-//
+-// Just use gettimeofday implementation for now
++#if LL_LINUX || LL_DARWIN || LL_SOLARIS
++// Both Linux and Mac use gettimeofday for accurate time
+
+ U64 get_cpu_clock_count()
+ {
+--
+tg: (2c826ba..) topic/fixes/jira-sent/50_get_cpu_clock_count_for_more_than_just_i386 (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/59_need_llpreprocessor_to_access_endian_macros.diff b/debian/patches/topic/fixes/jira-sent/59_need_llpreprocessor_to_access_endian_macros.diff
new file mode 100644
index 0000000..200d35e
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/59_need_llpreprocessor_to_access_endian_macros.diff
@@ -0,0 +1,25 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/59_need_llpreprocessor_to_access_endian_macros
+
+PPC compile fix, give access to endian macros
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llmessage/message.h | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h
+index c503a58..3fae382 100644
+--- a/indra/llmessage/message.h
++++ b/indra/llmessage/message.h
+@@ -48,6 +48,7 @@
+ #include "winsock2.h" // htons etc.
+ #endif
+
++#include "llpreprocessor.h"
+ #include "llerror.h"
+ #include "net.h"
+ #include "string_table.h"
+--
+tg: (2c826ba..) topic/fixes/jira-sent/59_need_llpreprocessor_to_access_endian_macros (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/VWR-10759_delete_LLMediaImplGStreamer_mediaData_as_array.diff b/debian/patches/topic/fixes/jira-sent/VWR-10759_delete_LLMediaImplGStreamer_mediaData_as_array.diff
new file mode 100644
index 0000000..1260571
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/VWR-10759_delete_LLMediaImplGStreamer_mediaData_as_array.diff
@@ -0,0 +1,27 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/fixes/new/delete_LLMediaImplGStreamer_mediaData_as_array
+
+LLMediaImplGStreamer::mediaData is a pointer to an array.
+Delete with operator [] instead of operator delete.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/llmedia/llmediaimplgstreamer.cpp | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/indra/llmedia/llmediaimplgstreamer.cpp b/indra/llmedia/llmediaimplgstreamer.cpp
+index 51614c5..72d9662 100644
+--- a/indra/llmedia/llmediaimplgstreamer.cpp
++++ b/indra/llmedia/llmediaimplgstreamer.cpp
+@@ -433,7 +433,7 @@ unload ()
+
+ if (mediaData)
+ {
+- delete mediaData;
++ delete [] mediaData;
+ mediaData = NULL;
+ }
+
+--
+tg: (0819ce6..) topic/fixes/jira-sent/VWR-10759_delete_LLMediaImplGStreamer_mediaData_as_array (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/VWR-11128-Find_Python.diff b/debian/patches/topic/fixes/jira-sent/VWR-11128-Find_Python.diff
new file mode 100644
index 0000000..a37eb16
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/VWR-11128-Find_Python.diff
@@ -0,0 +1,29 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/VWR-11128-Find_Python
+
+<patch description>
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/cmake/Python.cmake | 5 +++++
+ 1 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/indra/cmake/Python.cmake b/indra/cmake/Python.cmake
+index 4f86d32..e23e2ca 100644
+--- a/indra/cmake/Python.cmake
++++ b/indra/cmake/Python.cmake
+@@ -13,6 +13,11 @@ if (WINDOWS)
+ [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.6\\InstallPath]
++ [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath]
++ [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath]
++ [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath]
++
+ )
+ elseif (EXISTS /etc/debian_version)
+ # On Debian and Ubuntu, avoid Python 2.4 if possible.
+--
+tg: (2c826ba..) topic/fixes/jira-sent/VWR-11128-Find_Python (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/VWR-11138_Develop_py_and_express_VS.diff b/debian/patches/topic/fixes/jira-sent/VWR-11138_Develop_py_and_express_VS.diff
new file mode 100644
index 0000000..85c31bb
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/VWR-11138_Develop_py_and_express_VS.diff
@@ -0,0 +1,89 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/VWR-11138_Develop_py_and_express_VS
+
+<patch description>
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/develop.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++---
+ 1 files changed, 46 insertions(+), 3 deletions(-)
+
+diff --git a/indra/develop.py b/indra/develop.py
+index 29fe0e8..bb12437 100755
+--- a/indra/develop.py
++++ b/indra/develop.py
+@@ -464,8 +464,15 @@ class WindowsSetup(PlatformSetup):
+ print 'Building with ', self.gens[version]['gen']
+ break
+ else:
+- print >> sys.stderr, 'Cannot find a Visual Studio installation!'
+- eys.exit(1)
++ print >> sys.stderr, 'Cannot find a Visual Studio installation, testing for express editions'
++ for version in 'vc80 vc90 vc71'.split():
++ if self.find_visual_studio_express(version):
++ self._generator = version
++ print 'Building with ', self.gens[version]['gen'] , "Express edition"
++ break
++ else:
++ print >> sys.stderr, 'Cannot find any Visual Studio installation'
++ eys.exit(1)
+ return self._generator
+
+ def _set_generator(self, gen):
+@@ -515,6 +522,29 @@ class WindowsSetup(PlatformSetup):
+ except WindowsError, err:
+ print >> sys.stderr, "Didn't find ", self.gens[gen]['gen']
+ return ''
++
++ def find_visual_studio_express(self, gen=None):
++ if gen is None:
++ gen = self._generator
++ gen = gen.lower()
++ try:
++ import _winreg
++ key_str = (r'SOFTWARE\Microsoft\VCEXpress\%s\Setup\VC' %
++ self.gens[gen]['ver'])
++ value_str = (r'ProductDir')
++ print ('Reading VS environment from HKEY_LOCAL_MACHINE\%s\%s' %
++ (key_str, value_str))
++ print key_str
++
++ reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
++ key = _winreg.OpenKey(reg, key_str)
++ value = _winreg.QueryValueEx(key, value_str)[0]+"IDE"
++ print 'Found: %s' % value
++ return value
++ except WindowsError, err:
++ print >> sys.stderr, "Didn't find ", self.gens[gen]['gen']
++ return ''
++
+
+ def get_build_cmd(self):
+ if self.incredibuild:
+@@ -524,9 +554,22 @@ class WindowsSetup(PlatformSetup):
+
+ return "buildconsole Secondlife.sln /build %s" % config
+
++ environment = self.find_visual_studio()
++ if environment == '':
++ environment = self.find_visual_studio_express()
++ if environment == '':
++ print >> sys.stderr, "Something went very wrong during build stage, could not find a Visual Studio?"
++ else:
++ print >> sys.stderr, "\nSolution generation complete, as you are using an express edition the final\n stages will need to be completed by hand"
++ build_dirs=self.build_dirs();
++ print >> sys.stderr, "Solution can now be found in:", build_dirs[0]
++ print >> sys.stderr, "Set secondlife-bin as startup project"
++ print >> sys.stderr, "Set build target is Release or RelWithDbgInfo"
++ exit(0)
++
+ # devenv.com is CLI friendly, devenv.exe... not so much.
+ return ('"%sdevenv.com" Secondlife.sln /build %s' %
+- (self.find_visual_studio(), self.build_type))
++ (environment, self.build_type))
+
+ # this override of run exists because the PlatformSetup version
+ # uses Unix/Mac only calls. Freakin' os module!
+--
+tg: (2c826ba..) topic/fixes/jira-sent/VWR-11138_Develop_py_and_express_VS (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/VWR-1815_top_corner_fix.diff b/debian/patches/topic/fixes/jira-sent/VWR-1815_top_corner_fix.diff
new file mode 100644
index 0000000..6e87e2a
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/VWR-1815_top_corner_fix.diff
@@ -0,0 +1,68 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/VWR-1815_top_corner_fix
+
+Remains of the top corner texture fix, this part accellerates downloads
+for stuck textures
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/lltexturefetch.cpp | 22 +++++++++++++++++++++-
+ 1 files changed, 21 insertions(+), 1 deletions(-)
+
+diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
+index 24046cc..0540574 100644
+--- a/indra/newview/lltexturefetch.cpp
++++ b/indra/newview/lltexturefetch.cpp
+@@ -53,6 +53,9 @@ class LLTextureFetchWorker : public LLWorkerClass
+ {
+ friend class LLTextureFetch;
+
++public:
++ BOOL mAccelerateDownloadStuckTexture;
++
+ private:
+ #if 0
+ class URLResponder : public LLHTTPClient::Responder
+@@ -1074,6 +1077,20 @@ bool LLTextureFetchWorker::processSimulatorPackets()
+ buffer_size += mPackets[i]->mSize;
+ }
+ bool have_all_data = mLastPacket >= mTotalPackets-1;
++ //llassert_always(mRequestedSize > 0);
++ if(mRequestedSize<=0)
++ {
++ llwarns << "asserting mRequestedSize > 0, aborting this processSimulatorPackets run" << llendl;
++ return;
++ }
++
++ if((buffer_size >= mRequestedSize) && mRequestedDiscard <=0)
++ {
++ if(!mAccelerateDownloadStuckTexture);
++ llwarns << "Accelerating download of a stuck texture :"<<mID << llendl
++ mAccelerateDownloadStuckTexture=TRUE;
++ }
++
+ if (buffer_size >= mRequestedSize || have_all_data)
+ {
+ /// We have enough (or all) data
+@@ -1575,6 +1592,7 @@ void LLTextureFetch::sendRequestListToSimulators()
+ const F32 LAZY_FLUSH_TIMEOUT = 15.f; // 10.0f // temp
+ const F32 MIN_REQUEST_TIME = 1.0f;
+ const F32 MIN_DELTA_PRIORITY = 1000.f;
++ const F32 MIN_ACCEL_KICK = 0.5f;
+
+ LLMutexLock lock(&mQueueMutex);
+
+@@ -1605,7 +1623,9 @@ void LLTextureFetch::sendRequestListToSimulators()
+ F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority);
+ if ((req->mSimRequestedDiscard != req->mDesiredDiscard) ||
+ (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) ||
+- (elapsed >= LAZY_FLUSH_TIMEOUT))
++ (elapsed >= LAZY_FLUSH_TIMEOUT) ||
++ (req->mAccelerateDownloadStuckTexture && (elapsed >=MIN_ACCEL_KICK))
++ )
+ {
+ requests[req->mHost].insert(req);
+ }
+--
+tg: (2c826ba..) topic/fixes/jira-sent/VWR-1815_top_corner_fix (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/VWR-8194_clamp_outline_for_broken_nvidia.diff b/debian/patches/topic/fixes/jira-sent/VWR-8194_clamp_outline_for_broken_nvidia.diff
new file mode 100644
index 0000000..e651b78
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/VWR-8194_clamp_outline_for_broken_nvidia.diff
@@ -0,0 +1,38 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/VWR-8194_clamp_outline_for_broken_nvidia
+
+Prevent buffer overflow in temp GL matrix
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llrender/llrender.cpp | 7 +++++++
+ 1 files changed, 7 insertions(+), 0 deletions(-)
+
+diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
+index ff0a2db..f2b4352 100644
+--- a/indra/llrender/llrender.cpp
++++ b/indra/llrender/llrender.cpp
+@@ -899,6 +899,9 @@ void LLRender::vertex3fv(const GLfloat* v)
+
+ void LLRender::texCoord2f(const GLfloat& x, const GLfloat& y)
+ {
++ if (mCount >= 4096)
++ return;
++
+ mTexcoordsp[mCount] = LLVector2(x,y);
+ }
+
+@@ -914,6 +917,10 @@ void LLRender::texCoord2fv(const GLfloat* tc)
+
+ void LLRender::color4ub(const GLubyte& r, const GLubyte& g, const GLubyte& b, const GLubyte& a)
+ {
++
++ if (mCount >= 4096)
++ return;
++
+ mColorsp[mCount] = LLColor4U(r,g,b,a);
+ }
+ void LLRender::color4ubv(const GLubyte* c)
+--
+tg: (2c826ba..) topic/fixes/jira-sent/VWR-8194_clamp_outline_for_broken_nvidia (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/VWR-9557-EnableBuildWithNvidiaOrMesaHeaders.diff b/debian/patches/topic/fixes/jira-sent/VWR-9557-EnableBuildWithNvidiaOrMesaHeaders.diff
new file mode 100644
index 0000000..2196efc
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/VWR-9557-EnableBuildWithNvidiaOrMesaHeaders.diff
@@ -0,0 +1,75 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/VWR-9557-EnableBuildWithNvidiaOrMesaHeaders
+
+Enable the build to succeed with either the mesa gl.h or the nvidia gl.h headers
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llrender/llgl.cpp | 17 ++++++++++++++---
+ indra/llrender/llglheaders.h | 9 +++++++++
+ 2 files changed, 23 insertions(+), 3 deletions(-)
+
+diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
+index a9cf073..88b1671 100644
+--- a/indra/llrender/llgl.cpp
++++ b/indra/llrender/llgl.cpp
+@@ -252,6 +252,12 @@ PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
+ PFNGLCOLORTABLEEXTPROC glColorTableEXT = NULL;
+ #endif // LL_LINUX
+
++#if LL_LINUX && defined(WINGDIAPI)
++PFNGLACTIVETEXTUREARBPROC glActiveTextureARB=NULL;
++PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB=NULL;
++PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements=NULL;
++#endif
++
+ #endif
+
+ LLGLManager gGLManager;
+@@ -762,15 +768,20 @@ void LLGLManager::initExtensions()
+ glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGetFramebufferAttachmentParameterivEXT");
+ glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glGenerateMipmapEXT");
+ }
+-#if !LL_LINUX
+- // This is expected to be a static symbol on Linux GL implementations
++#if !LL_LINUX || (LL_LINUX && defined(WINGDIAPI))
++ // This is expected to be a static symbol on Linux GL implementations, except if we use the nvidia headers
+ glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElements");
+ if (!glDrawRangeElements)
+ {
+ mGLMaxVertexRange = 0;
+ mGLMaxIndexRange = 0;
+ }
+-#endif // !LL_LINUX
++#endif //!LL_LINUX
++
++#if LL_LINUX && defined(WINGDIAPI)
++ glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)GLH_EXT_GET_PROC_ADDRESS("glActiveTextureARB");
++ glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)GLH_EXT_GET_PROC_ADDRESS("glClientActiveTextureARB");
++#endif
+
+ if (mHasOcclusionQuery)
+ {
+diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h
+index 20a420b..c3035e0 100644
+--- a/indra/llrender/llglheaders.h
++++ b/indra/llrender/llglheaders.h
+@@ -76,6 +76,15 @@
+ #undef Status
+ #endif // LL_LINUX && !LL_MESA_HEADLESS
+
++#if LL_LINUX && defined(WINGDIAPI)
++// WINGDIAPI gets set if we are using the linux nvidia gl.h header which needs the
++// functions below setting up
++// Missing functions when using nvidia header
++extern PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
++extern PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB;
++extern PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements;
++
++#endif
+
+ // GL_ARB_vertex_buffer_object
+ extern PFNGLBINDBUFFERARBPROC glBindBufferARB;
+--
+tg: (2c826ba..) topic/fixes/jira-sent/VWR-9557-EnableBuildWithNvidiaOrMesaHeaders (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/correctly_find_dbus_headers_on_standalone.diff b/debian/patches/topic/fixes/jira-sent/correctly_find_dbus_headers_on_standalone.diff
new file mode 100644
index 0000000..344748e
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/correctly_find_dbus_headers_on_standalone.diff
@@ -0,0 +1,42 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/correctly_find_dbus_headers_on_standalone
+
+Correctly find the DBUS headers in cmake, patch sent up stream to LL and in the queue to be applied
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ doc/contributions.txt | 1 +
+ indra/newview/CMakeLists.txt | 5 +++++
+ 2 files changed, 6 insertions(+), 0 deletions(-)
+
+diff --git a/doc/contributions.txt b/doc/contributions.txt
+index 5cc26c2..5f77f91 100644
+--- a/doc/contributions.txt
++++ b/doc/contributions.txt
+@@ -236,6 +236,7 @@ Michelle2 Zenovka
+ VWR-8889
+ VWR-8310
+ VWR-4022
++ VWR-9499
+ Mm Alder
+ VWR-3777
+ VWR-4794
+diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
+index b95ba37..60fec36 100644
+--- a/indra/newview/CMakeLists.txt
++++ b/indra/newview/CMakeLists.txt
+@@ -60,6 +60,11 @@ include_directories(
+ ${LSCRIPT_INCLUDE_DIRS}/lscript_compile
+ )
+
++if (LINUX)
++ include_directories (${DBUSGLIB_INCLUDE_DIRS})
++endif (LINUX)
++
++
+ set(viewer_SOURCE_FILES
+ llagent.cpp
+ llagentdata.cpp
+--
+tg: (2c826ba..) topic/fixes/jira-sent/correctly_find_dbus_headers_on_standalone (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/dont_depend_on_artwork_to_build.diff b/debian/patches/topic/fixes/jira-sent/dont_depend_on_artwork_to_build.diff
new file mode 100644
index 0000000..e9678c4
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/dont_depend_on_artwork_to_build.diff
@@ -0,0 +1,28 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/dont_depend_on_artwork_to_build
+
+Do not require the artwork to be present in the cmake tree, sent
+upstream and discussed with BoS expected to be fixed upstream at
+some point.
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/CMakeLists.txt | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
+index b95ba37..509bfad 100644
+--- a/indra/newview/CMakeLists.txt
++++ b/indra/newview/CMakeLists.txt
+@@ -1245,7 +1245,7 @@ source_group("Character File" FILES ${viewer_CHARACTER_FILES})
+ set_source_files_properties(${viewer_CHARACTER_FILES}
+ PROPERTIES HEADER_FILE_ONLY TRUE)
+
+-list(APPEND viewer_SOURCE_FILES ${viewer_CHARACTER_FILES})
++#list(APPEND viewer_SOURCE_FILES ${viewer_CHARACTER_FILES})
+
+ if (WINDOWS)
+ file(GLOB viewer_INSTALLER_FILES installers/windows/*.nsi)
+--
+tg: (2c826ba..) topic/fixes/jira-sent/dont_depend_on_artwork_to_build (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/gcc_warning_on_llwindowsdl_var_not_set.diff b/debian/patches/topic/fixes/jira-sent/gcc_warning_on_llwindowsdl_var_not_set.diff
new file mode 100644
index 0000000..1f42c06
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/gcc_warning_on_llwindowsdl_var_not_set.diff
@@ -0,0 +1,25 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/gcc_warning_on_llwindowsdl_var_not_set
+
+Prevent FTBFS error with gcc and a wild variable.
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llwindow/llwindowsdl.cpp | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
+index 777b32d..f3f3c04 100644
+--- a/indra/llwindow/llwindowsdl.cpp
++++ b/indra/llwindow/llwindowsdl.cpp
+@@ -2644,6 +2644,7 @@ BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+ orig_color.red = guint16(65535 * *r);
+ orig_color.green= guint16(65535 * *g);
+ orig_color.blue = guint16(65535 * *b);
++ orig_color.pixel = -1; //Default this to something or the copy next will produce a gcc warning
+ color = orig_color;
+
+ gtk_color_selection_set_previous_color (colorsel, &color);
+--
+tg: (2c826ba..) topic/fixes/jira-sent/gcc_warning_on_llwindowsdl_var_not_set (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/lltemplatemessagereader_memcpy.diff b/debian/patches/topic/fixes/jira-sent/lltemplatemessagereader_memcpy.diff
new file mode 100644
index 0000000..01e5599
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/lltemplatemessagereader_memcpy.diff
@@ -0,0 +1,27 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/lltemplatemessagereader_memcpy
+
+Fix gcc FTBFS bug, possibly a gcc bug.
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llmessage/lltemplatemessagereader.cpp | 3 ++-
+ 1 files changed, 2 insertions(+), 1 deletions(-)
+
+diff --git a/indra/llmessage/lltemplatemessagereader.cpp b/indra/llmessage/lltemplatemessagereader.cpp
+index 7a7d3bb..aef2d33 100644
+--- a/indra/llmessage/lltemplatemessagereader.cpp
++++ b/indra/llmessage/lltemplatemessagereader.cpp
+@@ -676,7 +676,8 @@ BOOL LLTemplateMessageReader::decodeData(const U8* buffer, const LLHost& sender
+ // default to 0s.
+ U32 size = mvci.getSize();
+ std::vector<U8> data(size);
+- memset(&(data[0]), 0, size);
++ if(size>0)
++ memset(&(data[0]), 0, size);
+ cur_data_block->addData(mvci.getName(), &(data[0]),
+ size, mvci.getType());
+ }
+--
+tg: (2c826ba..) topic/fixes/jira-sent/lltemplatemessagereader_memcpy (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/no_format_or_string_literal.diff b/debian/patches/topic/fixes/jira-sent/no_format_or_string_literal.diff
new file mode 100644
index 0000000..1e96670
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/no_format_or_string_literal.diff
@@ -0,0 +1,27 @@
+From: Guillermo Gutiérrez Herrera <terrex at xiterrex.net>
+Subject: [PATCH] topic/fixes/jira-sent/no_format_or_string_literal
+
+Correcty pass format specifier to string via string literal
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+Signed-off-by: Guillermo Gutiérrez Herrera <terrex at xiterrex.net>
+
+---
+ indra/linux_crash_logger/llcrashloggerlinux.cpp | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp
+index 0896814..2dd5e61 100644
+--- a/indra/linux_crash_logger/llcrashloggerlinux.cpp
++++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp
+@@ -90,7 +90,7 @@ static BOOL do_ask_dialog(void)
+
+ win = gtk_message_dialog_new(NULL,
+ flags, messagetype, buttons,
+- dialog_text);
++ "%s", dialog_text);
+ gtk_window_set_type_hint(GTK_WINDOW(win),
+ GDK_WINDOW_TYPE_HINT_DIALOG);
+ gtk_window_set_title(GTK_WINDOW(win), dialog_title);
+--
+tg: (2c826ba..) topic/fixes/jira-sent/no_format_or_string_literal (depends on: upstream)
diff --git a/debian/patches/topic/fixes/jira-sent/use_c_locale_and_dont_spam_me_because_i_am_not_american.diff b/debian/patches/topic/fixes/jira-sent/use_c_locale_and_dont_spam_me_because_i_am_not_american.diff
new file mode 100644
index 0000000..cbc8176
--- /dev/null
+++ b/debian/patches/topic/fixes/jira-sent/use_c_locale_and_dont_spam_me_because_i_am_not_american.diff
@@ -0,0 +1,29 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/jira-sent/use_c_locale_and_dont_spam_me_because_i_am_not_american
+
+Better protection of locales, and don't enforce en_US.UTF-8
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llui/llresmgr.cpp | 5 ++++-
+ 1 files changed, 4 insertions(+), 1 deletions(-)
+
+diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp
+index d111bd6..9d8c283 100644
+--- a/indra/llui/llresmgr.cpp
++++ b/indra/llui/llresmgr.cpp
+@@ -446,7 +446,10 @@ const std::string LLLocale::SYSTEM_LOCALE("en_US.iso8859-1");
+ const std::string LLLocale::USER_LOCALE("en_US.ISO8859-1");
+ const std::string LLLocale::SYSTEM_LOCALE("C");
+ #else // LL_LINUX likes this
+-const std::string LLLocale::USER_LOCALE("en_US.utf8");
++//const std::string LLLocale::USER_LOCALE("en_US.utf8");
++// this is a good chance that I AM NOT AN AMERICAN don't assume that i am and
++// assume i have an american locale installed, C should do for the viewer
++const std::string LLLocale::USER_LOCALE("C");
+ const std::string LLLocale::SYSTEM_LOCALE("C");
+ #endif
+
+--
+tg: (2c826ba..) topic/fixes/jira-sent/use_c_locale_and_dont_spam_me_because_i_am_not_american (depends on: upstream)
diff --git a/debian/patches/topic/fixes/new/allow_compilation_when_GST_DISABLE_GST_DEBUG_is_defined.diff b/debian/patches/topic/fixes/new/allow_compilation_when_GST_DISABLE_GST_DEBUG_is_defined.diff
new file mode 100644
index 0000000..3ebc983
--- /dev/null
+++ b/debian/patches/topic/fixes/new/allow_compilation_when_GST_DISABLE_GST_DEBUG_is_defined.diff
@@ -0,0 +1,44 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/fixes/new/allow_compilation_when_GST_DISABLE_GST_DEBUG_is_defined
+
+Allow compilation when GST_DISABLE_GST_DEBUG is defined.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/llmedia/llmediaimplgstreamer_syms.h | 2 ++
+ indra/llmedia/llmediaimplgstreamer_syms_raw.inc | 2 ++
+ 2 files changed, 4 insertions(+), 0 deletions(-)
+
+diff --git a/indra/llmedia/llmediaimplgstreamer_syms.h b/indra/llmedia/llmediaimplgstreamer_syms.h
+index 6957b1c..29ac00d 100644
+--- a/indra/llmedia/llmediaimplgstreamer_syms.h
++++ b/indra/llmedia/llmediaimplgstreamer_syms.h
+@@ -67,8 +67,10 @@ void ungrab_gst_syms();
+ #define GST_TYPE_VIDEO_SINK (llgst_video_sink_get_type())
+ #undef _gst_debug_register_funcptr
+ #define _gst_debug_register_funcptr ll_gst_debug_register_funcptr
++#ifndef GST_DISABLE_GST_DEBUG
+ #undef _gst_debug_category_new
+ #define _gst_debug_category_new ll_gst_debug_category_new
++#endif
+ #undef __gst_debug_enabled
+ #define __gst_debug_enabled (0)
+
+diff --git a/indra/llmedia/llmediaimplgstreamer_syms_raw.inc b/indra/llmedia/llmediaimplgstreamer_syms_raw.inc
+index 08ba6df..eb2b3ee 100644
+--- a/indra/llmedia/llmediaimplgstreamer_syms_raw.inc
++++ b/indra/llmedia/llmediaimplgstreamer_syms_raw.inc
+@@ -19,8 +19,10 @@ LL_GST_SYM(true, gst_element_class_add_pad_template, void, GstElementClass *klas
+ LL_GST_SYM(true, gst_element_class_set_details, void, GstElementClass *klass, const GstElementDetails *details);
+ LL_GST_SYM(true, gst_caps_unref, void, GstCaps* caps);
+ LL_GST_SYM(true, gst_caps_ref, GstCaps *, GstCaps* caps);
++#ifndef GST_DISABLE_GST_DEBUG
+ LL_GST_SYM(true, _gst_debug_register_funcptr, void, GstDebugFuncPtr func, gchar* ptrname);
+ LL_GST_SYM(true, _gst_debug_category_new, GstDebugCategory *, gchar *name, guint color, gchar *description);
++#endif
+ LL_GST_SYM(true, gst_caps_is_empty, gboolean, const GstCaps *caps);
+ LL_GST_SYM(true, gst_caps_from_string, GstCaps *, const gchar *string);
+ LL_GST_SYM(true, gst_caps_replace, void, GstCaps **caps, GstCaps *newcaps);
+--
+tg: (2c826ba..) topic/fixes/new/allow_compilation_when_GST_DISABLE_GST_DEBUG_is_defined (depends on: upstream)
diff --git a/debian/patches/topic/fixes/new/delete_LLImageTGA_mColorMap_as_array.diff b/debian/patches/topic/fixes/new/delete_LLImageTGA_mColorMap_as_array.diff
new file mode 100644
index 0000000..9d9b322
--- /dev/null
+++ b/debian/patches/topic/fixes/new/delete_LLImageTGA_mColorMap_as_array.diff
@@ -0,0 +1,26 @@
+From: Carlo Wood <carlo at alinoe.com>
+Subject: [PATCH] topic/fixes/new/delete_LLImageTGA_mColorMap_as_array
+
+The destructor of LLImageTGA should delete mColorMap with 'operator delete[]', not 'operator delete'.
+
+Signed-off-by: Carlo Wood <carlo at alinoe.com>
+
+---
+ indra/llimage/llimagetga.cpp | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp
+index 853a0dc..c72a1cd 100644
+--- a/indra/llimage/llimagetga.cpp
++++ b/indra/llimage/llimagetga.cpp
+@@ -104,7 +104,7 @@ LLImageTGA::LLImageTGA(const std::string& file_name)
+
+ LLImageTGA::~LLImageTGA()
+ {
+- delete mColorMap;
++ delete [] mColorMap;
+ }
+
+ BOOL LLImageTGA::updateData()
+--
+tg: (2c826ba..) topic/fixes/new/delete_LLImageTGA_mColorMap_as_array (depends on: upstream)
diff --git a/debian/patches/topic/fixes/new/fix_cmake_install_target.diff b/debian/patches/topic/fixes/new/fix_cmake_install_target.diff
new file mode 100644
index 0000000..a9d0c4f
--- /dev/null
+++ b/debian/patches/topic/fixes/new/fix_cmake_install_target.diff
@@ -0,0 +1,23 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/new/fix_cmake_install_target
+
+Fix install cmake target for renamed binaries
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/ViewerInstall.cmake | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/indra/newview/ViewerInstall.cmake b/indra/newview/ViewerInstall.cmake
+index 55069ad..8168e91 100644
+--- a/indra/newview/ViewerInstall.cmake
++++ b/indra/newview/ViewerInstall.cmake
+@@ -1,4 +1,4 @@
+-install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/secondlife-bin
++install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}
+ DESTINATION ${APP_BINARY_DIR}
+ )
+
+--
+tg: (2c826ba..) topic/fixes/new/fix_cmake_install_target (depends on: upstream)
diff --git a/debian/patches/topic/fixes/thirdparty/0000_keep_statbars_from_overshooting.patch.diff b/debian/patches/topic/fixes/thirdparty/0000_keep_statbars_from_overshooting.patch.diff
new file mode 100644
index 0000000..cb73438
--- /dev/null
+++ b/debian/patches/topic/fixes/thirdparty/0000_keep_statbars_from_overshooting.patch.diff
@@ -0,0 +1,54 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/thirdparty/0000_keep_statbars_from_overshooting.patch
+
+fix an issue with status bars overshooting their window
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/llstatbar.cpp | 13 +++++++------
+ 1 files changed, 7 insertions(+), 6 deletions(-)
+
+diff --git a/indra/newview/llstatbar.cpp b/indra/newview/llstatbar.cpp
+index 2673166..88b329a 100644
+--- a/indra/newview/llstatbar.cpp
++++ b/indra/newview/llstatbar.cpp
+@@ -213,6 +213,7 @@ void LLStatBar::draw()
+ }
+
+ right = (S32) ((max - mMinBar) * value_scale);
++ right = llclamp(0, right, width);
+ gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 0.f, 0.f, 0.25f));
+
+ S32 num_values = mStatp->getNumValues() - 1;
+@@ -228,22 +229,22 @@ void LLStatBar::draw()
+ if (mPerSec)
+ {
+ left = (S32)((mStatp->getPrevPerSec(i) - mMinBar) * value_scale);
+- right = (S32)((mStatp->getPrevPerSec(i) - mMinBar) * value_scale) + 1;
+- gl_rect_2d(left, bottom+i+1, right, bottom+i, LLColor4(1.f, 0.f, 0.f, 1.f));
+ }
+ else
+ {
+ left = (S32)((mStatp->getPrev(i) - mMinBar) * value_scale);
+- right = (S32)((mStatp->getPrev(i) - mMinBar) * value_scale) + 1;
+- gl_rect_2d(left, bottom+i+1, right, bottom+i, LLColor4(1.f, 0.f, 0.f, 1.f));
+- }
++ }
++ left = llclamp(0, left, width-1);
++ right = left+1;
++ gl_rect_2d(left, bottom+i+1, right, bottom+i, LLColor4(1.f, 0.f, 0.f, 1.f));
+ }
+ }
+ else
+ {
+ // draw current
+ left = (S32) ((current - mMinBar) * value_scale) - 1;
+- right = (S32) ((current - mMinBar) * value_scale) + 1;
++ left = llclamp(0, left, width-1);
++ right = left + 2;
+ gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 0.f, 0.f, 1.f));
+ }
+
+--
+tg: (2c826ba..) topic/fixes/thirdparty/0000_keep_statbars_from_overshooting.patch (depends on: upstream)
diff --git a/debian/patches/topic/fixes/thirdparty/0001_possible_crash_and_leak_llassetstorage.patch.diff b/debian/patches/topic/fixes/thirdparty/0001_possible_crash_and_leak_llassetstorage.patch.diff
new file mode 100644
index 0000000..48a0462
--- /dev/null
+++ b/debian/patches/topic/fixes/thirdparty/0001_possible_crash_and_leak_llassetstorage.patch.diff
@@ -0,0 +1,148 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/thirdparty/0001_possible_crash_and_leak_llassetstorage.patch
+
+possible crash and memory leak
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llmessage/llassetstorage.cpp | 41 ++++++++++++++++++-----------------
+ 1 files changed, 21 insertions(+), 20 deletions(-)
+
+diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp
+index a89a786..5970a34 100644
+--- a/indra/llmessage/llassetstorage.cpp
++++ b/indra/llmessage/llassetstorage.cpp
+@@ -522,16 +522,19 @@ void LLAssetStorage::downloadCompleteCallback(
+ S32 result,
+ const LLUUID& file_id,
+ LLAssetType::EType file_type,
+- void* user_data, LLExtStat ext_status)
++ void* callback_parm_req, LLExtStat ext_status)
+ {
+ lldebugs << "LLAssetStorage::downloadCompleteCallback() for " << file_id
+ << "," << LLAssetType::lookup(file_type) << llendl;
+- LLAssetRequest* req = (LLAssetRequest*)user_data;
++
++ // be careful! req may be a ptr to memory already freed (a timeout does this)
++ LLAssetRequest* req = (LLAssetRequest*)callback_parm_req;
+ if(!req)
+ {
+ llwarns << "LLAssetStorage::downloadCompleteCallback called without"
+ "a valid request." << llendl;
+- return;
++ // we can live with a null pointer, we're not allowed to deref the ptr anyway (see above)
++ // return;
+ }
+ if (!gAssetStorage)
+ {
+@@ -539,12 +542,10 @@ void LLAssetStorage::downloadCompleteCallback(
+ return;
+ }
+
+- req->setUUID(file_id);
+- req->setType(file_type);
+ if (LL_ERR_NOERR == result)
+ {
+ // we might have gotten a zero-size file
+- LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
++ LLVFile vfile(gAssetStorage->mVFS, file_id, file_type);
+ if (vfile.getSize() <= 0)
+ {
+ llwarns << "downloadCompleteCallback has non-existent or zero-size asset " << req->getUUID() << llendl;
+@@ -563,7 +564,7 @@ void LLAssetStorage::downloadCompleteCallback(
+ {
+ request_list_t::iterator curiter = iter++;
+ LLAssetRequest* tmp = *curiter;
+- if ((tmp->getUUID() == req->getUUID()) && (tmp->getType()== req->getType()))
++ if ((tmp->getUUID() == file_id) && (tmp->getType() == file_type))
+ {
+ requests.push_front(tmp);
+ iter = gAssetStorage->mPendingDownloads.erase(curiter);
+@@ -576,7 +577,7 @@ void LLAssetStorage::downloadCompleteCallback(
+ LLAssetRequest* tmp = *curiter;
+ if (tmp->mDownCallback)
+ {
+- tmp->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), tmp->mUserData, result, ext_status);
++ tmp->mDownCallback(gAssetStorage->mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, result, ext_status);
+ }
+ delete tmp;
+ }
+@@ -672,10 +673,10 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback(
+ S32 result,
+ const LLUUID& file_id,
+ LLAssetType::EType file_type,
+- void* user_data,
++ void* callback_parm_req,
+ LLExtStat ext_status)
+ {
+- LLEstateAssetRequest *req = (LLEstateAssetRequest*)user_data;
++ LLEstateAssetRequest *req = (LLEstateAssetRequest*)callback_parm_req;
+ if(!req)
+ {
+ llwarns << "LLAssetStorage::downloadEstateAssetCompleteCallback called"
+@@ -689,12 +690,10 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback(
+ return;
+ }
+
+- req->setUUID(file_id);
+- req->setType(file_type);
+ if (LL_ERR_NOERR == result)
+ {
+ // we might have gotten a zero-size file
+- LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType());
++ LLVFile vfile(gAssetStorage->mVFS, file_id, file_type);
+ if (vfile.getSize() <= 0)
+ {
+ llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
+@@ -704,7 +703,9 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback(
+ }
+ }
+
+- req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getAType(), req->mUserData, result, ext_status);
++ req->mDownCallback(gAssetStorage->mVFS, file_id, file_type, req->mUserData, result, ext_status);
++
++ delete req;
+ }
+
+ void LLAssetStorage::getInvItemAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
+@@ -809,10 +810,10 @@ void LLAssetStorage::downloadInvItemCompleteCallback(
+ S32 result,
+ const LLUUID& file_id,
+ LLAssetType::EType file_type,
+- void* user_data,
++ void* callback_parm_req,
+ LLExtStat ext_status)
+ {
+- LLInvItemRequest *req = (LLInvItemRequest*)user_data;
++ LLInvItemRequest *req = (LLInvItemRequest*)callback_parm_req;
+ if(!req)
+ {
+ llwarns << "LLAssetStorage::downloadEstateAssetCompleteCallback called"
+@@ -825,12 +826,10 @@ void LLAssetStorage::downloadInvItemCompleteCallback(
+ return;
+ }
+
+- req->setUUID(file_id);
+- req->setType(file_type);
+ if (LL_ERR_NOERR == result)
+ {
+ // we might have gotten a zero-size file
+- LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
++ LLVFile vfile(gAssetStorage->mVFS, file_id, file_type);
+ if (vfile.getSize() <= 0)
+ {
+ llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
+@@ -840,7 +839,9 @@ void LLAssetStorage::downloadInvItemCompleteCallback(
+ }
+ }
+
+- req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), req->mUserData, result, ext_status);
++ req->mDownCallback(gAssetStorage->mVFS, file_id, file_type, req->mUserData, result, ext_status);
++
++ delete req;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+--
+tg: (2c826ba..) topic/fixes/thirdparty/0001_possible_crash_and_leak_llassetstorage.patch (depends on: upstream)
diff --git a/debian/patches/topic/fixes/thirdparty/0001_possible_crash_in_llviewerpartssim.diff b/debian/patches/topic/fixes/thirdparty/0001_possible_crash_in_llviewerpartssim.diff
new file mode 100644
index 0000000..372acd8
--- /dev/null
+++ b/debian/patches/topic/fixes/thirdparty/0001_possible_crash_in_llviewerpartssim.diff
@@ -0,0 +1,38 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/thirdparty/0001_possible_crash_in_llviewerpartssim
+
+Another possible crash fix
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/llviewerpartsim.cpp | 13 ++++++++++---
+ 1 files changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp
+index c303fc5..2c79791 100644
+--- a/indra/newview/llviewerpartsim.cpp
++++ b/indra/newview/llviewerpartsim.cpp
+@@ -714,10 +714,17 @@ void LLViewerPartSim::updateSimulation()
+ S32 visirate = 1;
+ if (vobj)
+ {
+- LLSpatialGroup* group = vobj->mDrawable->getSpatialGroup();
+- if (group && !group->isVisible()) // && !group->isState(LLSpatialGroup::OBJECT_DIRTY))
++ if(vobj->mDrawable)
+ {
+- visirate = 8;
++ LLSpatialGroup* group = vobj->mDrawable->getSpatialGroup();
++ if (group && !group->isVisible()) // && !group->isState(LLSpatialGroup::OBJECT_DIRTY))
++ {
++ visirate = 8;
++ }
++ }
++ else
++ {
++ llwarns << "Crash avoided, vobj->mDrawable is NULL" << llendl;
+ }
+ }
+
+--
+tg: (2c826ba..) topic/fixes/thirdparty/0001_possible_crash_in_llviewerpartssim (depends on: upstream)
diff --git a/debian/patches/topic/fixes/thirdparty/1294_llworkerthread_when_terminating_program.patch.diff b/debian/patches/topic/fixes/thirdparty/1294_llworkerthread_when_terminating_program.patch.diff
new file mode 100644
index 0000000..2d19ac3
--- /dev/null
+++ b/debian/patches/topic/fixes/thirdparty/1294_llworkerthread_when_terminating_program.patch.diff
@@ -0,0 +1,26 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/thirdparty/1294_llworkerthread_when_terminating_program.patch
+
+Kill a llworker thread when exiting
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llcommon/llworkerthread.cpp | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp
+index 96a957b..a28259b 100644
+--- a/indra/llcommon/llworkerthread.cpp
++++ b/indra/llcommon/llworkerthread.cpp
+@@ -111,7 +111,7 @@ S32 LLWorkerThread::update(U32 max_time_ms)
+ }
+ LLWorkerClass::sDeleteLock = FALSE ;
+ // delete and aborted entries mean there's still work to do
+- res += delete_list.size() + abort_list.size();
++ res += !mDeleteList.empty() + !delete_list.empty(); // deleted entries mean there's still work to do
+ return res;
+ }
+
+--
+tg: (2c826ba..) topic/fixes/thirdparty/1294_llworkerthread_when_terminating_program.patch (depends on: upstream)
diff --git a/debian/patches/topic/fixes/thirdparty/1857_apr_thread_mutex_nested.patch.diff b/debian/patches/topic/fixes/thirdparty/1857_apr_thread_mutex_nested.patch.diff
new file mode 100644
index 0000000..21f163b
--- /dev/null
+++ b/debian/patches/topic/fixes/thirdparty/1857_apr_thread_mutex_nested.patch.diff
@@ -0,0 +1,56 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/thirdparty/1857_apr_thread_mutex_nested.patch
+
+Fix issue with nested apt thread mutex
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llcommon/llapr.cpp | 2 +-
+ indra/llcommon/llthread.cpp | 2 +-
+ indra/llmessage/llpumpio.cpp | 4 ++--
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
+index 589d3f5..0613df5 100644
+--- a/indra/llcommon/llapr.cpp
++++ b/indra/llcommon/llapr.cpp
+@@ -47,7 +47,7 @@ void ll_init_apr()
+ apr_pool_create(&gAPRPoolp, NULL);
+
+ // Initialize the logging mutex
+- apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, gAPRPoolp);
++ apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_NESTED, gAPRPoolp);
+ }
+ }
+
+diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
+index b31c9ab..b187e50 100644
+--- a/indra/llcommon/llthread.cpp
++++ b/indra/llcommon/llthread.cpp
+@@ -276,7 +276,7 @@ LLMutex::LLMutex(apr_pool_t *poolp) :
+ mIsLocalPool = TRUE;
+ apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
+ }
+- apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mAPRPoolp);
++ apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_NESTED, mAPRPoolp);
+ }
+
+
+diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp
+index 01a43ec..bb754c2 100644
+--- a/indra/llmessage/llpumpio.cpp
++++ b/indra/llmessage/llpumpio.cpp
+@@ -830,8 +830,8 @@ void LLPumpIO::initialize(apr_pool_t* pool)
+ if(!pool) return;
+ #if LL_THREADS_APR
+ // SJB: Windows defaults to NESTED and OSX defaults to UNNESTED, so use UNNESTED explicitly.
+- apr_thread_mutex_create(&mChainsMutex, APR_THREAD_MUTEX_UNNESTED, pool);
+- apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_UNNESTED, pool);
++ apr_thread_mutex_create(&mChainsMutex, APR_THREAD_MUTEX_NESTED, pool);
++ apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_NESTED, pool);
+ #endif
+ mPool = pool;
+ }
+--
+tg: (2c826ba..) topic/fixes/thirdparty/1857_apr_thread_mutex_nested.patch (depends on: upstream)
diff --git a/debian/patches/topic/fixes/thirdparty/2003_possible_crash_draganddrop.patch.diff b/debian/patches/topic/fixes/thirdparty/2003_possible_crash_draganddrop.patch.diff
new file mode 100644
index 0000000..2a5eb5d
--- /dev/null
+++ b/debian/patches/topic/fixes/thirdparty/2003_possible_crash_draganddrop.patch.diff
@@ -0,0 +1,30 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/thirdparty/2003_possible_crash_draganddrop.patch
+
+Possible crash fix
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/lltooldraganddrop.cpp | 6 ++++++
+ 1 files changed, 6 insertions(+), 0 deletions(-)
+
+diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
+index c83d0f4..efd8060 100644
+--- a/indra/newview/lltooldraganddrop.cpp
++++ b/indra/newview/lltooldraganddrop.cpp
+@@ -850,6 +850,12 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop,
+ {
+ LLInventoryObject* cargo = locateInventory(item, cat);
+
++ if (!cargo)
++ {
++ handled = FALSE;
++ break;
++ }
++
+ EAcceptance item_acceptance = ACCEPT_NO;
+ handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE,
+ mCargoTypes[mCurItemIndex],
+--
+tg: (2c826ba..) topic/fixes/thirdparty/2003_possible_crash_draganddrop.patch (depends on: upstream)
diff --git a/debian/patches/topic/fixes/thirdparty/2543_possible_crash_in_group_voting_propsals.patch.diff b/debian/patches/topic/fixes/thirdparty/2543_possible_crash_in_group_voting_propsals.patch.diff
new file mode 100644
index 0000000..f209d39
--- /dev/null
+++ b/debian/patches/topic/fixes/thirdparty/2543_possible_crash_in_group_voting_propsals.patch.diff
@@ -0,0 +1,126 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/thirdparty/2543_possible_crash_in_group_voting_propsals.patch
+
+Possible crash fix
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/llui/llscrolllistctrl.cpp | 18 ++++++++++++++++++
+ indra/newview/llpanelgroupvoting.cpp | 9 ++++++++-
+ 2 files changed, 26 insertions(+), 1 deletions(-)
+
+diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
+index 7dba55f..4691701 100644
+--- a/indra/llui/llscrolllistctrl.cpp
++++ b/indra/llui/llscrolllistctrl.cpp
+@@ -676,6 +676,11 @@ void LLScrollListCtrl::clearRows()
+
+ LLScrollListItem* LLScrollListCtrl::getFirstSelected() const
+ {
++ if (!getCanSelect())
++ {
++ return NULL;
++ }
++
+ item_list::const_iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+@@ -691,6 +696,13 @@ LLScrollListItem* LLScrollListCtrl::getFirstSelected() const
+ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
+ {
+ std::vector<LLScrollListItem*> ret;
++
++ if (!getCanSelect())
++ {
++ return ret;
++ }
++
++
+ item_list::const_iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+@@ -705,6 +717,12 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
+
+ S32 LLScrollListCtrl::getFirstSelectedIndex() const
+ {
++ if (!getCanSelect())
++ {
++ return -1;
++ }
++
++
+ S32 CurSelectedIndex = 0;
+ item_list::const_iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+diff --git a/indra/newview/llpanelgroupvoting.cpp b/indra/newview/llpanelgroupvoting.cpp
+index 41650ad..d3783d5 100644
+--- a/indra/newview/llpanelgroupvoting.cpp
++++ b/indra/newview/llpanelgroupvoting.cpp
+@@ -592,6 +592,7 @@ void LLPanelGroupVoting::impl::sendGroupProposalsRequest(const LLUUID& group_id)
+ //we're pining the server in high latency situations
+ addPendingActiveScrollListItem(0, 0, ADD_BOTTOM);
+ mProposals->setCanSelect(FALSE);
++ mBtnViewProposalItem->setEnabled(FALSE);
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_GroupActiveProposalsRequest);
+@@ -870,6 +871,7 @@ void LLPanelGroupVoting::impl::sendGroupVoteHistoryRequest(const LLUUID& group_i
+ //add some text so the user knows we're doing something
+ addPendingHistoryScrollListItem(0, 0, ADD_BOTTOM);
+ mVotesHistory->setCanSelect(FALSE);
++ mBtnViewHistoryItem->setEnabled(FALSE);
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_GroupVoteHistoryRequest);
+@@ -1066,6 +1068,7 @@ void LLPanelGroupVoting::impl::processGroupActiveProposalItemReply(LLMessageSyst
+ //no active proposals and make the scroll list unselectable
+ self->addNoActiveScrollListItem(ADD_BOTTOM);
+ self->mProposals->setCanSelect(FALSE);
++ self->mBtnViewProposalItem->setEnabled(FALSE);
+ }
+ else if ( (U32)received != num_expected )
+ {
+@@ -1073,6 +1076,7 @@ void LLPanelGroupVoting::impl::processGroupActiveProposalItemReply(LLMessageSyst
+ num_expected,
+ ADD_BOTTOM);
+ self->mProposals->setCanSelect(FALSE);
++ self->mBtnViewProposalItem->setEnabled(FALSE);
+ }
+ else
+ {
+@@ -1086,6 +1090,7 @@ void LLPanelGroupVoting::impl::processGroupActiveProposalItemReply(LLMessageSyst
+ }
+
+ self->mProposals->setCanSelect(TRUE);
++ self->mBtnViewProposalItem->setEnabled(TRUE);
+ }
+ }
+
+@@ -1134,7 +1139,7 @@ void LLPanelGroupVoting::impl::processGroupVoteHistoryItemReply(LLMessageSystem
+ //no active proposals and make the scroll list unselectable
+ self->addNoHistoryScrollListItem(ADD_BOTTOM);
+ self->mVotesHistory->setCanSelect(FALSE);
+-
++ self->mBtnViewHistoryItem->setEnabled(FALSE);
+ return;
+ }
+
+@@ -1248,6 +1253,7 @@ void LLPanelGroupVoting::impl::processGroupVoteHistoryItemReply(LLMessageSystem
+ num_expected,
+ ADD_BOTTOM);
+ self->mVotesHistory->setCanSelect(FALSE);
++ self->mBtnViewHistoryItem->setEnabled(FALSE);
+ }
+ else
+ {
+@@ -1261,6 +1267,7 @@ void LLPanelGroupVoting::impl::processGroupVoteHistoryItemReply(LLMessageSystem
+ }
+
+ self->mVotesHistory->setCanSelect(TRUE);
++ self->mBtnViewHistoryItem->setEnabled(TRUE);
+ }
+ }
+
+--
+tg: (2c826ba..) topic/fixes/thirdparty/2543_possible_crash_in_group_voting_propsals.patch (depends on: upstream)
diff --git a/debian/patches/topic/fixes/thirdparty/2683_possible_crash_update_speaker_list.patch.diff b/debian/patches/topic/fixes/thirdparty/2683_possible_crash_update_speaker_list.patch.diff
new file mode 100644
index 0000000..916fc78
--- /dev/null
+++ b/debian/patches/topic/fixes/thirdparty/2683_possible_crash_update_speaker_list.patch.diff
@@ -0,0 +1,51 @@
+From: Robin Cornelius <robin.cornelius at gmail.com>
+Subject: [PATCH] topic/fixes/thirdparty/2683_possible_crash_update_speaker_list.patch
+
+Possible crash fix
+
+Signed-off-by: Robin Cornelius <robin.cornelius at gmail.com>
+
+---
+ indra/newview/llfloateractivespeakers.cpp | 4 ++--
+ indra/newview/llviewerobjectlist.cpp | 4 ++++
+ 2 files changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/indra/newview/llfloateractivespeakers.cpp b/indra/newview/llfloateractivespeakers.cpp
+index 0a19fb2..a8f3f91 100644
+--- a/indra/newview/llfloateractivespeakers.cpp
++++ b/indra/newview/llfloateractivespeakers.cpp
+@@ -1386,7 +1386,7 @@ void LLLocalSpeakerMgr::updateSpeakerList()
+ for(avatar_it = LLCharacter::sInstances.begin(); avatar_it != LLCharacter::sInstances.end(); ++avatar_it)
+ {
+ LLVOAvatar* avatarp = (LLVOAvatar*)*avatar_it;
+- if (dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) <= CHAT_NORMAL_RADIUS)
++ if (!avatarp->isDead() && dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) <= CHAT_NORMAL_RADIUS)
+ {
+ setSpeaker(avatarp->getID());
+ }
+@@ -1400,7 +1400,7 @@ void LLLocalSpeakerMgr::updateSpeakerList()
+ if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
+ {
+ LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id);
+- if (!avatarp || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS)
++ if (!avatarp || avatarp->isDead() || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS)
+ {
+ speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
+ speakerp->mDotColor = INACTIVE_COLOR;
+diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
+index e28a768..9b85fec 100644
+--- a/indra/newview/llviewerobjectlist.cpp
++++ b/indra/newview/llviewerobjectlist.cpp
+@@ -873,6 +873,10 @@ void LLViewerObjectList::killObjects(LLViewerRegion *regionp)
+ if (objectp->mRegionp == regionp)
+ {
+ killObject(objectp);
++
++ // invalidate region pointer. region will become invalid, but
++ // refcounted objects may survive the cleanDeadObjects() call below
++ objectp->mRegionp = NULL;
+ }
+ }
+
+--
+tg: (2c826ba..) topic/fixes/thirdparty/2683_possible_crash_update_speaker_list.patch (depends on: upstream)
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 258a5e2..33e5fdc 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -34,7 +34,7 @@
const S32 LL_VERSION_MAJOR = 1;
const S32 LL_VERSION_MINOR = 22;
-const S32 LL_VERSION_PATCH = 10;
+const S32 LL_VERSION_PATCH = 11;
const S32 LL_VERSION_BUILD = 0;
const char * const LL_CHANNEL = "Second Life Release";
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index a40fec8..62064d9 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -1009,7 +1009,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
else
{
- while( (text[right] != '\n') && (right <= getLength() ) )
+ while( right < getLength() && (text[right] != '\n') )
{
right++;
}
--
A client for connecting to 3D metaverses such as Linden Labs Secondlife(tm) and OpenSim grids
More information about the Pkg-games-commits
mailing list