merge LL 3.3.4 beta

autoreplace does not work (needs a hook in nearby chat), no cmd line option to add new stuff
spellcheck only works when packaged
Tank_Master 2012-07-16 21:59:14 -07:00
commit cb24ec2e0d
233 changed files with 19114 additions and 6614 deletions

View File

@ -30,6 +30,7 @@ indra/lib/mono/indra/*.exe
indra/lib/mono/indra/*.pdb
indra/lib/python/eventlet/
indra/llwindow/glh/glh_linear.h
indra/newview/app_settings/dictionaries
indra/newview/app_settings/mozilla
indra/newview/app_settings/mozilla-runtime-*
indra/newview/app_settings/mozilla_debug

22
.hgtags
View File

@ -282,6 +282,7 @@ c6175c955a19e9b9353d242889ec1779b5762522 3.2.5-release
16f8e2915f3f2e4d732fb3125daf229cb0fd1875 DRTVWR-114_3.2.8-beta1
37dd400ad721e2a89ee820ffc1e7e433c68f3ca2 3.2.9-start
16f8e2915f3f2e4d732fb3125daf229cb0fd1875 3.2.8-beta1
089e5c84b2dece68f2b016c842ef9b5de4786842 DRTVWR-161
987425b1acf4752379b2e1eb20944b4b35d67a85 DRTVWR-115_3.2.8-beta2
987425b1acf4752379b2e1eb20944b4b35d67a85 3.2.8-beta2
51b2fd52e36aab8f670e0874e7e1472434ec4b4a DRTVWR-113_3.2.8-release
@ -304,6 +305,9 @@ d5f263687f43f278107363365938f0a214920a4b 3.3.0-beta1
28b95a6a28dca3338d9a1f4f204b96678df9f6a5 viewer-beta-candidate
b43cd25be49e3984ff5361cefad020e069131d98 3.3.1-start
3e2fca4ed1a0dc9fe6d8a6664e71098bb035a367 DRTVWR-125
dffd0457ee0745de65bf95f0642a5c9e46b8e2f0 viewer-beta-candidate
3e2fca4ed1a0dc9fe6d8a6664e71098bb035a367 viewer-beta-candidate
3e2fca4ed1a0dc9fe6d8a6664e71098bb035a367 viewer-beta-candidate
3e2fca4ed1a0dc9fe6d8a6664e71098bb035a367 3.3.1-start
28b95a6a28dca3338d9a1f4f204b96678df9f6a5 3.3.1-beta1
1dc545e44617975da2a4a32fe303386c687a6ca1 viewer-beta-candidate
@ -318,7 +322,13 @@ c623bbc854b6f7ee1b33a3718f76715046aa2937 viewer-release-candidate
675668bd24d3bea570814f71762a2a806f7e1b8d viewer-release-candidate
675668bd24d3bea570814f71762a2a806f7e1b8d 3.3.2-release
675668bd24d3bea570814f71762a2a806f7e1b8d viewer-release-candidate
050e48759337249130f684b4a21080b683f61732 DRTVWR-168
b9d0170b62eb1c7c3adaa37a0b13a833e5e659f9 DRTVWR-171
c08e2ac17a99973b2a94477659220b99b8847ae2 DRTVWR-163
600f3b3920d94de805ac6dc8bb6def9c069dd360 DRTVWR-162
600f3b3920d94de805ac6dc8bb6def9c069dd360 DRTVWR-162
9a78ac13f047056f788c4734dd91aebfe30970e3 DRTVWR-157
a716684aa7c07c440b1de5815b8a1f3dd3fd8bfb DRTVWR-159
24a7281bef42bd4430ceb25db8b195449c2c7de3 DRTVWR-153
15e90b52dc0297921b022b90d10d797436b8a1bd viewer-release-candidate
6414ecdabc5d89515b08d1f872cf923ed3a5523a DRTVWR-148
@ -335,3 +345,15 @@ fdcc08a4f20ae9bb060f4693c8980d216534efdf 3.3.3-beta2
af5f3e43e6e4424b1da19d9e16f6b853a7b822ed DRTVWR-169
4b3c68199a86cabaa5d9466d7b0f7e141e901d7a 3.3.3-beta3
6428242e124b523813bfaf4c45b3d422f0298c81 3.3.3-release
57d221de3df94f90b55204313c2cef044a3c0ae2 DRTVWR-176
09ef7fd1b0781f33b8a3a9af6236b7bcb4831910 DRTVWR-170
f87bfbe0b62d26f451d02a47c80ebef6b9168fc2 3.3.4-beta1
f87bfbe0b62d26f451d02a47c80ebef6b9168fc2 DRTVWR-158
f87bfbe0b62d26f451d02a47c80ebef6b9168fc2 3.3.4-beta1
cbea6356ce9cb0c313b6777f10c5c14783264fcc DRTVWR-174
bce218b2b45b730b22cc51e4807aa8b571cadef3 DRTVWR-173
f91d003091a61937a044652c4c674447f7dcbb7a 3.3.4-beta1
82b5330bc8b17d0d4b598832e9c5a92e90075682 3.3.4-beta2
eb539c65e6ee26eea2bf373af2d0f4b52dc91289 DRTVWR-177
a8057e1b9a1246b434a27405be35e030f7d28b0c 3.3.4-beta3
4281aa899fb2cedb7a9ca7ce91c5c29d4aa69594 DRTVWR-180

View File

@ -112,6 +112,17 @@ viewer-mesh.login_channel = "Project Viewer - Mesh"
viewer-mesh.viewer_grid = aditi
viewer-mesh.email = shining@lists.lindenlab.com
# ========================================
# viewer-adult-check
# ========================================
viewer-adult-check.viewer_channel = "Project Viewer - AdultCheck"
viewer-adult-check.login_channel = "Project Viewer - AdultCheck"
viewer-adult-check.viewer_grid = agni
viewer-adult-check.build_debug_release_separately = true
viewer-adult-check.build_CYGWIN_Debug = false
viewer-adult-check.build_viewer_update_version_manager = false
# ================
# oz
# ================
@ -138,6 +149,10 @@ oz_viewer-beta-review.viewer_channel = "Second Life Beta Viewer"
oz_viewer-beta-review.login_channel = "Second Life Beta Viewer"
oz_viewer-beta-review.email = oz@lindenlab.com
oz_project-7.build_debug_release_separately = true
oz_project-7.codeticket_add_context = false
oz_project-7.email = "sldev@catznip.com oz@lindenlab.com"
# =================================================================
# asset delivery 2010 projects
# =================================================================

View File

@ -407,6 +407,54 @@
</map>
</map>
</map>
<key>dictionaries</key>
<map>
<key>license</key>
<string>various open</string>
<key>license_file</key>
<string>LICENSES/dictionaries.txt</string>
<key>name</key>
<string>dictionaries</string>
<key>platforms</key>
<map>
<key>darwin</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>06a6c49eb1873e95623d3d2d07aee903</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-dictionaries/rev/259873/arch/Darwin/installer/dictionaries-1-darwin-20120616.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
</map>
<key>linux</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>4f0ca21d27e0cd0b002149062b0a4b25</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-dictionaries/rev/259873/arch/Linux/installer/dictionaries-1-linux-20120616.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>7520d75f6af325328322201c888191d4</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-dictionaries/rev/259873/arch/CYGWIN/installer/dictionaries-1-windows-20120616.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
</map>
<key>elfio</key>
<map>
<key>license</key>
@ -1043,6 +1091,54 @@
</map>
</map>
</map>
<key>libhunspell</key>
<map>
<key>license</key>
<string>libhunspell</string>
<key>license_file</key>
<string>LICENSES/hunspell.txt</string>
<key>name</key>
<string>libhunspell</string>
<key>platforms</key>
<map>
<key>darwin</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>6f5db0ef258df6e5c93c843ec559db6d</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-hunspell/rev/259874/arch/Darwin/installer/libhunspell-1.3.2-darwin-20120616.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
</map>
<key>linux</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>0c432d2626aea2e91a56335879c92965</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-hunspell/rev/259874/arch/Linux/installer/libhunspell-1.3.2-linux-20120616.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>6a140e5620826aa5e587b4157f57b389</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-hunspell/rev/259874/arch/CYGWIN/installer/libhunspell-1.3.2-windows-20120616.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
</map>
<key>libpng</key>
<map>
<key>license</key>
@ -1143,8 +1239,6 @@
<map>
<key>license</key>
<string>havok</string>
<key>license_file</key>
<string>on_file</string>
<key>name</key>
<string>llconvexdecomposition</string>
<key>platforms</key>
@ -1854,8 +1948,12 @@
</map>
<key>package_description</key>
<map>
<key>description</key>
<string>Spell checking dictionaries</string>
<key>license</key>
<string>various open</string>
<key>name</key>
<string>viewer_development</string>
<string>dictionaries</string>
<key>platforms</key>
<map>
<key>common</key>
@ -3057,6 +3155,8 @@
<string>windows</string>
</map>
</map>
<key>version</key>
<string>1.0</string>
</map>
<key>type</key>
<string>autobuild</string>

View File

@ -15,6 +15,12 @@
# * The basic convention is that the build name can be mapped onto a mercurial URL,
# which is also used as the "branch" name.
check_for()
{
if [ -e "$2" ]; then found_dict='FOUND'; else found_dict='MISSING'; fi
echo "$1 ${found_dict} '$2' " 1>&2
}
build_dir_Darwin()
{
echo build-darwin-i386
@ -59,6 +65,8 @@ pre_build()
&& [ -r "$master_message_template_checkout/message_template.msg" ] \
&& template_verifier_master_url="-DTEMPLATE_VERIFIER_MASTER_URL=file://$master_message_template_checkout/message_template.msg"
check_for "Before 'autobuild configure'" ${build_dir}/packages/dictionaries
"$AUTOBUILD" configure -c $variant -- \
-DPACKAGE:BOOL=ON \
-DRELEASE_CRASH_REPORTING:BOOL=ON \
@ -67,7 +75,10 @@ pre_build()
-DGRID:STRING="\"$viewer_grid\"" \
-DLL_TESTS:BOOL="$run_tests" \
-DTEMPLATE_VERIFIER_OPTIONS:STRING="$template_verifier_options" $template_verifier_master_url
end_section "Pre$variant"
check_for "After 'autobuild configure'" ${build_dir}/packages/dictionaries
end_section "Pre$variant"
}
build()
@ -76,12 +87,17 @@ build()
if $build_viewer
then
begin_section "Viewer$variant"
check_for "Before 'autobuild build'" ${build_dir}/packages/dictionaries
if "$AUTOBUILD" build --no-configure -c $variant
then
echo true >"$build_dir"/build_ok
else
echo false >"$build_dir"/build_ok
fi
check_for "After 'autobuild configure'" ${build_dir}/packages/dictionaries
end_section "Viewer$variant"
fi
}
@ -172,7 +188,10 @@ eval "$("$AUTOBUILD" source_environment)"
# dump environment variables for debugging
env|sort
check_for "Before 'autobuild install'" ${build_dir}/packages/dictionaries
check_for "After 'autobuild install'" ${build_dir}/packages/dictionaries
# Now run the build
succeeded=true
build_processes=

View File

@ -297,6 +297,8 @@ Cherry Cheevers
ChickyBabes Zuzu
Christopher Organiser
Ciaran Laval
Cinder Roxley
STORM-1703
Clara Young
Coaldust Numbers
VWR-1095
@ -471,6 +473,7 @@ Hiro Sommambulist
VWR-143
Hitomi Tiponi
STORM-1741
STORM-1862
Holger Gilruth
Horatio Freund
Hoze Menges
@ -623,12 +626,22 @@ Jonathan Yap
STORM-1799
STORM-1796
STORM-1807
STORM-1812
STORM-1820
STORM-1839
STORM-1842
STORM-1808
STORM-637
STORM-1822
STORM-1809
STORM-1793
STORM-1810
STORM-1860
STORM-1852
STORM-1870
STORM-1872
STORM-1858
STORM-1862
Kadah Coba
STORM-1060
Jondan Lundquist
@ -725,6 +738,8 @@ Marc2 Sands
Marianne McCann
Marine Kelley
STORM-281
MartinRJ Fayray
STORM-1845
Matthew Anthony
Matthew Dowd
VWR-1344
@ -1052,6 +1067,8 @@ Simon Nolan
Sini Nubalo
Sitearm Madonna
SLB Wirefly
Slee Mayo
SEC-1075
snowy Sidran
SpacedOut Frye
VWR-34
@ -1287,6 +1304,7 @@ Zi Ree
VWR-24017
VWR-25588
STORM-1790
STORM-1842
Zipherius Turas
VWR-76
VWR-77

View File

@ -39,6 +39,7 @@ set(cmake_SOURCE_FILES
GooglePerfTools.cmake
Growl.cmake
HACD.cmake
Hunspell.cmake
JPEG.cmake
LLAddBuildTest.cmake
LLAudio.cmake

View File

@ -45,6 +45,7 @@ if(WINDOWS)
libeay32.dll
libcollada14dom22-d.dll
glod.dll
libhunspell.dll
)
set(release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")
@ -59,6 +60,7 @@ if(WINDOWS)
libeay32.dll
libcollada14dom22.dll
glod.dll
libhunspell.dll
)
if(USE_GOOGLE_PERFTOOLS)
@ -215,11 +217,12 @@ elseif(DARWIN)
libexpat.1.5.2.dylib
libexpat.dylib
libGLOD.dylib
libllqtwebkit.dylib
libminizip.a
libllqtwebkit.dylib
libminizip.a
libndofdev.dylib
libhunspell-1.3.0.dylib
libexception_handler.dylib
libcollada14dom.dylib
libcollada14dom.dylib
#libgrowl.dylib # *TODO - test/fix/get mac growl working
)
@ -261,14 +264,15 @@ elseif(LINUX)
libdb-5.1.so
libexpat.so
libexpat.so.1
libglod.so
libglod.so
libgmock_main.so
libgmock.so.0
libgmodule-2.0.so
libgobject-2.0.so
libgtest_main.so
libgtest.so.0
libminizip.so
libhunspell-1.3.so.0.0.0
libminizip.so
libopenal.so
libopenjpeg.so
libssl.so

View File

@ -10,7 +10,7 @@ find_path(HUNSPELL_INCLUDE_DIR hunspell.h
PATH_SUFFIXES hunspell
)
set(HUNSPELL_NAMES ${HUNSPELL_NAMES} libhunspell-1.3 libhunspell)
set(HUNSPELL_NAMES ${HUNSPELL_NAMES} libhunspell-1.3.0 libhunspell)
find_library(HUNSPELL_LIBRARY
NAMES ${HUNSPELL_NAMES}
)

View File

@ -0,0 +1,22 @@
# -*- cmake -*-
include(Prebuilt)
set(HUNSPELL_FIND_QUIETLY ON)
set(HUNSPELL_FIND_REQUIRED ON)
if (STANDALONE)
include(FindHUNSPELL)
else (STANDALONE)
use_prebuilt_binary(libhunspell)
if (WINDOWS)
set(HUNSPELL_LIBRARY libhunspell)
elseif(DARWIN)
set(HUNSPELL_LIBRARY hunspell-1.3.0)
elseif(LINUX)
set(HUNSPELL_LIBRARY hunspell-1.3)
else()
message(FATAL_ERROR "Invalid platform")
endif()
set(HUNSPELL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/hunspell)
use_prebuilt_binary(dictionaries)
endif (STANDALONE)

View File

@ -3,6 +3,7 @@ include(Prebuilt)
if (NOT STANDALONE)
use_prebuilt_binary(nd_hacdConvexDecomposition)
use_prebuilt_binary(libhunspell)
use_prebuilt_binary(libuuid)
use_prebuilt_binary(slvoice)
use_prebuilt_binary(fontconfig)

View File

@ -18,6 +18,7 @@ include(LLWindow)
include(LLUI)
include(LLVFS) # ugh, needed for LLDir
include(LLXML)
include(Hunspell)
include(Linking)
# include(Tut)
@ -31,6 +32,7 @@ include_directories(
${LLVFS_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
${LIBS_PREBUILD_DIR}/include/hunspell
)
set(llui_libtest_SOURCE_FILES
@ -78,6 +80,7 @@ target_link_libraries(llui_libtest
${LLIMAGEJ2COJ_LIBRARIES}
${OS_LIBRARIES}
${GOOGLE_PERFTOOLS_LIBRARIES}
${HUNSPELL_LIBRARY}
)
if (WINDOWS)

View File

@ -269,6 +269,7 @@ namespace
virtual LLSD::UUID asUUID() const { return LLUUID(mValue); }
virtual LLSD::Date asDate() const { return LLDate(mValue); }
virtual LLSD::URI asURI() const { return LLURI(mValue); }
virtual int size() const { return mValue.size(); }
};
LLSD::Integer ImplString::asInteger() const

View File

@ -44,6 +44,15 @@ public:
const LLStrider<Object>& operator = (Object *first) { mObjectp = first; return *this;}
void setStride (S32 skipBytes) { mSkip = (skipBytes ? skipBytes : sizeof(Object));}
LLStrider<Object> operator+(const S32& index)
{
LLStrider<Object> ret;
ret.mBytep = mBytep + mSkip*index;
ret.mSkip = mSkip;
return ret;
}
void skip(const U32 index) { mBytep += mSkip*index;}
U32 getSkip() const { return mSkip; }
Object* get() { return mObjectp; }
@ -51,6 +60,7 @@ public:
Object& operator *() { return *mObjectp; }
Object* operator ++(int) { Object* old = mObjectp; mBytep += mSkip; return old; }
Object* operator +=(int i) { mBytep += mSkip*i; return mObjectp; }
Object& operator[](U32 index) { return *(Object*)(mBytep + (mSkip * index)); }
};

View File

@ -183,6 +183,9 @@ public:
static bool isPunct(char a) { return ispunct((unsigned char)a) != 0; }
static bool isPunct(llwchar a) { return iswpunct(a) != 0; }
static bool isAlpha(char a) { return isalpha((unsigned char)a) != 0; }
static bool isAlpha(llwchar a) { return iswalpha(a) != 0; }
static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; }
static bool isAlnum(llwchar a) { return iswalnum(a) != 0; }

View File

@ -29,7 +29,7 @@
const S32 LL_VERSION_MAJOR = 3;
const S32 LL_VERSION_MINOR = 3;
const S32 LL_VERSION_PATCH = 3;
const S32 LL_VERSION_PATCH = 4;
const S32 LL_VERSION_BUILD = 0;
const char * const LL_CHANNEL = "Firestorm-private";

View File

@ -45,7 +45,7 @@ const S32 PARCEL_UNIT_AREA = 16;
const F32 PARCEL_HEIGHT = 50.f;
//Height above ground which parcel boundries exist for explicitly banned avatars
const F32 BAN_HEIGHT = 768.f;
const F32 BAN_HEIGHT = 5000.f;
// Maximum number of entries in an access list
const S32 PARCEL_MAX_ACCESS_LIST = 300;

View File

@ -85,7 +85,7 @@ const F32 F_ALMOST_ONE = 1.0f - F_ALMOST_ZERO;
const F32 FP_MAG_THRESHOLD = 0.0000001f;
// TODO: Replace with logic like is_approx_equal
inline BOOL is_approx_zero( F32 f ) { return (-F_APPROXIMATELY_ZERO < f) && (f < F_APPROXIMATELY_ZERO); }
inline bool is_approx_zero( F32 f ) { return (-F_APPROXIMATELY_ZERO < f) && (f < F_APPROXIMATELY_ZERO); }
// These functions work by interpreting sign+exp+mantissa as an unsigned
// integer.
@ -111,13 +111,13 @@ inline BOOL is_approx_zero( F32 f ) { return (-F_APPROXIMATELY_ZERO < f) && (f <
// WARNING: Infinity is comparable with F32_MAX and negative
// infinity is comparable with F32_MIN
inline BOOL is_approx_equal(F32 x, F32 y)
inline bool is_approx_equal(F32 x, F32 y)
{
const S32 COMPARE_MANTISSA_UP_TO_BIT = 0x02;
return (std::abs((S32) ((U32&)x - (U32&)y) ) < COMPARE_MANTISSA_UP_TO_BIT);
}
inline BOOL is_approx_equal(F64 x, F64 y)
inline bool is_approx_equal(F64 x, F64 y)
{
const S64 COMPARE_MANTISSA_UP_TO_BIT = 0x02;
return (std::abs((S32) ((U64&)x - (U64&)y) ) < COMPARE_MANTISSA_UP_TO_BIT);

View File

@ -54,6 +54,7 @@ class LLVolumeTriangle;
#include "llstrider.h"
#include "v4coloru.h"
#include "llrefcount.h"
#include "llpointer.h"
#include "llfile.h"
//============================================================================
@ -922,6 +923,10 @@ public:
LLVector2* mTexCoords;
U16* mIndices;
//vertex buffer filled in by LLFace to cache this volume face geometry in vram
// (declared as a LLPointer to LLRefCount to avoid dependency on LLVertexBuffer)
mutable LLPointer<LLRefCount> mVertexBuffer;
std::vector<S32> mEdge;
//list of skin weights for rigged volumes

View File

@ -46,7 +46,7 @@ static const U32 LENGTHOFCOLOR4U = 4;
class LLColor4U
{
public:
//64bit compile fix - Do not put pointers into the LLColor4U union. FS:ND
// union
// {
U8 mV[LENGTHOFCOLOR4U];

View File

@ -276,7 +276,7 @@ S32 getElementSize(const LLSD& llsd)
case LLSD::TypeReal:
return sizeof(F64);
case LLSD::TypeString:
return llsd.asString().size();
return llsd.size();
case LLSD::TypeUUID:
return sizeof(LLUUID);
case LLSD::TypeDate:

View File

@ -81,7 +81,7 @@ void LLCubeMap::initGL()
{
U32 texname = 0;
LLImageGL::generateTextures(1, &texname);
LLImageGL::generateTextures(LLTexUnit::TT_CUBE_MAP, GL_RGB8, 1, &texname);
for (int i = 0; i < 6; i++)
{

View File

@ -422,6 +422,16 @@ S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y
}
// font metrics - override for LLFontFreetype that returns units of virtual pixels
F32 LLFontGL::getAscenderHeight() const
{
return mFontFreetype->getAscenderHeight() / sScaleY;
}
F32 LLFontGL::getDescenderHeight() const
{
return mFontFreetype->getDescenderHeight() / sScaleY;
}
S32 LLFontGL::getLineHeight() const
{
return llceil(mFontFreetype->getAscenderHeight() / sScaleY) + llceil(mFontFreetype->getDescenderHeight() / sScaleY);

View File

@ -115,6 +115,8 @@ public:
S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style = NORMAL, ShadowType shadow = NO_SHADOW) const;
// font metrics - override for LLFontFreetype that returns units of virtual pixels
F32 getAscenderHeight() const;
F32 getDescenderHeight() const;
S32 getLineHeight() const;
S32 getWidth(const std::string& utf8text) const;

View File

@ -249,6 +249,12 @@ PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample = NULL;
PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv = NULL;
PFNGLSAMPLEMASKIPROC glSampleMaski = NULL;
//transform feedback (4.0 core)
PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback = NULL;
PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback = NULL;
PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings = NULL;
PFNGLBINDBUFFERRANGEPROC glBindBufferRange = NULL;
//GL_ARB_debug_output
PFNGLDEBUGMESSAGECONTROLARBPROC glDebugMessageControlARB = NULL;
PFNGLDEBUGMESSAGEINSERTARBPROC glDebugMessageInsertARB = NULL;
@ -421,6 +427,7 @@ LLGLManager::LLGLManager() :
mHasDrawBuffers(FALSE),
mHasTextureRectangle(FALSE),
mHasTextureMultisample(FALSE),
mHasTransformFeedback(FALSE),
mMaxSampleMaskWords(0),
mMaxColorTextureSamples(0),
mMaxDepthTextureSamples(0),
@ -558,7 +565,8 @@ bool LLGLManager::initGL()
parse_gl_version( &mDriverVersionMajor,
&mDriverVersionMinor,
&mDriverVersionRelease,
&mDriverVersionVendorString );
&mDriverVersionVendorString,
&mGLVersionString);
mGLVersion = mDriverVersionMajor + mDriverVersionMinor * .1f;
@ -938,7 +946,6 @@ void LLGLManager::initExtensions()
mHasMultitexture = glh_init_extensions("GL_ARB_multitexture");
mHasATIMemInfo = ExtensionExists("GL_ATI_meminfo", gGLHExts.mSysExts);
mHasNVXMemInfo = ExtensionExists("GL_NVX_gpu_memory_info", gGLHExts.mSysExts);
mHasMipMapGeneration = glh_init_extensions("GL_SGIS_generate_mipmap");
mHasSeparateSpecularColor = glh_init_extensions("GL_EXT_separate_specular_color");
mHasAnisotropic = glh_init_extensions("GL_EXT_texture_filter_anisotropic");
glh_init_extensions("GL_ARB_texture_cube_map");
@ -963,11 +970,14 @@ void LLGLManager::initExtensions()
ExtensionExists("GL_EXT_packed_depth_stencil", gGLHExts.mSysExts);
#endif
mHasMipMapGeneration = mHasFramebufferObject || mGLVersion >= 1.4f;
mHasDrawBuffers = ExtensionExists("GL_ARB_draw_buffers", gGLHExts.mSysExts);
mHasBlendFuncSeparate = ExtensionExists("GL_EXT_blend_func_separate", gGLHExts.mSysExts);
mHasTextureRectangle = ExtensionExists("GL_ARB_texture_rectangle", gGLHExts.mSysExts);
mHasTextureMultisample = ExtensionExists("GL_ARB_texture_multisample", gGLHExts.mSysExts);
mHasDebugOutput = ExtensionExists("GL_ARB_debug_output", gGLHExts.mSysExts);
mHasTransformFeedback = mGLVersion >= 4.f ? TRUE : FALSE;
#if !LL_DARWIN
mHasPointParameters = !mIsATI && ExtensionExists("GL_ARB_point_parameters", gGLHExts.mSysExts);
#endif
@ -1207,7 +1217,14 @@ void LLGLManager::initExtensions()
glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) GLH_EXT_GET_PROC_ADDRESS("glTexImage3DMultisample");
glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) GLH_EXT_GET_PROC_ADDRESS("glGetMultisamplefv");
glSampleMaski = (PFNGLSAMPLEMASKIPROC) GLH_EXT_GET_PROC_ADDRESS("glSampleMaski");
}
}
if (mHasTransformFeedback)
{
glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) GLH_EXT_GET_PROC_ADDRESS("glBeginTransformFeedback");
glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) GLH_EXT_GET_PROC_ADDRESS("glEndTransformFeedback");
glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) GLH_EXT_GET_PROC_ADDRESS("glTransformFeedbackVaryings");
glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) GLH_EXT_GET_PROC_ADDRESS("glBindBufferRange");
}
if (mHasDebugOutput)
{
glDebugMessageControlARB = (PFNGLDEBUGMESSAGECONTROLARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDebugMessageControlARB");
@ -1964,6 +1981,7 @@ LLGLState::LLGLState(LLGLenum state, S32 enabled) :
case GL_COLOR_MATERIAL:
case GL_FOG:
case GL_LINE_STIPPLE:
case GL_POLYGON_STIPPLE:
mState = 0;
break;
}
@ -2052,7 +2070,7 @@ void LLGLManager::initGLStates()
////////////////////////////////////////////////////////////////////////////////
void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific )
void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific, std::string* version_string )
{
// GL_VERSION returns a null-terminated string with the format:
// <major>.<minor>[.<release>] [<vendor specific>]
@ -2068,6 +2086,8 @@ void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor
return;
}
version_string->assign(version);
std::string ver_copy( version );
S32 len = (S32)strlen( version ); /* Flawfinder: ignore */
S32 i = 0;
@ -2429,3 +2449,65 @@ LLGLSquashToFarClip::~LLGLSquashToFarClip()
gGL.matrixMode(LLRender::MM_MODELVIEW);
}
LLGLSyncFence::LLGLSyncFence()
{
#ifdef GL_ARB_sync
mSync = 0;
#endif
}
LLGLSyncFence::~LLGLSyncFence()
{
#ifdef GL_ARB_sync
if (mSync)
{
glDeleteSync(mSync);
}
#endif
}
void LLGLSyncFence::placeFence()
{
#ifdef GL_ARB_sync
if (mSync)
{
glDeleteSync(mSync);
}
mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
#endif
}
bool LLGLSyncFence::isCompleted()
{
bool ret = true;
#ifdef GL_ARB_sync
if (mSync)
{
GLenum status = glClientWaitSync(mSync, 0, 1);
if (status == GL_TIMEOUT_EXPIRED)
{
ret = false;
}
}
#endif
return ret;
}
void LLGLSyncFence::wait()
{
#ifdef GL_ARB_sync
if (mSync)
{
while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
{ //track the number of times we've waited here
static S32 waits = 0;
waits++;
}
}
#endif
}

View File

@ -104,6 +104,7 @@ public:
BOOL mHasDepthClamp;
BOOL mHasTextureRectangle;
BOOL mHasTextureMultisample;
BOOL mHasTransformFeedback;
S32 mMaxSampleMaskWords;
S32 mMaxColorTextureSamples;
S32 mMaxDepthTextureSamples;
@ -141,6 +142,7 @@ public:
S32 mGLSLVersionMajor;
S32 mGLSLVersionMinor;
std::string mDriverVersionVendorString;
std::string mGLVersionString;
S32 mVRAM; // VRAM in MB
S32 mGLMaxVertexRange;
@ -417,13 +419,38 @@ public:
virtual void updateGL() = 0;
};
const U32 FENCE_WAIT_TIME_NANOSECONDS = 1000; //1 ms
class LLGLFence
{
public:
virtual void placeFence() = 0;
virtual bool isCompleted() = 0;
virtual void wait() = 0;
};
class LLGLSyncFence : public LLGLFence
{
public:
#ifdef GL_ARB_sync
GLsync mSync;
#endif
LLGLSyncFence();
virtual ~LLGLSyncFence();
void placeFence();
bool isCompleted();
void wait();
};
extern LLMatrix4 gGLObliqueProjectionInverse;
#include "llglstates.h"
void init_glstates();
void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific );
void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific, std::string* version_string );
extern BOOL gClothRipple;
extern BOOL gHeadlessClient;

View File

@ -528,6 +528,13 @@ extern PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample;
extern PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv;
extern PFNGLSAMPLEMASKIPROC glSampleMaski;
//transform feedback (4.0 core)
extern PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback;
extern PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback;
extern PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings;
extern PFNGLBINDBUFFERRANGEPROC glBindBufferRange;
#elif LL_WINDOWS
//----------------------------------------------------------------------------
// LL_WINDOWS
@ -759,6 +766,12 @@ extern PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample;
extern PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv;
extern PFNGLSAMPLEMASKIPROC glSampleMaski;
//transform feedback (4.0 core)
extern PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback;
extern PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback;
extern PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings;
extern PFNGLBINDBUFFERRANGEPROC glBindBufferRange;
//GL_ARB_debug_output
extern PFNGLDEBUGMESSAGECONTROLARBPROC glDebugMessageControlARB;
extern PFNGLDEBUGMESSAGEINSERTARBPROC glDebugMessageInsertARB;

View File

@ -129,7 +129,9 @@ void LLGLSLShader::unload()
}
BOOL LLGLSLShader::createShader(vector<string> * attributes,
vector<string> * uniforms)
vector<string> * uniforms,
U32 varying_count,
const char** varyings)
{
//reloading, reset matrix hash values
for (U32 i = 0; i < LLRender::NUM_MATRIX_MODES; ++i)
@ -172,6 +174,13 @@ BOOL LLGLSLShader::createShader(vector<string> * attributes,
mFeatures.mIndexedTextureChannels = llmin(mFeatures.mIndexedTextureChannels, 1);
}
#ifdef GL_INTERLEAVED_ATTRIBS
if (varying_count > 0 && varyings)
{
glTransformFeedbackVaryings(mProgramObject, varying_count, varyings, GL_INTERLEAVED_ATTRIBS);
}
#endif
// Map attributes and uniforms
if (success)
{

View File

@ -76,7 +76,9 @@ public:
void unload();
BOOL createShader(std::vector<std::string> * attributes,
std::vector<std::string> * uniforms);
std::vector<std::string> * uniforms,
U32 varying_count = 0,
const char** varyings = NULL);
BOOL attachObject(std::string object);
void attachObject(GLhandleARB object);
void attachObjects(GLhandleARB* objects = NULL, S32 count = 0);

View File

@ -42,6 +42,10 @@
//----------------------------------------------------------------------------
const F32 MIN_TEXTURE_LIFETIME = 10.f;
//which power of 2 is i?
//assumes i is a power of 2 > 0
U32 wpo2(U32 i);
//statics
U32 LLImageGL::sUniqueCount = 0;
@ -50,7 +54,8 @@ S32 LLImageGL::sGlobalTextureMemoryInBytes = 0;
S32 LLImageGL::sBoundTextureMemoryInBytes = 0;
S32 LLImageGL::sCurBoundTextureMemory = 0;
S32 LLImageGL::sCount = 0;
std::list<U32> LLImageGL::sDeadTextureList;
LLImageGL::dead_texturelist_t LLImageGL::sDeadTextureList[LLTexUnit::TT_NONE];
U32 LLImageGL::sCurTexName = 1;
BOOL LLImageGL::sGlobalUseAnisotropic = FALSE;
F32 LLImageGL::sLastFrameTime = 0.f;
@ -416,6 +421,7 @@ void LLImageGL::init(BOOL usemipmaps)
mTarget = GL_TEXTURE_2D;
mBindTarget = LLTexUnit::TT_TEXTURE;
mHasMipMaps = false;
mMipLevels = -1;
mIsResident = 0;
@ -606,8 +612,24 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
is_compressed = true;
}
if (mUseMipMaps)
{
//set has mip maps to true before binding image so tex parameters get set properly
gGL.getTexUnit(0)->unbind(mBindTarget);
mHasMipMaps = true;
mTexOptionsDirty = true;
setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
}
else
{
mHasMipMaps = false;
}
llverify(gGL.getTexUnit(0)->bind(this));
if (mUseMipMaps)
{
if (data_hasmips)
@ -620,6 +642,9 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
S32 w = getWidth(d);
S32 h = getHeight(d);
S32 gl_level = d-mCurrentDiscardLevel;
mMipLevels = llmax(mMipLevels, gl_level);
if (d > mCurrentDiscardLevel)
{
data_in -= dataFormatBytes(mFormatPrimary, w, h); // see above comment
@ -662,10 +687,6 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
{
if (mAutoGenMips)
{
if (!gGLManager.mHasFramebufferObject)
{
glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_GENERATE_MIPMAP_SGIS, TRUE);
}
stop_glerror();
{
// LLFastTimer t2(FTM_TEMP4);
@ -679,6 +700,11 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
S32 w = getWidth(mCurrentDiscardLevel);
S32 h = getHeight(mCurrentDiscardLevel);
mMipLevels = wpo2(llmax(w, h));
//use legacy mipmap generation mode
glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE);
LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
w, h,
mFormatPrimary, mFormatType,
@ -694,16 +720,10 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
stop_glerror();
}
}
if (gGLManager.mHasFramebufferObject)
{
glGenerateMipmap(LLTexUnit::getInternalType(mBindTarget));
}
}
else
{
// Create mips by hand
// about 30% faster than autogen on ATI 9800, 50% slower on nVidia 4800
// ~4x faster than gluBuild2DMipmaps
S32 width = getWidth(mCurrentDiscardLevel);
S32 height = getHeight(mCurrentDiscardLevel);
@ -713,6 +733,9 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
const U8* cur_mip_data = 0;
S32 prev_mip_size = 0;
S32 cur_mip_size = 0;
mMipLevels = nummips;
for (int m=0; m<nummips; m++)
{
if (m==0)
@ -777,10 +800,10 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
{
llerrs << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)" << llendl;
}
mHasMipMaps = true;
}
else
{
mMipLevels = 0;
S32 w = getWidth();
S32 h = getHeight();
if (is_compressed)
@ -812,7 +835,6 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
}
}
mHasMipMaps = false;
}
stop_glerror();
mGLTextureCreated = true;
@ -1025,23 +1047,65 @@ BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_
}
// static
void LLImageGL::generateTextures(S32 numTextures, U32 *textures)
void LLImageGL::generateTextures(LLTexUnit::eTextureType type, U32 format, S32 numTextures, U32 *textures)
{
glGenTextures(numTextures, (GLuint*)textures);
bool empty = true;
dead_texturelist_t::iterator iter = sDeadTextureList[type].find(format);
if (iter != sDeadTextureList[type].end())
{
empty = iter->second.empty();
}
for (S32 i = 0; i < numTextures; ++i)
{
if (!empty)
{
textures[i] = iter->second.front();
iter->second.pop_front();
empty = iter->second.empty();
}
else
{
textures[i] = sCurTexName++;
}
}
}
// static
void LLImageGL::deleteTextures(S32 numTextures, U32 *textures, bool immediate)
void LLImageGL::deleteTextures(LLTexUnit::eTextureType type, U32 format, S32 mip_levels, S32 numTextures, U32 *textures, bool immediate)
{
for (S32 i = 0; i < numTextures; i++)
if (gGLManager.mInited)
{
sDeadTextureList.push_back(textures[i]);
}
if (format == 0 || type == LLTexUnit::TT_CUBE_MAP || mip_levels == -1)
{ //unknown internal format or unknown number of mip levels, not safe to reuse
glDeleteTextures(numTextures, textures);
}
else
{
for (S32 i = 0; i < numTextures; ++i)
{ //remove texture from VRAM by setting its size to zero
for (S32 j = 0; j <= mip_levels; j++)
{
gGL.getTexUnit(0)->bindManual(type, textures[i]);
if (immediate)
glTexImage2D(LLTexUnit::getInternalType(type), j, format, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
llassert(std::find(sDeadTextureList[type][format].begin(),
sDeadTextureList[type][format].end(), textures[i]) ==
sDeadTextureList[type][format].end());
sDeadTextureList[type][format].push_back(textures[i]);
}
}
}
/*if (immediate)
{
LLImageGL::deleteDeadTextures();
}
}*/
}
// static
@ -1166,10 +1230,11 @@ BOOL LLImageGL::createGLTexture()
if(mTexName)
{
glDeleteTextures(1, (reinterpret_cast<GLuint*>(&mTexName))) ;
LLImageGL::deleteTextures(mBindTarget, mFormatInternal, mMipLevels, 1, (reinterpret_cast<GLuint*>(&mTexName))) ;
}
glGenTextures(1, (GLuint*)&mTexName);
LLImageGL::generateTextures(mBindTarget, mFormatInternal, 1, &mTexName);
stop_glerror();
if (!mTexName)
{
@ -1282,7 +1347,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
}
else
{
LLImageGL::generateTextures(1, &mTexName);
LLImageGL::generateTextures(mBindTarget, mFormatInternal, 1, &mTexName);
stop_glerror();
{
llverify(gGL.getTexUnit(0)->bind(this));
@ -1327,7 +1392,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
{
sGlobalTextureMemoryInBytes -= mTextureMemory;
LLImageGL::deleteTextures(1, &old_name);
LLImageGL::deleteTextures(mBindTarget, mFormatInternal, mMipLevels, 1, &old_name);
stop_glerror();
}
@ -1456,7 +1521,7 @@ void LLImageGL::deleteDeadTextures()
{
bool reset = false;
while (!sDeadTextureList.empty())
/*while (!sDeadTextureList.empty())
{
GLuint tex = sDeadTextureList.front();
sDeadTextureList.pop_front();
@ -1478,7 +1543,7 @@ void LLImageGL::deleteDeadTextures()
glDeleteTextures(1, &tex);
stop_glerror();
}
}*/
if (reset)
{
@ -1496,7 +1561,7 @@ void LLImageGL::destroyGLTexture()
mTextureMemory = 0;
}
LLImageGL::deleteTextures(1, &mTexName);
LLImageGL::deleteTextures(mBindTarget, mFormatInternal, mMipLevels, 1, &mTexName);
mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
mTexName = 0;
mGLTextureCreated = FALSE ;

View File

@ -45,8 +45,16 @@ class LLImageGL : public LLRefCount
{
friend class LLTexUnit;
public:
static std::list<U32> sDeadTextureList;
static U32 sCurTexName;
//previously used but now available texture names
// sDeadTextureList[<usage>][<internal format>]
typedef std::map<U32, std::list<U32> > dead_texturelist_t;
static dead_texturelist_t sDeadTextureList[LLTexUnit::TT_NONE];
// These 2 functions replace glGenTextures() and glDeleteTextures()
static void generateTextures(LLTexUnit::eTextureType type, U32 format, S32 numTextures, U32 *textures);
static void deleteTextures(LLTexUnit::eTextureType type, U32 format, S32 mip_levels, S32 numTextures, U32 *textures, bool immediate = false);
static void deleteDeadTextures();
// Size calculation
@ -96,10 +104,6 @@ public:
void setComponents(S32 ncomponents) { mComponents = (S8)ncomponents ;}
void setAllowCompression(bool allow) { mAllowCompression = allow; }
// These 3 functions currently wrap glGenTextures(), glDeleteTextures(), and glTexImage2D()
// for tracking purposes and will be deprecated in the future
static void generateTextures(S32 numTextures, U32 *textures);
static void deleteTextures(S32 numTextures, U32 *textures, bool immediate = false);
static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression = true);
BOOL createGLTexture() ;
@ -217,7 +221,8 @@ protected:
LLGLenum mTarget; // Normally GL_TEXTURE2D, sometimes something else (ex. cube maps)
LLTexUnit::eTextureType mBindTarget; // Normally TT_TEXTURE, sometimes something else (ex. cube maps)
bool mHasMipMaps;
S32 mMipLevels;
LLGLboolean mIsResident;
S8 mComponents;

View File

@ -408,12 +408,14 @@ void LLTexUnit::unbind(eTextureType type)
if (mIndex < 0) return;
//always flush and activate for consistency
// some code paths assume unbind always flushes and sets the active texture
gGL.flush();
activate();
// Disabled caching of binding state.
if (mCurrTexType == type)
{
gGL.flush();
activate();
mCurrTexture = 0;
if (LLGLSLShader::sNoFixedFunction && type == LLTexUnit::TT_TEXTURE)
{
@ -464,11 +466,25 @@ void LLTexUnit::setTextureFilteringOption(LLTexUnit::eTextureFilterOptions optio
}
else if (option >= TFO_BILINEAR)
{
glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (mHasMipMaps)
{
glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
}
else
{
glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
}
else
{
glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
if (mHasMipMaps)
{
glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
}
else
{
glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
}
if (gGLManager.mHasAnisotropic)

View File

@ -55,7 +55,6 @@ bool LLRenderTarget::sUseFBO = false;
LLRenderTarget::LLRenderTarget() :
mResX(0),
mResY(0),
mTex(0),
mFBO(0),
mDepth(0),
mStencil(0),
@ -135,7 +134,7 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt)
}
U32 tex;
LLImageGL::generateTextures(1, &tex);
LLImageGL::generateTextures(mUsage, color_fmt, 1, &tex);
gGL.getTexUnit(0)->bindManual(mUsage, tex);
stop_glerror();
@ -193,6 +192,7 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt)
}
mTex.push_back(tex);
mInternalFormat.push_back(color_fmt);
if (gDebugGL)
{ //bind and unbind to validate target
@ -217,7 +217,7 @@ bool LLRenderTarget::allocateDepth()
}
else
{
LLImageGL::generateTextures(1, &mDepth);
LLImageGL::generateTextures(mUsage, GL_DEPTH_COMPONENT24, 1, &mDepth);
gGL.getTexUnit(0)->bindManual(mUsage, mDepth);
U32 internal_type = LLTexUnit::getInternalType(mUsage);
@ -294,7 +294,7 @@ void LLRenderTarget::release()
}
else
{
LLImageGL::deleteTextures(1, &mDepth, true);
LLImageGL::deleteTextures(mUsage, 0, 0, 1, &mDepth, true);
stop_glerror();
}
mDepth = 0;
@ -326,8 +326,9 @@ void LLRenderTarget::release()
if (mTex.size() > 0)
{
sBytesAllocated -= mResX*mResY*4*mTex.size();
LLImageGL::deleteTextures(mTex.size(), &mTex[0], true);
LLImageGL::deleteTextures(mUsage, mInternalFormat[0], 0, mTex.size(), &mTex[0], true);
mTex.clear();
mInternalFormat.clear();
}
mResX = mResY = 0;

View File

@ -142,6 +142,7 @@ protected:
U32 mResX;
U32 mResY;
std::vector<U32> mTex;
std::vector<U32> mInternalFormat;
U32 mFBO;
U32 mDepth;
bool mStencil;

View File

@ -709,7 +709,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
if (texture_index_channels > 1)
{
text[count++] = strdup("VARYING_FLAT ivec4 vary_texture_index;\n");
text[count++] = strdup("VARYING_FLAT int vary_texture_index;\n");
}
text[count++] = strdup("vec4 diffuseLookup(vec2 texcoord)\n");
@ -723,38 +723,33 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
}
else if (major_version > 1 || minor_version >= 30)
{ //switches are supported in GLSL 1.30 and later
// <FS:ND> FIRE-6845 / VWR-29228 Yay for pink textures again :(
// replacing LL's version once again with the FS one that does not use a switch statement
// text[count++] = strdup("\tvec4 ret = vec4(1,0,1,1);\n");
// text[count++] = strdup("\tswitch (vary_texture_index.r)\n");
// text[count++] = strdup("\t{\n");
//
// //switch body
// for (S32 i = 0; i < texture_index_channels; ++i)
// {
// std::string case_str = llformat("\t\tcase %d: ret = texture2D(tex%d, texcoord); break;\n", i, i);
// text[count++] = strdup(case_str.c_str());
// }
//
// text[count++] = strdup("\t}\n");
// text[count++] = strdup("\treturn ret;\n");
// text[count++] = strdup("}\n");
text[count++] = strdup("float frVal = float( vary_texture_index.r );\n");
text[count++] = strdup("if ( frVal < 0 ) { return vec4(0,1,1,1); }\n");
for (S32 i = 0; i < texture_index_channels; ++i)
{
std::stringstream str;
str << "if ( frVal <= " << i << ".25 ) { return texture2D(tex" << i << ", texcoord); }\n";
text[count++] = strdup(str.str().c_str());
if (gGLManager.mIsNVIDIA)
{ //switches are unreliable on some NVIDIA drivers
for (U32 i = 0; i < texture_index_channels; ++i)
{
std::string if_string = llformat("\t%sif (vary_texture_index == %d) { return texture2D(tex%d, texcoord); }\n", i > 0 ? "else " : "", i, i);
text[count++] = strdup(if_string.c_str());
}
text[count++] = strdup("\treturn vec4(1,0,1,1);\n");
text[count++] = strdup("}\n");
}
text[count++] = strdup("return vec4(1,0,1,1);\n");
text[count++] = strdup("}\n");
else
{
text[count++] = strdup("\tvec4 ret = vec4(1,0,1,1);\n");
text[count++] = strdup("\tswitch (vary_texture_index)\n");
text[count++] = strdup("\t{\n");
//switch body
for (S32 i = 0; i < texture_index_channels; ++i)
{
std::string case_str = llformat("\t\tcase %d: return texture2D(tex%d, texcoord);\n", i, i);
text[count++] = strdup(case_str.c_str());
}
// </FS:ND>
text[count++] = strdup("\t}\n");
text[count++] = strdup("\treturn ret;\n");
text[count++] = strdup("}\n");
}
}
else
{ //should never get here. Indexed texture rendering requires GLSL 1.30 or later
@ -1051,6 +1046,9 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("size");
mReservedUniforms.push_back("falloff");
mReservedUniforms.push_back("box_center");
mReservedUniforms.push_back("box_size");
mReservedUniforms.push_back("minLuminance");
mReservedUniforms.push_back("maxExtractAlpha");

View File

@ -97,6 +97,8 @@ public:
LIGHT_CENTER,
LIGHT_SIZE,
LIGHT_FALLOFF,
BOX_CENTER,
BOX_SIZE,
GLOW_MIN_LUMINANCE,
GLOW_MAX_EXTRACT_ALPHA,

View File

@ -41,9 +41,7 @@
#if LL_DARWIN
#define LL_VBO_POOLING 1
#else
#define LL_VBO_POOLING 0
#endif
//Next Highest Power Of Two
//helper function, returns first number > v that is a power of 2, or v if v is already a power of 2
U32 nhpo2(U32 v)
@ -71,6 +69,7 @@ U32 wpo2(U32 i)
const U32 LL_VBO_BLOCK_SIZE = 2048;
const U32 LL_VBO_POOL_MAX_SEED_SIZE = 256*1024;
U32 vbo_block_size(U32 size)
{ //what block size will fit size?
@ -83,6 +82,7 @@ U32 vbo_block_index(U32 size)
return vbo_block_size(size)/LL_VBO_BLOCK_SIZE;
}
const U32 LL_VBO_POOL_SEED_COUNT = vbo_block_index(LL_VBO_POOL_MAX_SEED_SIZE);
//============================================================================
@ -95,6 +95,11 @@ LLVBOPool LLVertexBuffer::sDynamicIBOPool(GL_DYNAMIC_DRAW_ARB, GL_ELEMENT_ARRAY_
U32 LLVBOPool::sBytesPooled = 0;
U32 LLVBOPool::sIndexBytesPooled = 0;
U32 LLVBOPool::sCurGLName = 1;
std::list<U32> LLVertexBuffer::sAvailableVAOName;
U32 LLVertexBuffer::sCurVAOName = 1;
U32 LLVertexBuffer::sAllocatedIndexBytes = 0;
U32 LLVertexBuffer::sIndexCount = 0;
@ -119,69 +124,55 @@ bool LLVertexBuffer::sUseStreamDraw = true;
bool LLVertexBuffer::sUseVAO = false;
bool LLVertexBuffer::sPreferStreamDraw = false;
const U32 FENCE_WAIT_TIME_NANOSECONDS = 10000; //1 ms
class LLGLSyncFence : public LLGLFence
U32 LLVBOPool::genBuffer()
{
public:
#ifdef GL_ARB_sync
GLsync mSync;
#endif
LLGLSyncFence()
U32 ret = 0;
if (mGLNamePool.empty())
{
#ifdef GL_ARB_sync
mSync = 0;
#endif
ret = sCurGLName++;
}
else
{
ret = mGLNamePool.front();
mGLNamePool.pop_front();
}
virtual ~LLGLSyncFence()
return ret;
}
void LLVBOPool::deleteBuffer(U32 name)
{
if (gGLManager.mInited)
{
#ifdef GL_ARB_sync
if (mSync)
{
glDeleteSync(mSync);
}
#endif
}
void placeFence()
{
#ifdef GL_ARB_sync
if (mSync)
{
glDeleteSync(mSync);
}
mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
#endif
}
void wait()
{
#ifdef GL_ARB_sync
if (mSync)
{
while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
{ //track the number of times we've waited here
static S32 waits = 0;
waits++;
}
}
#endif
LLVertexBuffer::unbind();
glBindBufferARB(mType, name);
glBufferDataARB(mType, 0, NULL, mUsage);
llassert(std::find(mGLNamePool.begin(), mGLNamePool.end(), name) == mGLNamePool.end());
mGLNamePool.push_back(name);
glBindBufferARB(mType, 0);
}
}
};
LLVBOPool::LLVBOPool(U32 vboUsage, U32 vboType)
: mUsage(vboUsage), mType(vboType)
{
mMissCount.resize(LL_VBO_POOL_SEED_COUNT);
std::fill(mMissCount.begin(), mMissCount.end(), 0);
}
volatile U8* LLVBOPool::allocate(U32& name, U32 size)
volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed)
{
llassert(vbo_block_size(size) == size);
volatile U8* ret = NULL;
#if LL_VBO_POOLING
U32 i = vbo_block_index(size);
if (mFreeList.size() <= i)
@ -189,12 +180,18 @@ volatile U8* LLVBOPool::allocate(U32& name, U32 size)
mFreeList.resize(i+1);
}
if (mFreeList[i].empty())
if (mFreeList[i].empty() || for_seed)
{
//make a new buffer
glGenBuffersARB(1, &name);
name = genBuffer();
glBindBufferARB(mType, name);
if (!for_seed && i < LL_VBO_POOL_SEED_COUNT)
{ //record this miss
mMissCount[i]++;
}
if (mType == GL_ARRAY_BUFFER_ARB)
{
LLVertexBuffer::sAllocatedBytes += size;
@ -215,6 +212,25 @@ volatile U8* LLVBOPool::allocate(U32& name, U32 size)
}
glBindBufferARB(mType, 0);
if (for_seed)
{ //put into pool for future use
llassert(mFreeList.size() > i);
Record rec;
rec.mGLName = name;
rec.mClientData = ret;
if (mType == GL_ARRAY_BUFFER_ARB)
{
sBytesPooled += size;
}
else
{
sIndexBytesPooled += size;
}
mFreeList[i].push_back(rec);
}
}
else
{
@ -232,33 +248,6 @@ volatile U8* LLVBOPool::allocate(U32& name, U32 size)
mFreeList[i].pop_front();
}
#else //no pooling
glGenBuffersARB(1, &name);
glBindBufferARB(mType, name);
if (mType == GL_ARRAY_BUFFER_ARB)
{
LLVertexBuffer::sAllocatedBytes += size;
}
else
{
LLVertexBuffer::sAllocatedIndexBytes += size;
}
if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB)
{
glBufferDataARB(mType, size, 0, mUsage);
ret = (U8*) ll_aligned_malloc_16(size);
}
else
{ //always use a true hint of static draw when allocating non-client-backed buffers
glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB);
}
glBindBufferARB(mType, 0);
#endif
return ret;
}
@ -267,34 +256,7 @@ void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
{
llassert(vbo_block_size(size) == size);
#if LL_VBO_POOLING
U32 i = vbo_block_index(size);
llassert(mFreeList.size() > i);
Record rec;
rec.mGLName = name;
rec.mClientData = buffer;
if (buffer == NULL)
{
glDeleteBuffersARB(1, &rec.mGLName);
}
else
{
if (mType == GL_ARRAY_BUFFER_ARB)
{
sBytesPooled += size;
}
else
{
sIndexBytesPooled += size;
}
mFreeList[i].push_back(rec);
}
#else //no pooling
glDeleteBuffersARB(1, &name);
deleteBuffer(name);
ll_aligned_free_16((U8*) buffer);
if (mType == GL_ARRAY_BUFFER_ARB)
@ -305,12 +267,36 @@ void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
{
LLVertexBuffer::sAllocatedIndexBytes -= size;
}
#endif
}
void LLVBOPool::seedPool()
{
U32 dummy_name = 0;
if (mFreeList.size() < LL_VBO_POOL_SEED_COUNT)
{
mFreeList.resize(LL_VBO_POOL_SEED_COUNT);
}
for (U32 i = 0; i < LL_VBO_POOL_SEED_COUNT; i++)
{
if (mMissCount[i] > mFreeList[i].size())
{
U32 size = i*LL_VBO_BLOCK_SIZE;
S32 count = mMissCount[i] - mFreeList[i].size();
for (U32 j = 0; j < count; ++j)
{
allocate(dummy_name, size, true);
}
}
}
}
void LLVBOPool::cleanup()
{
U32 size = 1;
U32 size = LL_VBO_BLOCK_SIZE;
for (U32 i = 0; i < mFreeList.size(); ++i)
{
@ -320,8 +306,8 @@ void LLVBOPool::cleanup()
{
Record& r = l.front();
glDeleteBuffersARB(1, &r.mGLName);
deleteBuffer(r.mGLName);
if (r.mClientData)
{
ll_aligned_free_16((void*) r.mClientData);
@ -341,8 +327,11 @@ void LLVBOPool::cleanup()
}
}
size *= 2;
size += LL_VBO_BLOCK_SIZE;
}
//reset miss counts
std::fill(mMissCount.begin(), mMissCount.end(), 0);
}
@ -376,6 +365,41 @@ U32 LLVertexBuffer::sGLMode[LLRender::NUM_MODES] =
GL_LINE_LOOP,
};
//static
U32 LLVertexBuffer::getVAOName()
{
U32 ret = 0;
if (!sAvailableVAOName.empty())
{
ret = sAvailableVAOName.front();
sAvailableVAOName.pop_front();
}
else
{
#ifdef GL_ARB_vertex_array_object
glGenVertexArrays(1, &ret);
#endif
}
return ret;
}
//static
void LLVertexBuffer::releaseVAOName(U32 name)
{
sAvailableVAOName.push_back(name);
}
//static
void LLVertexBuffer::seedPools()
{
sStreamVBOPool.seedPool();
sDynamicVBOPool.seedPool();
sStreamIBOPool.seedPool();
sDynamicIBOPool.seedPool();
}
//static
void LLVertexBuffer::setupClientArrays(U32 data_mask)
@ -985,7 +1009,7 @@ LLVertexBuffer::~LLVertexBuffer()
if (mGLArray)
{
#if GL_ARB_vertex_array_object
glDeleteVertexArrays(1, &mGLArray);
releaseVAOName(mGLArray);
#endif
}
@ -1211,13 +1235,13 @@ void LLVertexBuffer::updateNumVerts(S32 nverts)
llassert(nverts >= 0);
if (nverts >= 65535)
if (nverts > 65536)
{
// <FS:ND> FIRE-5077; Just print an info if there are more than 0xFFFF, for now just so there is a message in the logs where in older version #vertices would have been capped.
// llwarns << "Vertex buffer overflow!" << llendl;
// nverts = 65535;
llinfos << "More vertices than 65535 (#" << nverts << ")" <<llendl;
// nverts = 65536;
llinfos << "More vertices than 65536 (#" << nverts << ")" <<llendl;
// </FS:ND>
}
@ -1285,7 +1309,7 @@ void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
if (gGLManager.mHasVertexArrayObject && useVBOs() && (LLRender::sGLCoreProfile || sUseVAO))
{
#if GL_ARB_vertex_array_object
glGenVertexArrays(1, &mGLArray);
mGLArray = getVAOName();
#endif
setupVertexArray();
}
@ -1321,7 +1345,7 @@ void LLVertexBuffer::setupVertexArray()
1, //TYPE_WEIGHT,
4, //TYPE_WEIGHT4,
4, //TYPE_CLOTHWEIGHT,
4, //TYPE_TEXTURE_INDEX
1, //TYPE_TEXTURE_INDEX
};
U32 attrib_type[] =
@ -1338,7 +1362,7 @@ void LLVertexBuffer::setupVertexArray()
GL_FLOAT, //TYPE_WEIGHT,
GL_FLOAT, //TYPE_WEIGHT4,
GL_FLOAT, //TYPE_CLOTHWEIGHT,
GL_UNSIGNED_BYTE, //TYPE_TEXTURE_INDEX
GL_UNSIGNED_INT, //TYPE_TEXTURE_INDEX
};
bool attrib_integer[] =
@ -2155,6 +2179,16 @@ void LLVertexBuffer::flush()
}
}
// bind for transform feedback (quick 'n dirty)
void LLVertexBuffer::bindForFeedback(U32 channel, U32 type, U32 index, U32 count)
{
#ifdef GL_TRANSFORM_FEEDBACK_BUFFER
U32 offset = mOffsets[type] + sTypeSize[type]*index;
U32 size= (sTypeSize[type]*count);
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, channel, mGLBuffer, offset, size);
#endif
}
// Set for rendering
void LLVertexBuffer::setBuffer(U32 data_mask)
{
@ -2306,10 +2340,10 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
stop_glerror();
volatile U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
/*if ((data_mask & mTypeMask) != data_mask)
if (gDebugGL && ((data_mask & mTypeMask) != data_mask))
{
llerrs << "LLVertexBuffer::setupVertexBuffer missing required components for supplied data mask." << llendl;
}*/
}
if (LLGLSLShader::sNoFixedFunction)
{
@ -2385,7 +2419,7 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
#if !LL_DARWIN
S32 loc = TYPE_TEXTURE_INDEX;
void *ptr = (void*) (base + mOffsets[TYPE_VERTEX] + 12);
glVertexAttribIPointer(loc, 4, GL_UNSIGNED_BYTE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
glVertexAttribIPointer(loc, 1, GL_UNSIGNED_INT, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
#endif
}
if (data_mask & MAP_VERTEX)

View File

@ -57,23 +57,28 @@ public:
static U32 sBytesPooled;
static U32 sIndexBytesPooled;
LLVBOPool(U32 vboUsage, U32 vboType)
: mUsage(vboUsage)
, mType(vboType)
{}
static U32 sCurGLName;
LLVBOPool(U32 vboUsage, U32 vboType);
const U32 mUsage;
const U32 mType;
//size MUST be a power of 2
volatile U8* allocate(U32& name, U32 size);
volatile U8* allocate(U32& name, U32 size, bool for_seed = false);
//size MUST be the size provided to allocate that returned the given name
void release(U32 name, volatile U8* buffer, U32 size);
//batch allocate buffers to be provided to the application on demand
void seedPool();
//destroy all records in mFreeList
void cleanup();
U32 genBuffer();
void deleteBuffer(U32 name);
class Record
{
public:
@ -81,16 +86,14 @@ public:
volatile U8* mClientData;
};
std::list<U32> mGLNamePool;
typedef std::list<Record> record_list_t;
std::vector<record_list_t> mFreeList;
std::vector<U32> mMissCount;
};
class LLGLFence
{
public:
virtual void placeFence() = 0;
virtual void wait() = 0;
};
//============================================================================
// base class
@ -125,13 +128,22 @@ public:
static LLVBOPool sStreamIBOPool;
static LLVBOPool sDynamicIBOPool;
static std::list<U32> sAvailableVAOName;
static U32 sCurVAOName;
static bool sUseStreamDraw;
static bool sUseVAO;
static bool sPreferStreamDraw;
static void seedPools();
static U32 getVAOName();
static void releaseVAOName(U32 name);
static void initClass(bool use_vbo, bool no_vbo_mapping);
static void cleanupClass();
static void setupClientArrays(U32 data_mask);
static void pushPositions(U32 mode, const LLVector4a* pos, U32 count);
static void drawArrays(U32 mode, const std::vector<LLVector3>& pos, const std::vector<LLVector3>& norm);
static void drawElements(U32 mode, const LLVector4a* pos, const LLVector2* tc, S32 num_indices, const U16* indicesp);
@ -208,7 +220,6 @@ protected:
void destroyGLIndices();
void updateNumVerts(S32 nverts);
void updateNumIndices(S32 nindices);
bool useVBOs() const;
void unmapBuffer();
public:
@ -218,6 +229,8 @@ public:
volatile U8* mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range);
volatile U8* mapIndexBuffer(S32 index, S32 count, bool map_range);
void bindForFeedback(U32 channel, U32 type, U32 index, U32 count);
// set for rendering
virtual void setBuffer(U32 data_mask); // calls setupVertexBuffer() if data_mask is not 0
void flush(); //flush pending data to GL memory
@ -240,12 +253,14 @@ public:
bool getNormalStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getBinormalStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getColorStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getTextureIndexStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getEmissiveStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getWeightStrider(LLStrider<F32>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getWeight4Strider(LLStrider<LLVector4>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index=0, S32 count = -1, bool map_range = false);
bool useVBOs() const;
bool isEmpty() const { return mEmpty; }
bool isLocked() const { return mVertexLocked || mIndexLocked; }
S32 getNumVerts() const { return mNumVerts; }

View File

@ -24,6 +24,7 @@ include_directories(
${LLWINDOW_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
${LIBS_PREBUILD_DIR}/include/hunspell
${TEAPOT_INCLUDE_DIRS}# <FS:AW opensim currency support>
)
@ -86,6 +87,7 @@ set(llui_SOURCE_FILES
llsearcheditor.cpp
llslider.cpp
llsliderctrl.cpp
llspellcheck.cpp
llspinctrl.cpp
llstatbar.cpp
llstatgraph.cpp
@ -193,6 +195,8 @@ set(llui_HEADER_FILES
llscrolllistitem.h
llsliderctrl.h
llslider.h
llspellcheck.h
llspellcheckmenuhandler.h
llspinctrl.h
llstatbar.h
llstatgraph.h
@ -262,6 +266,7 @@ target_link_libraries(llui
${LLXUIXML_LIBRARIES}
${LLXML_LIBRARIES}
${LLMATH_LIBRARIES}
${HUNSPELL_LIBRARY}
${TEAPOT_LIBRARIES}# <FS:AW opensim currency support>
${LLCOMMON_LIBRARIES} # must be after llimage, llwindow, llrender
)

View File

@ -45,6 +45,7 @@
#include "llkeyboard.h"
#include "llrect.h"
#include "llresmgr.h"
#include "llspellcheck.h"
#include "llstring.h"
#include "llwindow.h"
#include "llui.h"
@ -66,6 +67,7 @@ const S32 SCROLL_INCREMENT_ADD = 0; // make space for typing
const S32 SCROLL_INCREMENT_DEL = 4; // make space for baskspacing
const F32 AUTO_SCROLL_TIME = 0.05f;
const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. *TODO: make this equal to the double click interval?
const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on
const std::string PASSWORD_ASTERISK( "\xE2\x80\xA2" ); // U+2022 BULLET
@ -89,6 +91,7 @@ LLLineEditor::Params::Params()
background_image_focused("background_image_focused"),
select_on_focus("select_on_focus", false),
revert_on_esc("revert_on_esc", true),
spellcheck("spellcheck", false),
commit_on_focus_lost("commit_on_focus_lost", true),
ignore_tab("ignore_tab", true),
is_password("is_password", false),
@ -135,6 +138,9 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mIgnoreArrowKeys( FALSE ),
mIgnoreTab( p.ignore_tab ),
mDrawAsterixes( p.is_password ),
mSpellCheck( p.spellcheck ),
mSpellCheckStart(-1),
mSpellCheckEnd(-1),
mSelectAllonFocusReceived( p.select_on_focus ),
mSelectAllonCommit( TRUE ),
mPassDelete(FALSE),
@ -152,7 +158,8 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mHighlightColor(p.highlight_color()),
mPreeditBgColor(p.preedit_bg_color()),
mGLFont(p.font),
mContextMenuHandle()
mContextMenuHandle(),
mAutoreplaceCallback()
{
llassert( mMaxLengthBytes > 0 );
@ -178,6 +185,12 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
updateTextPadding();
setCursor(mText.length());
if (mSpellCheck)
{
LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLLineEditor::onSpellCheckSettingsChange, this));
}
mSpellCheckTimer.reset();
setPrevalidateInput(p.prevalidate_input_callback());
setPrevalidate(p.prevalidate_callback());
@ -520,6 +533,99 @@ void LLLineEditor::selectAll()
//updatePrimary();
}
bool LLLineEditor::getSpellCheck() const
{
return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck);
}
const std::string& LLLineEditor::getSuggestion(U32 index) const
{
return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null;
}
U32 LLLineEditor::getSuggestionCount() const
{
return mSuggestionList.size();
}
void LLLineEditor::replaceWithSuggestion(U32 index)
{
for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
{
if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) )
{
deselect();
// Delete the misspelled word
mText.erase(it->first, it->second - it->first);
// Insert the suggestion in its place
LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]);
mText.insert(it->first, suggestion);
setCursor(it->first + (S32)suggestion.length());
break;
}
}
mSpellCheckStart = mSpellCheckEnd = -1;
}
void LLLineEditor::addToDictionary()
{
if (canAddToDictionary())
{
LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos));
}
}
bool LLLineEditor::canAddToDictionary() const
{
return (getSpellCheck()) && (isMisspelledWord(mCursorPos));
}
void LLLineEditor::addToIgnore()
{
if (canAddToIgnore())
{
LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos));
}
}
bool LLLineEditor::canAddToIgnore() const
{
return (getSpellCheck()) && (isMisspelledWord(mCursorPos));
}
std::string LLLineEditor::getMisspelledWord(U32 pos) const
{
for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
{
if ( (it->first <= pos) && (it->second >= pos) )
{
return wstring_to_utf8str(mText.getWString().substr(it->first, it->second - it->first));
}
}
return LLStringUtil::null;
}
bool LLLineEditor::isMisspelledWord(U32 pos) const
{
for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
{
if ( (it->first <= pos) && (it->second >= pos) )
{
return true;
}
}
return false;
}
void LLLineEditor::onSpellCheckSettingsChange()
{
// Recheck the spelling on every change
mMisspellRanges.clear();
mSpellCheckStart = mSpellCheckEnd = -1;
}
BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
{
setFocus( TRUE );
@ -867,6 +973,12 @@ void LLLineEditor::addChar(const llwchar uni_char)
LLUI::reportBadKeystroke();
}
if (!mReadOnly && mAutoreplaceCallback != NULL)
{
// call callback
mAutoreplaceCallback(mText, mCursorPos);
}
getWindow()->hideCursorUntilMouseMove();
}
@ -1061,9 +1173,8 @@ void LLLineEditor::cut()
LLUI::reportBadKeystroke();
}
else
if( mKeystrokeCallback )
{
mKeystrokeCallback( this );
onKeystroke();
}
}
}
@ -1190,9 +1301,8 @@ void LLLineEditor::pasteHelper(bool is_primary)
LLUI::reportBadKeystroke();
}
else
if( mKeystrokeCallback )
{
mKeystrokeCallback( this );
onKeystroke();
}
}
}
@ -1446,9 +1556,10 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask )
// Notify owner if requested
if (!need_to_rollback && handled)
{
if (mKeystrokeCallback)
onKeystroke();
if ( (!selection_modified) && (KEY_BACKSPACE == key) )
{
mKeystrokeCallback(this);
mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
}
}
@ -1501,12 +1612,11 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
// Notify owner if requested
if( !need_to_rollback && handled )
{
if( mKeystrokeCallback )
{
// HACK! The only usage of this callback doesn't do anything with the character.
// We'll have to do something about this if something ever changes! - Doug
mKeystrokeCallback( this );
}
// HACK! The only usage of this callback doesn't do anything with the character.
// We'll have to do something about this if something ever changes! - Doug
onKeystroke();
mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
}
return handled;
@ -1535,9 +1645,7 @@ void LLLineEditor::doDelete()
if (!prevalidateInput(text_to_delete))
{
if( mKeystrokeCallback )
mKeystrokeCallback( this );
onKeystroke();
return;
}
setCursor(getCursor() + 1);
@ -1553,10 +1661,9 @@ void LLLineEditor::doDelete()
}
else
{
if( mKeystrokeCallback )
{
mKeystrokeCallback( this );
}
onKeystroke();
mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
}
}
@ -1628,6 +1735,10 @@ void LLLineEditor::draw()
background.stretch( -mBorderThickness );
S32 lineeditor_v_pad = (background.getHeight() - mGLFont->getLineHeight()) / 2;
if (mSpellCheck)
{
lineeditor_v_pad += 1;
}
drawBackground();
@ -1702,14 +1813,14 @@ void LLLineEditor::draw()
{
S32 select_left;
S32 select_right;
if( mSelectionStart < getCursor() )
if (mSelectionStart < mSelectionEnd)
{
select_left = mSelectionStart;
select_right = getCursor();
select_right = mSelectionEnd;
}
else
{
select_left = getCursor();
select_left = mSelectionEnd;
select_right = mSelectionStart;
}
@ -1753,7 +1864,7 @@ void LLLineEditor::draw()
if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
{
// unselected, right side
mGLFont->render(
rendered_text += mGLFont->render(
mText, mScrollHPos + rendered_text,
rendered_pixels_right, text_bottom,
text_color,
@ -1767,7 +1878,7 @@ void LLLineEditor::draw()
}
else
{
mGLFont->render(
rendered_text = mGLFont->render(
mText, mScrollHPos,
rendered_pixels_right, text_bottom,
text_color,
@ -1782,6 +1893,101 @@ void LLLineEditor::draw()
mBorder->setVisible(FALSE); // no more programmatic art.
#endif
if ( (getSpellCheck()) && (mText.length() > 2) )
{
// Calculate start and end indices for the first and last visible word
U32 start = prevWordPos(mScrollHPos), end = nextWordPos(mScrollHPos + rendered_text);
if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) )
{
const LLWString& text = mText.getWString().substr(start, end);
// Find the start of the first word
U32 word_start = 0, word_end = 0;
while ( (word_start < text.length()) && (!LLStringOps::isAlpha(text[word_start])) )
{
word_start++;
}
// Iterate over all words in the text block and check them one by one
mMisspellRanges.clear();
while (word_start < text.length())
{
// Find the end of the current word (special case handling for "'" when it's used as a contraction)
word_end = word_start + 1;
while ( (word_end < text.length()) &&
((LLWStringUtil::isPartOfWord(text[word_end])) ||
((L'\'' == text[word_end]) && (word_end + 1 < text.length()) &&
(LLStringOps::isAlnum(text[word_end - 1])) && (LLStringOps::isAlnum(text[word_end + 1])))) )
{
word_end++;
}
if (word_end > text.length())
{
break;
}
// Don't process words shorter than 3 characters
std::string word = wstring_to_utf8str(text.substr(word_start, word_end - word_start));
if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) )
{
mMisspellRanges.push_back(std::pair<U32, U32>(start + word_start, start + word_end));
}
// Find the start of the next word
word_start = word_end + 1;
while ( (word_start < text.length()) && (!LLWStringUtil::isPartOfWord(text[word_start])) )
{
word_start++;
}
}
mSpellCheckStart = start;
mSpellCheckEnd = end;
}
// Draw squiggly lines under any (visible) misspelled words
for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
{
// Skip over words that aren't (partially) visible
if ( ((it->first < start) && (it->second < start)) || (it->first > end) )
{
continue;
}
// Skip the current word if the user is still busy editing it
if ( (!mSpellCheckTimer.hasExpired()) && (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) )
{
continue;
}
S32 pxWidth = getRect().getWidth();
S32 pxStart = findPixelNearestPos(it->first - getCursor());
if (pxStart > pxWidth)
{
continue;
}
S32 pxEnd = findPixelNearestPos(it->second - getCursor());
if (pxEnd > pxWidth)
{
pxEnd = pxWidth;
}
S32 pxBottom = (S32)(text_bottom + mGLFont->getDescenderHeight());
gGL.color4ub(255, 0, 0, 200);
while (pxStart + 1 < pxEnd)
{
gl_line_2d(pxStart, pxBottom, pxStart + 2, pxBottom - 2);
if (pxStart + 3 < pxEnd)
{
gl_line_2d(pxStart + 2, pxBottom - 3, pxStart + 4, pxBottom - 1);
}
pxStart += 4;
}
}
}
// If we're editing...
if( hasFocus())
{
@ -2113,6 +2319,15 @@ void LLLineEditor::setSelectAllonFocusReceived(BOOL b)
mSelectAllonFocusReceived = b;
}
void LLLineEditor::onKeystroke()
{
if (mKeystrokeCallback)
{
mKeystrokeCallback(this);
}
mSpellCheckStart = mSpellCheckEnd = -1;
}
void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data)
{
@ -2235,10 +2450,9 @@ void LLLineEditor::updatePreedit(const LLWString &preedit_string,
// Update of the preedit should be caused by some key strokes.
mKeystrokeTimer.reset();
if( mKeystrokeCallback )
{
mKeystrokeCallback( this );
}
onKeystroke();
mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
@ -2390,7 +2604,38 @@ void LLLineEditor::showContextMenu(S32 x, S32 y)
S32 screen_x, screen_y;
localPointToScreen(x, y, &screen_x, &screen_y);
menu->show(screen_x, screen_y);
setCursorAtLocalPos(x);
if (hasSelection())
{
if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) )
{
deselect();
}
else
{
setCursor(llmax(mSelectionStart, mSelectionEnd));
}
}
bool use_spellcheck = getSpellCheck(), is_misspelled = false;
if (use_spellcheck)
{
mSuggestionList.clear();
// If the cursor is on a misspelled word, retrieve suggestions for it
std::string misspelled_word = getMisspelledWord(mCursorPos);
if ((is_misspelled = !misspelled_word.empty()) == true)
{
LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList);
}
}
menu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty()));
menu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled));
menu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled));
menu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled));
menu->show(screen_x, screen_y, this);
}
}

View File

@ -40,6 +40,7 @@
#include "llframetimer.h"
#include "lleditmenuhandler.h"
#include "llspellcheckmenuhandler.h"
#include "lluictrl.h"
#include "lluiimage.h"
#include "lluistring.h"
@ -54,7 +55,7 @@ class LLButton;
class LLContextMenu;
class LLLineEditor
: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor
: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor, public LLSpellCheckMenuHandler
{
public:
@ -86,6 +87,7 @@ public:
Optional<bool> select_on_focus,
revert_on_esc,
spellcheck,
commit_on_focus_lost,
ignore_tab,
is_password;
@ -146,6 +148,24 @@ public:
virtual void deselect();
virtual BOOL canDeselect() const;
// LLSpellCheckMenuHandler overrides
/*virtual*/ bool getSpellCheck() const;
/*virtual*/ const std::string& getSuggestion(U32 index) const;
/*virtual*/ U32 getSuggestionCount() const;
/*virtual*/ void replaceWithSuggestion(U32 index);
/*virtual*/ void addToDictionary();
/*virtual*/ bool canAddToDictionary() const;
/*virtual*/ void addToIgnore();
/*virtual*/ bool canAddToIgnore() const;
// Spell checking helper functions
std::string getMisspelledWord(U32 pos) const;
bool isMisspelledWord(U32 pos) const;
void onSpellCheckSettingsChange();
// view overrides
virtual void draw();
virtual void reshape(S32 width,S32 height,BOOL called_from_parent=TRUE);
@ -169,6 +189,9 @@ public:
virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text );
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
typedef boost::function<void(LLUIString&, S32&)> autoreplace_callback_t;
autoreplace_callback_t mAutoreplaceCallback;
void setAutoreplaceCallback(autoreplace_callback_t cb) { mAutoreplaceCallback = cb; }
void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; }
const std::string& getLabel() { return mLabel.getString(); }
@ -223,6 +246,7 @@ public:
void setSelectAllonFocusReceived(BOOL b);
void setSelectAllonCommit(BOOL b) { mSelectAllonCommit = b; }
void onKeystroke();
typedef boost::function<void (LLLineEditor* caller, void* user_data)> callback_t;
void setKeystrokeCallback(callback_t callback, void* user_data);
@ -322,6 +346,13 @@ protected:
S32 mLastSelectionStart;
S32 mLastSelectionEnd;
bool mSpellCheck;
S32 mSpellCheckStart;
S32 mSpellCheckEnd;
LLTimer mSpellCheckTimer;
std::list<std::pair<U32, U32> > mMisspellRanges;
std::vector<std::string> mSuggestionList;
LLTextValidate::validate_func_t mPrevalidateFunc;
LLTextValidate::validate_func_t mPrevalidateInputFunc;

View File

@ -3854,7 +3854,7 @@ void LLContextMenu::setVisible(BOOL visible)
}
// Takes cursor position in screen space?
void LLContextMenu::show(S32 x, S32 y)
void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view)
{
if (getChildList()->empty())
{
@ -3908,6 +3908,14 @@ void LLContextMenu::show(S32 x, S32 y)
setRect(rect);
arrange();
if (spawning_view)
{
mSpawningViewHandle = spawning_view->getHandle();
}
else
{
mSpawningViewHandle.markDead();
}
LLView::setVisible(TRUE);
}

View File

@ -670,7 +670,7 @@ public:
virtual void draw ();
virtual void show (S32 x, S32 y);
virtual void show (S32 x, S32 y, LLView* spawning_view = NULL);
virtual void hide ();
virtual BOOL handleHover ( S32 x, S32 y, MASK mask );
@ -683,10 +683,14 @@ public:
LLHandle<LLContextMenu> getHandle() { return getDerivedHandle<LLContextMenu>(); }
LLView* getSpawningView() const { return mSpawningViewHandle.get(); }
void setSpawningView(LLHandle<LLView> spawning_view) { mSpawningViewHandle = spawning_view; }
protected:
BOOL mHoveredAnyItem;
LLMenuItemGL* mHoverItem;
LLRootHandle<LLContextMenu> mHandle;
LLHandle<LLView> mSpawningViewHandle;
};

View File

@ -389,12 +389,11 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height
{
*show_h_scrollbar = TRUE;
*visible_height -= scrollbar_size;
// Note: Do *not* recompute *show_v_scrollbar here because with
// The view inside the scroll container should not be extended
// to container's full height to ensure the correct computation
// of *show_v_scrollbar after subtracting horizontal scrollbar_size.
// Must retest now that visible_height has changed
if( !*show_v_scrollbar && ((doc_height - *visible_height) > 1) )
{
*show_v_scrollbar = TRUE;

505
indra/llui/llspellcheck.cpp Normal file
View File

@ -0,0 +1,505 @@
/**
* @file llspellcheck.cpp
* @brief Spell checking functionality
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "lldir.h"
#include "llsdserialize.h"
#include "llspellcheck.h"
#if LL_WINDOWS
#include <hunspell/hunspelldll.h>
#pragma comment(lib, "libhunspell.lib")
#else
#include <hunspell/hunspell.hxx>
#endif
static const std::string DICT_DIR = "dictionaries";
static const std::string DICT_FILE_CUSTOM = "user_custom.dic";
static const std::string DICT_FILE_IGNORE = "user_ignore.dic";
static const std::string DICT_FILE_MAIN = "dictionaries.xml";
static const std::string DICT_FILE_USER = "user_dictionaries.xml";
LLSD LLSpellChecker::sDictMap;
LLSpellChecker::settings_change_signal_t LLSpellChecker::sSettingsChangeSignal;
LLSpellChecker::LLSpellChecker()
: mHunspell(NULL)
{
// Load initial dictionary information
refreshDictionaryMap();
}
LLSpellChecker::~LLSpellChecker()
{
delete mHunspell;
}
bool LLSpellChecker::checkSpelling(const std::string& word) const
{
if ( (!mHunspell) || (word.length() < 3) || (0 != mHunspell->spell(word.c_str())) )
{
return true;
}
if (mIgnoreList.size() > 0)
{
std::string word_lower(word);
LLStringUtil::toLower(word_lower);
return (mIgnoreList.end() != std::find(mIgnoreList.begin(), mIgnoreList.end(), word_lower));
}
return false;
}
S32 LLSpellChecker::getSuggestions(const std::string& word, std::vector<std::string>& suggestions) const
{
suggestions.clear();
if ( (!mHunspell) || (word.length() < 3) )
{
return 0;
}
char** suggestion_list; int suggestion_cnt = 0;
if ( (suggestion_cnt = mHunspell->suggest(&suggestion_list, word.c_str())) != 0 )
{
for (int suggestion_index = 0; suggestion_index < suggestion_cnt; suggestion_index++)
{
suggestions.push_back(suggestion_list[suggestion_index]);
}
mHunspell->free_list(&suggestion_list, suggestion_cnt);
}
return suggestions.size();
}
// static
const LLSD LLSpellChecker::getDictionaryData(const std::string& dict_language)
{
for (LLSD::array_const_iterator it = sDictMap.beginArray(); it != sDictMap.endArray(); ++it)
{
const LLSD& dict_entry = *it;
if (dict_language == dict_entry["language"].asString())
{
return dict_entry;
}
}
return LLSD();
}
// static
bool LLSpellChecker::hasDictionary(const std::string& dict_language, bool check_installed)
{
const LLSD dict_info = getDictionaryData(dict_language);
return dict_info.has("language") && ( (!check_installed) || (dict_info["installed"].asBoolean()) );
}
// static
void LLSpellChecker::setDictionaryData(const LLSD& dict_info)
{
const std::string dict_language = dict_info["language"].asString();
if (dict_language.empty())
{
return;
}
for (LLSD::array_iterator it = sDictMap.beginArray(); it != sDictMap.endArray(); ++it)
{
LLSD& dict_entry = *it;
if (dict_language == dict_entry["language"].asString())
{
dict_entry = dict_info;
return;
}
}
sDictMap.append(dict_info);
return;
}
// static
void LLSpellChecker::refreshDictionaryMap()
{
const std::string app_path = getDictionaryAppPath();
const std::string user_path = getDictionaryUserPath();
// Load dictionary information (file name, friendly name, ...)
llifstream user_file(user_path + DICT_FILE_MAIN, std::ios::binary);
if ( (!user_file.is_open()) || (0 == LLSDSerialize::fromXMLDocument(sDictMap, user_file)) || (0 == sDictMap.size()) )
{
llifstream app_file(app_path + DICT_FILE_MAIN, std::ios::binary);
if ( (!app_file.is_open()) || (0 == LLSDSerialize::fromXMLDocument(sDictMap, app_file)) || (0 == sDictMap.size()) )
{
return;
}
}
// Load user installed dictionary information
llifstream custom_file(user_path + DICT_FILE_USER, std::ios::binary);
if (custom_file.is_open())
{
LLSD custom_dict_map;
LLSDSerialize::fromXMLDocument(custom_dict_map, custom_file);
for (LLSD::array_iterator it = custom_dict_map.beginArray(); it != custom_dict_map.endArray(); ++it)
{
LLSD& dict_info = *it;
dict_info["user_installed"] = true;
setDictionaryData(dict_info);
}
custom_file.close();
}
// Look for installed dictionaries
std::string tmp_app_path, tmp_user_path;
for (LLSD::array_iterator it = sDictMap.beginArray(); it != sDictMap.endArray(); ++it)
{
LLSD& sdDict = *it;
tmp_app_path = (sdDict.has("name")) ? app_path + sdDict["name"].asString() : LLStringUtil::null;
tmp_user_path = (sdDict.has("name")) ? user_path + sdDict["name"].asString() : LLStringUtil::null;
sdDict["installed"] =
(!tmp_app_path.empty()) && ((gDirUtilp->fileExists(tmp_user_path + ".dic")) || (gDirUtilp->fileExists(tmp_app_path + ".dic")));
}
sSettingsChangeSignal();
}
void LLSpellChecker::addToCustomDictionary(const std::string& word)
{
if (mHunspell)
{
mHunspell->add(word.c_str());
}
addToDictFile(getDictionaryUserPath() + DICT_FILE_CUSTOM, word);
sSettingsChangeSignal();
}
void LLSpellChecker::addToIgnoreList(const std::string& word)
{
std::string word_lower(word);
LLStringUtil::toLower(word_lower);
if (mIgnoreList.end() == std::find(mIgnoreList.begin(), mIgnoreList.end(), word_lower))
{
mIgnoreList.push_back(word_lower);
addToDictFile(getDictionaryUserPath() + DICT_FILE_IGNORE, word_lower);
sSettingsChangeSignal();
}
}
void LLSpellChecker::addToDictFile(const std::string& dict_path, const std::string& word)
{
std::vector<std::string> word_list;
if (gDirUtilp->fileExists(dict_path))
{
llifstream file_in(dict_path, std::ios::in);
if (file_in.is_open())
{
std::string word; int line_num = 0;
while (getline(file_in, word))
{
// Skip over the first line since that's just a line count
if (0 != line_num)
{
word_list.push_back(word);
}
line_num++;
}
}
else
{
// TODO: show error message?
return;
}
}
word_list.push_back(word);
llofstream file_out(dict_path, std::ios::out | std::ios::trunc);
if (file_out.is_open())
{
file_out << word_list.size() << std::endl;
for (std::vector<std::string>::const_iterator itWord = word_list.begin(); itWord != word_list.end(); ++itWord)
{
file_out << *itWord << std::endl;
}
file_out.close();
}
}
bool LLSpellChecker::isActiveDictionary(const std::string& dict_language) const
{
return
(mDictLanguage == dict_language) ||
(mDictSecondary.end() != std::find(mDictSecondary.begin(), mDictSecondary.end(), dict_language));
}
void LLSpellChecker::setSecondaryDictionaries(dict_list_t dict_list)
{
if (!getUseSpellCheck())
{
return;
}
// Check if we're only adding secondary dictionaries, or removing them
dict_list_t dict_add(llmax(dict_list.size(), mDictSecondary.size())), dict_rem(llmax(dict_list.size(), mDictSecondary.size()));
dict_list.sort();
mDictSecondary.sort();
dict_list_t::iterator end_added = std::set_difference(dict_list.begin(), dict_list.end(), mDictSecondary.begin(), mDictSecondary.end(), dict_add.begin());
dict_list_t::iterator end_removed = std::set_difference(mDictSecondary.begin(), mDictSecondary.end(), dict_list.begin(), dict_list.end(), dict_rem.begin());
if (end_removed != dict_rem.begin()) // We can't remove secondary dictionaries so we need to recreate the Hunspell instance
{
mDictSecondary = dict_list;
std::string dict_language = mDictLanguage;
initHunspell(dict_language);
}
else if (end_added != dict_add.begin()) // Add the new secondary dictionaries one by one
{
const std::string app_path = getDictionaryAppPath();
const std::string user_path = getDictionaryUserPath();
for (dict_list_t::const_iterator it_added = dict_add.begin(); it_added != end_added; ++it_added)
{
const LLSD dict_entry = getDictionaryData(*it_added);
if ( (!dict_entry.isDefined()) || (!dict_entry["installed"].asBoolean()) )
{
continue;
}
const std::string strFileDic = dict_entry["name"].asString() + ".dic";
if (gDirUtilp->fileExists(user_path + strFileDic))
{
mHunspell->add_dic((user_path + strFileDic).c_str());
}
else if (gDirUtilp->fileExists(app_path + strFileDic))
{
mHunspell->add_dic((app_path + strFileDic).c_str());
}
}
mDictSecondary = dict_list;
sSettingsChangeSignal();
}
}
void LLSpellChecker::initHunspell(const std::string& dict_language)
{
if (mHunspell)
{
delete mHunspell;
mHunspell = NULL;
mDictLanguage.clear();
mDictFile.clear();
mIgnoreList.clear();
}
const LLSD dict_entry = (!dict_language.empty()) ? getDictionaryData(dict_language) : LLSD();
if ( (!dict_entry.isDefined()) || (!dict_entry["installed"].asBoolean()) || (!dict_entry["is_primary"].asBoolean()))
{
sSettingsChangeSignal();
return;
}
const std::string app_path = getDictionaryAppPath();
const std::string user_path = getDictionaryUserPath();
if (dict_entry.has("name"))
{
const std::string filename_aff = dict_entry["name"].asString() + ".aff";
const std::string filename_dic = dict_entry["name"].asString() + ".dic";
if ( (gDirUtilp->fileExists(user_path + filename_aff)) && (gDirUtilp->fileExists(user_path + filename_dic)) )
{
mHunspell = new Hunspell((user_path + filename_aff).c_str(), (user_path + filename_dic).c_str());
}
else if ( (gDirUtilp->fileExists(app_path + filename_aff)) && (gDirUtilp->fileExists(app_path + filename_dic)) )
{
mHunspell = new Hunspell((app_path + filename_aff).c_str(), (app_path + filename_dic).c_str());
}
if (!mHunspell)
{
return;
}
mDictLanguage = dict_language;
mDictFile = dict_entry["name"].asString();
if (gDirUtilp->fileExists(user_path + DICT_FILE_CUSTOM))
{
mHunspell->add_dic((user_path + DICT_FILE_CUSTOM).c_str());
}
if (gDirUtilp->fileExists(user_path + DICT_FILE_IGNORE))
{
llifstream file_in(user_path + DICT_FILE_IGNORE, std::ios::in);
if (file_in.is_open())
{
std::string word; int idxLine = 0;
while (getline(file_in, word))
{
// Skip over the first line since that's just a line count
if (0 != idxLine)
{
LLStringUtil::toLower(word);
mIgnoreList.push_back(word);
}
idxLine++;
}
}
}
for (dict_list_t::const_iterator it = mDictSecondary.begin(); it != mDictSecondary.end(); ++it)
{
const LLSD dict_entry = getDictionaryData(*it);
if ( (!dict_entry.isDefined()) || (!dict_entry["installed"].asBoolean()) )
{
continue;
}
const std::string filename_dic = dict_entry["name"].asString() + ".dic";
if (gDirUtilp->fileExists(user_path + filename_dic))
{
mHunspell->add_dic((user_path + filename_dic).c_str());
}
else if (gDirUtilp->fileExists(app_path + filename_dic))
{
mHunspell->add_dic((app_path + filename_dic).c_str());
}
}
}
sSettingsChangeSignal();
}
// static
const std::string LLSpellChecker::getDictionaryAppPath()
{
std::string dict_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, DICT_DIR, "");
return dict_path;
}
// static
const std::string LLSpellChecker::getDictionaryUserPath()
{
std::string dict_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, DICT_DIR, "");
if (!gDirUtilp->fileExists(dict_path))
{
LLFile::mkdir(dict_path);
}
return dict_path;
}
// static
bool LLSpellChecker::getUseSpellCheck()
{
return (LLSpellChecker::instanceExists()) && (LLSpellChecker::instance().mHunspell);
}
// static
bool LLSpellChecker::canRemoveDictionary(const std::string& dict_language)
{
// Only user-installed inactive dictionaries can be removed
const LLSD dict_info = getDictionaryData(dict_language);
return
(dict_info["user_installed"].asBoolean()) &&
( (!getUseSpellCheck()) || (!LLSpellChecker::instance().isActiveDictionary(dict_language)) );
}
// static
void LLSpellChecker::removeDictionary(const std::string& dict_language)
{
if (!canRemoveDictionary(dict_language))
{
return;
}
LLSD dict_map = loadUserDictionaryMap();
for (LLSD::array_const_iterator it = dict_map.beginArray(); it != dict_map.endArray(); ++it)
{
const LLSD& dict_info = *it;
if (dict_info["language"].asString() == dict_language)
{
const std::string dict_dic = getDictionaryUserPath() + dict_info["name"].asString() + ".dic";
if (gDirUtilp->fileExists(dict_dic))
{
LLFile::remove(dict_dic);
}
const std::string dict_aff = getDictionaryUserPath() + dict_info["name"].asString() + ".aff";
if (gDirUtilp->fileExists(dict_aff))
{
LLFile::remove(dict_aff);
}
dict_map.erase(it - dict_map.beginArray());
break;
}
}
saveUserDictionaryMap(dict_map);
refreshDictionaryMap();
}
// static
LLSD LLSpellChecker::loadUserDictionaryMap()
{
LLSD dict_map;
llifstream dict_file(getDictionaryUserPath() + DICT_FILE_USER, std::ios::binary);
if (dict_file.is_open())
{
LLSDSerialize::fromXMLDocument(dict_map, dict_file);
dict_file.close();
}
return dict_map;
}
// static
void LLSpellChecker::saveUserDictionaryMap(const LLSD& dict_map)
{
llofstream dict_file(getDictionaryUserPath() + DICT_FILE_USER, std::ios::trunc);
if (dict_file.is_open())
{
LLSDSerialize::toPrettyXML(dict_map, dict_file);
dict_file.close();
}
}
// static
boost::signals2::connection LLSpellChecker::setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb)
{
return sSettingsChangeSignal.connect(cb);
}
// static
void LLSpellChecker::setUseSpellCheck(const std::string& dict_language)
{
if ( (((dict_language.empty()) && (getUseSpellCheck())) || (!dict_language.empty())) &&
(LLSpellChecker::instance().mDictLanguage != dict_language) )
{
LLSpellChecker::instance().initHunspell(dict_language);
}
}
// static
void LLSpellChecker::initClass()
{
if (sDictMap.isUndefined())
{
refreshDictionaryMap();
}
}

93
indra/llui/llspellcheck.h Normal file
View File

@ -0,0 +1,93 @@
/**
* @file llspellcheck.h
* @brief Spell checking functionality
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLSPELLCHECK_H
#define LLSPELLCHECK_H
#include "llsingleton.h"
#include "llui.h"
#include <boost/signals2.hpp>
class Hunspell;
class LLSpellChecker : public LLSingleton<LLSpellChecker>, public LLInitClass<LLSpellChecker>
{
friend class LLSingleton<LLSpellChecker>;
friend class LLInitClass<LLSpellChecker>;
protected:
LLSpellChecker();
~LLSpellChecker();
public:
void addToCustomDictionary(const std::string& word);
void addToIgnoreList(const std::string& word);
bool checkSpelling(const std::string& word) const;
S32 getSuggestions(const std::string& word, std::vector<std::string>& suggestions) const;
protected:
void addToDictFile(const std::string& dict_path, const std::string& word);
void initHunspell(const std::string& dict_language);
public:
typedef std::list<std::string> dict_list_t;
const std::string& getPrimaryDictionary() const { return mDictLanguage; }
const dict_list_t& getSecondaryDictionaries() const { return mDictSecondary; }
bool isActiveDictionary(const std::string& dict_language) const;
void setSecondaryDictionaries(dict_list_t dict_list);
static bool canRemoveDictionary(const std::string& dict_language);
static const std::string getDictionaryAppPath();
static const std::string getDictionaryUserPath();
static const LLSD getDictionaryData(const std::string& dict_language);
static const LLSD& getDictionaryMap() { return sDictMap; }
static bool getUseSpellCheck();
static bool hasDictionary(const std::string& dict_language, bool check_installed = false);
static void refreshDictionaryMap();
static void removeDictionary(const std::string& dict_language);
static void setUseSpellCheck(const std::string& dict_language);
protected:
static LLSD loadUserDictionaryMap();
static void setDictionaryData(const LLSD& dict_info);
static void saveUserDictionaryMap(const LLSD& dict_map);
public:
typedef boost::signals2::signal<void()> settings_change_signal_t;
static boost::signals2::connection setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb);
protected:
static void initClass();
protected:
Hunspell* mHunspell;
std::string mDictLanguage;
std::string mDictFile;
dict_list_t mDictSecondary;
std::vector<std::string> mIgnoreList;
static LLSD sDictMap;
static settings_change_signal_t sSettingsChangeSignal;
};
#endif // LLSPELLCHECK_H

View File

@ -0,0 +1,46 @@
/**
* @file llspellcheckmenuhandler.h
* @brief Interface used by spell check menu handling
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLSPELLCHECKMENUHANDLER_H
#define LLSPELLCHECKMENUHANDLER_H
class LLSpellCheckMenuHandler
{
public:
virtual bool getSpellCheck() const { return false; }
virtual const std::string& getSuggestion(U32 index) const { return LLStringUtil::null; }
virtual U32 getSuggestionCount() const { return 0; }
virtual void replaceWithSuggestion(U32 index){}
virtual void addToDictionary() {}
virtual bool canAddToDictionary() const { return false; }
virtual void addToIgnore() {}
virtual bool canAddToIgnore() const { return false; }
};
#endif // LLSPELLCHECKMENUHANDLER_H

View File

@ -32,6 +32,7 @@
#include "lllocalcliprect.h"
#include "llmenugl.h"
#include "llscrollcontainer.h"
#include "llspellcheck.h"
#include "llstl.h"
#include "lltextparser.h"
#include "lltextutil.h"
@ -158,6 +159,7 @@ LLTextBase::Params::Params()
plain_text("plain_text",false),
track_end("track_end", false),
read_only("read_only", false),
spellcheck("spellcheck", false),
v_pad("v_pad", 0),
h_pad("h_pad", 0),
clip("clip", true),
@ -184,6 +186,9 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
mFontShadow(p.font_shadow),
mPopupMenu(NULL),
mReadOnly(p.read_only),
mSpellCheck(p.spellcheck),
mSpellCheckStart(-1),
mSpellCheckEnd(-1),
mCursorColor(p.cursor_color),
mFgColor(p.text_color),
mBorderVisible( p.border_visible ),
@ -255,6 +260,12 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
addChild(mDocumentView);
}
if (mSpellCheck)
{
LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLTextBase::onSpellCheckSettingsChange, this));
}
mSpellCheckTimer.reset();
createDefaultSegment();
updateRects();
@ -289,12 +300,23 @@ bool LLTextBase::truncate()
if (getLength() >= S32(mMaxTextByteLength / 4))
{
// Have to check actual byte size
LLWString text(getWText());
S32 utf8_byte_size = wstring_utf8_length(text);
S32 utf8_byte_size = 0;
LLSD value = getViewModel()->getValue();
if (value.type() == LLSD::TypeString)
{
// save a copy for strings.
utf8_byte_size = value.size();
}
else
{
// non string LLSDs need explicit conversion to string
utf8_byte_size = value.asString().size();
}
if ( utf8_byte_size > mMaxTextByteLength )
{
// Truncate safely in UTF-8
std::string temp_utf8_text = wstring_to_utf8str(text);
std::string temp_utf8_text = value.asString();
temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength );
LLWString text = utf8str_to_wstring( temp_utf8_text );
// remove extra bit of current string, to preserve formatting, etc.
@ -547,8 +569,92 @@ void LLTextBase::drawText()
return;
}
// Perform spell check if needed
if ( (getSpellCheck()) && (getWText().length() > 2) )
{
// Calculate start and end indices for the spell checking range
S32 start = line_start, end = getLineEnd(last_line);
if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) )
{
const LLWString& wstrText = getWText();
mMisspellRanges.clear();
segment_set_t::const_iterator seg_it = getSegIterContaining(start);
while (mSegments.end() != seg_it)
{
LLTextSegmentPtr text_segment = *seg_it;
if ( (text_segment.isNull()) || (text_segment->getStart() >= end) )
{
break;
}
if (!text_segment->canEdit())
{
++seg_it;
continue;
}
// Combine adjoining text segments into one
U32 seg_start = text_segment->getStart(), seg_end = llmin(text_segment->getEnd(), end);
while (mSegments.end() != ++seg_it)
{
text_segment = *seg_it;
if ( (text_segment.isNull()) || (!text_segment->canEdit()) || (text_segment->getStart() >= end) )
{
break;
}
seg_end = llmin(text_segment->getEnd(), end);
}
// Find the start of the first word
U32 word_start = seg_start, word_end = -1;
while ( (word_start < wstrText.length()) && (!LLStringOps::isAlpha(wstrText[word_start])) )
{
word_start++;
}
// Iterate over all words in the text block and check them one by one
while (word_start < seg_end)
{
// Find the end of the current word (special case handling for "'" when it's used as a contraction)
word_end = word_start + 1;
while ( (word_end < seg_end) &&
((LLWStringUtil::isPartOfWord(wstrText[word_end])) ||
((L'\'' == wstrText[word_end]) &&
(LLStringOps::isAlnum(wstrText[word_end - 1])) && (LLStringOps::isAlnum(wstrText[word_end + 1])))) )
{
word_end++;
}
if (word_end > seg_end)
{
break;
}
// Don't process words shorter than 3 characters
std::string word = wstring_to_utf8str(wstrText.substr(word_start, word_end - word_start));
if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) )
{
mMisspellRanges.push_back(std::pair<U32, U32>(word_start, word_end));
}
// Find the start of the next word
word_start = word_end + 1;
while ( (word_start < seg_end) && (!LLWStringUtil::isPartOfWord(wstrText[word_start])) )
{
word_start++;
}
}
}
mSpellCheckStart = start;
mSpellCheckEnd = end;
}
}
LLTextSegmentPtr cur_segment = *seg_iter;
std::list<std::pair<U32, U32> >::const_iterator misspell_it = std::lower_bound(mMisspellRanges.begin(), mMisspellRanges.end(), std::pair<U32, U32>(line_start, 0));
for (S32 cur_line = first_line; cur_line < last_line; cur_line++)
{
S32 next_line = cur_line + 1;
@ -583,7 +689,8 @@ void LLTextBase::drawText()
cur_segment = *seg_iter;
}
S32 clipped_end = llmin( line_end, cur_segment->getEnd() ) - cur_segment->getStart();
S32 seg_end = llmin(line_end, cur_segment->getEnd());
S32 clipped_end = seg_end - cur_segment->getStart();
if (mUseEllipses // using ellipses
&& clipped_end == line_end // last segment on line
@ -595,6 +702,46 @@ void LLTextBase::drawText()
text_rect.mRight -= 2;
}
// Draw squiggly lines under any visible misspelled words
while ( (mMisspellRanges.end() != misspell_it) && (misspell_it->first < seg_end) && (misspell_it->second > seg_start) )
{
// Skip the current word if the user is still busy editing it
if ( (!mSpellCheckTimer.hasExpired()) && (misspell_it->first <= (U32)mCursorPos) && (misspell_it->second >= (U32)mCursorPos) )
{
++misspell_it;
continue;
}
U32 misspell_start = llmax<U32>(misspell_it->first, seg_start), misspell_end = llmin<U32>(misspell_it->second, seg_end);
S32 squiggle_start = 0, squiggle_end = 0, pony = 0;
cur_segment->getDimensions(seg_start - cur_segment->getStart(), misspell_start - seg_start, squiggle_start, pony);
cur_segment->getDimensions(misspell_start - cur_segment->getStart(), misspell_end - misspell_start, squiggle_end, pony);
squiggle_start += text_rect.mLeft;
pony = (squiggle_end + 3) / 6;
squiggle_start += squiggle_end / 2 - pony * 3;
squiggle_end = squiggle_start + pony * 6;
S32 squiggle_bottom = text_rect.mBottom + (S32)cur_segment->getStyle()->getFont()->getDescenderHeight();
gGL.color4ub(255, 0, 0, 200);
while (squiggle_start + 1 < squiggle_end)
{
gl_line_2d(squiggle_start, squiggle_bottom, squiggle_start + 2, squiggle_bottom - 2);
if (squiggle_start + 3 < squiggle_end)
{
gl_line_2d(squiggle_start + 2, squiggle_bottom - 3, squiggle_start + 4, squiggle_bottom - 1);
}
squiggle_start += 4;
}
if (misspell_it->second > seg_end)
{
break;
}
++misspell_it;
}
text_rect.mLeft = (S32)(cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect));
seg_start = clipped_end + cur_segment->getStart();
@ -609,8 +756,7 @@ void LLTextBase::drawText()
S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::segment_vec_t* segments )
{
LLWString text(getWText());
S32 old_len = text.length(); // length() returns character length
S32 old_len = getLength(); // length() returns character length
S32 insert_len = wstr.length();
pos = getEditableIndex(pos, true);
@ -670,8 +816,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
}
}
text.insert(pos, wstr);
getViewModel()->setDisplay(text);
getViewModel()->getEditableDisplay().insert(pos, wstr);
if ( truncate() )
{
@ -686,7 +831,6 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
{
LLWString text(getWText());
segment_set_t::iterator seg_iter = getSegIterContaining(pos);
while(seg_iter != mSegments.end())
{
@ -732,8 +876,7 @@ S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
++seg_iter;
}
text.erase(pos, length);
getViewModel()->setDisplay(text);
getViewModel()->getEditableDisplay().erase(pos, length);
// recreate default segment in case we erased everything
createDefaultSegment();
@ -750,9 +893,7 @@ S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc)
{
return 0;
}
LLWString text(getWText());
text[pos] = wc;
getViewModel()->setDisplay(text);
getViewModel()->getEditableDisplay()[pos] = wc;
onValueChange(pos, pos + 1);
needsReflow(pos);
@ -1120,6 +1261,99 @@ void LLTextBase::deselect()
mIsSelecting = FALSE;
}
bool LLTextBase::getSpellCheck() const
{
return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck);
}
const std::string& LLTextBase::getSuggestion(U32 index) const
{
return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null;
}
U32 LLTextBase::getSuggestionCount() const
{
return mSuggestionList.size();
}
void LLTextBase::replaceWithSuggestion(U32 index)
{
for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
{
if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) )
{
deselect();
// Delete the misspelled word
removeStringNoUndo(it->first, it->second - it->first);
// Insert the suggestion in its place
LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]);
insertStringNoUndo(it->first, utf8str_to_wstring(mSuggestionList[index]));
setCursorPos(it->first + (S32)suggestion.length());
break;
}
}
mSpellCheckStart = mSpellCheckEnd = -1;
}
void LLTextBase::addToDictionary()
{
if (canAddToDictionary())
{
LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos));
}
}
bool LLTextBase::canAddToDictionary() const
{
return (getSpellCheck()) && (isMisspelledWord(mCursorPos));
}
void LLTextBase::addToIgnore()
{
if (canAddToIgnore())
{
LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos));
}
}
bool LLTextBase::canAddToIgnore() const
{
return (getSpellCheck()) && (isMisspelledWord(mCursorPos));
}
std::string LLTextBase::getMisspelledWord(U32 pos) const
{
for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
{
if ( (it->first <= pos) && (it->second >= pos) )
{
return wstring_to_utf8str(getWText().substr(it->first, it->second - it->first));
}
}
return LLStringUtil::null;
}
bool LLTextBase::isMisspelledWord(U32 pos) const
{
for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
{
if ( (it->first <= pos) && (it->second >= pos) )
{
return true;
}
}
return false;
}
void LLTextBase::onSpellCheckSettingsChange()
{
// Recheck the spelling on every change
mMisspellRanges.clear();
mSpellCheckStart = mSpellCheckEnd = -1;
}
// Sets the scrollbar from the cursor position
void LLTextBase::updateScrollFromCursor()
{
@ -1744,6 +1978,8 @@ static LLUIImagePtr image_from_icon_name(const std::string& icon_name)
}
}
static LLFastTimer::DeclareTimer FTM_PARSE_HTML("Parse HTML");
void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params)
{
LLStyle::Params style_params(input_params);
@ -1752,15 +1988,13 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
S32 part = (S32)LLTextParser::WHOLE;
if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358).
{
LLFastTimer _(FTM_PARSE_HTML);
S32 start=0,end=0;
LLUrlMatch match;
std::string text = new_text;
while ( LLUrlRegistry::instance().findUrl(text, match,
boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3)) )
{
LLTextUtil::processUrlMatch(&match,this);
start = match.getStart();
end = match.getEnd()+1;
@ -1796,6 +2030,8 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
}
}
LLTextUtil::processUrlMatch(&match,this);
// move on to the rest of the text after the Url
if (end < (S32)text.length())
{
@ -1819,8 +2055,11 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
}
}
static LLFastTimer::DeclareTimer FTM_APPEND_TEXT("Append Text");
void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
{
LLFastTimer _(FTM_APPEND_TEXT);
if (new_text.empty())
return;

View File

@ -30,6 +30,7 @@
#include "v4color.h"
#include "lleditmenuhandler.h"
#include "llspellcheckmenuhandler.h"
#include "llstyle.h"
#include "llkeywords.h"
#include "llpanel.h"
@ -236,7 +237,8 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
///
class LLTextBase
: public LLUICtrl,
protected LLEditMenuHandler
protected LLEditMenuHandler,
public LLSpellCheckMenuHandler
{
public:
friend class LLTextSegment;
@ -268,6 +270,7 @@ public:
border_visible,
track_end,
read_only,
spellcheck,
allow_scroll,
plain_text,
wrap,
@ -324,6 +327,24 @@ public:
void setParseHTML(bool parse_html) { mParseHTML = parse_html; }
//</FS:KC - expose ParseHTML setting>
// LLSpellCheckMenuHandler overrides
/*virtual*/ bool getSpellCheck() const;
/*virtual*/ const std::string& getSuggestion(U32 index) const;
/*virtual*/ U32 getSuggestionCount() const;
/*virtual*/ void replaceWithSuggestion(U32 index);
/*virtual*/ void addToDictionary();
/*virtual*/ bool canAddToDictionary() const;
/*virtual*/ void addToIgnore();
/*virtual*/ bool canAddToIgnore() const;
// Spell checking helper functions
std::string getMisspelledWord(U32 pos) const;
bool isMisspelledWord(U32 pos) const;
void onSpellCheckSettingsChange();
// used by LLTextSegment layout code
bool getWordWrap() { return mWordWrap; }
bool getUseEllipses() { return mUseEllipses; }
@ -565,6 +586,14 @@ protected:
S32 mSelectionEnd;
BOOL mIsSelecting; // Are we in the middle of a drag-select?
// spell checking
bool mSpellCheck;
S32 mSpellCheckStart;
S32 mSpellCheckEnd;
LLTimer mSpellCheckTimer;
std::list<std::pair<U32, U32> > mMisspellRanges;
std::vector<std::string> mSuggestionList;
// configuration
S32 mHPad; // padding on left of text

View File

@ -150,7 +150,7 @@ S32 LLTextBox::getTextPixelHeight()
LLSD LLTextBox::getValue() const
{
return LLSD(getText());
return getViewModel()->getValue();
}
BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text )

View File

@ -54,6 +54,7 @@
#include "llwindow.h"
#include "lltextparser.h"
#include "llscrollcontainer.h"
#include "llspellcheck.h"
#include "llpanel.h"
#include "llurlregistry.h"
#include "lltooltip.h"
@ -77,6 +78,7 @@ template class LLTextEditor* LLView::getChild<class LLTextEditor>(
const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32;
const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4;
const S32 SPACES_PER_TAB = 4;
const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on
///////////////////////////////////////////////////////////////////
@ -2024,7 +2026,38 @@ void LLTextEditor::showContextMenu(S32 x, S32 y)
S32 screen_x, screen_y;
localPointToScreen(x, y, &screen_x, &screen_y);
mContextMenu->show(screen_x, screen_y);
setCursorAtLocalPos(x, y, false);
if (hasSelection())
{
if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) )
{
deselect();
}
else
{
setCursorPos(llmax(mSelectionStart, mSelectionEnd));
}
}
bool use_spellcheck = getSpellCheck(), is_misspelled = false;
if (use_spellcheck)
{
mSuggestionList.clear();
// If the cursor is on a misspelled word, retrieve suggestions for it
std::string misspelled_word = getMisspelledWord(mCursorPos);
if ((is_misspelled = !misspelled_word.empty()) == true)
{
LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList);
}
}
mContextMenu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty()));
mContextMenu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled));
mContextMenu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled));
mContextMenu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled));
mContextMenu->show(screen_x, screen_y, this);
}
@ -2922,6 +2955,9 @@ void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& cal
void LLTextEditor::onKeyStroke()
{
mKeystrokeSignal(this);
mSpellCheckStart = mSpellCheckEnd = -1;
mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
//virtual

View File

@ -832,7 +832,11 @@ void gl_stippled_line_3d( const LLVector3& start, const LLVector3& end, const LL
gGL.flush();
glLineWidth(2.5f);
glLineStipple(2, 0x3333 << shift);
if (!LLGLSLShader::sNoFixedFunction)
{
glLineStipple(2, 0x3333 << shift);
}
gGL.begin(LLRender::LINES);
{

View File

@ -102,6 +102,7 @@ public:
// New functions
/// Get the stored value in string form
const LLWString& getDisplay() const { return mDisplay; }
LLWString& getEditableDisplay() { mDirty = true; mUpdateFromDisplay = true; return mDisplay; }
/**
* Set the display string directly (see LLTextEditor). What the user is

View File

@ -46,7 +46,7 @@ LLKeyStringTranslatorFunc* LLKeyboard::mStringTranslator = NULL; // Used for l10
// Class Implementation
//
LLKeyboard::LLKeyboard() : mCallbacks(NULL), mNumpadDistinct(ND_NUMLOCK_OFF)
LLKeyboard::LLKeyboard() : mCallbacks(NULL)
{
S32 i;

View File

@ -62,14 +62,6 @@ class LLWindowCallbacks;
class LLKeyboard
{
public:
typedef enum e_numpad_distinct
{
ND_NEVER,
ND_NUMLOCK_OFF,
ND_NUMLOCK_ON
} ENumpadDistinct;
public:
LLKeyboard();
virtual ~LLKeyboard();
@ -107,8 +99,6 @@ public:
static BOOL keyFromString(const std::string& str, KEY *key); // False on failure
static std::string stringFromKey(KEY key);
static std::string stringFromAccelerator( MASK accel_mask, KEY key );
e_numpad_distinct getNumpadDistinct() { return mNumpadDistinct; }
void setNumpadDistinct(e_numpad_distinct val) { mNumpadDistinct = val; }
void setCallbacks(LLWindowCallbacks *cbs) { mCallbacks = cbs; }
F32 getKeyElapsedTime( KEY key ); // Returns time in seconds since key was pressed.
@ -135,8 +125,6 @@ protected:
static LLKeyStringTranslatorFunc* mStringTranslator; // Used for l10n + PC/Mac/Linux accelerator labeling
e_numpad_distinct mNumpadDistinct;
EKeyboardInsertMode mInsertMode;
static std::map<KEY,std::string> sKeysToNames;

View File

@ -299,28 +299,11 @@ void LLKeyboardMacOSX::scanKeyboard()
BOOL LLKeyboardMacOSX::translateNumpadKey( const U16 os_key, KEY *translated_key )
{
if(mNumpadDistinct == ND_NUMLOCK_ON)
{
std::map<U16, KEY>::iterator iter= mTranslateNumpadMap.find(os_key);
if(iter != mTranslateNumpadMap.end())
{
*translated_key = iter->second;
return TRUE;
}
}
return translateKey(os_key, translated_key);
}
U16 LLKeyboardMacOSX::inverseTranslateNumpadKey(const KEY translated_key)
{
if(mNumpadDistinct == ND_NUMLOCK_ON)
{
std::map<KEY, U16>::iterator iter= mInvTranslateNumpadMap.find(translated_key);
if(iter != mInvTranslateNumpadMap.end())
{
return iter->second;
}
}
return inverseTranslateKey(translated_key);
}

View File

@ -312,29 +312,11 @@ void LLKeyboardSDL::scanKeyboard()
BOOL LLKeyboardSDL::translateNumpadKey( const U16 os_key, KEY *translated_key)
{
if(mNumpadDistinct == ND_NUMLOCK_ON)
{
std::map<U16, KEY>::iterator iter= mTranslateNumpadMap.find(os_key);
if(iter != mTranslateNumpadMap.end())
{
*translated_key = iter->second;
return TRUE;
}
}
BOOL success = translateKey(os_key, translated_key);
return success;
return translateKey(os_key, translated_key);
}
U16 LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key)
{
if(mNumpadDistinct == ND_NUMLOCK_ON)
{
std::map<KEY, U16>::iterator iter= mInvTranslateNumpadMap.find(translated_key);
if(iter != mInvTranslateNumpadMap.end())
{
return iter->second;
}
}
return inverseTranslateKey(translated_key);
}

View File

@ -302,69 +302,13 @@ void LLKeyboardWin32::scanKeyboard()
BOOL LLKeyboardWin32::translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key)
{
if(mNumpadDistinct == ND_NUMLOCK_ON)
{
std::map<U16, KEY>::iterator iter = mTranslateNumpadMap.find(os_key);
if (iter != mTranslateNumpadMap.end())
{
*translated_key = iter->second;
return TRUE;
}
}
BOOL success = translateKey(os_key, translated_key);
if(mNumpadDistinct != ND_NEVER) {
if(!success) return success;
if(mask & MASK_EXTENDED)
{
// this is where we'd create new keycodes for extended keys
// the set of extended keys includes the 'normal' arrow keys and
// the pgup/dn/insert/home/end/delete cluster above the arrow keys
// see http://windowssdk.msdn.microsoft.com/en-us/library/ms646280.aspx
// only process the return key if numlock is off
if(((mNumpadDistinct == ND_NUMLOCK_OFF &&
!(GetKeyState(VK_NUMLOCK) & 1))
|| mNumpadDistinct == ND_NUMLOCK_ON) &&
*translated_key == KEY_RETURN) {
*translated_key = KEY_PAD_RETURN;
}
}
else
{
// the non-extended keys, those are in the numpad
switch (*translated_key)
{
case KEY_LEFT:
*translated_key = KEY_PAD_LEFT; break;
case KEY_RIGHT:
*translated_key = KEY_PAD_RIGHT; break;
case KEY_UP:
*translated_key = KEY_PAD_UP; break;
case KEY_DOWN:
*translated_key = KEY_PAD_DOWN; break;
case KEY_HOME:
*translated_key = KEY_PAD_HOME; break;
case KEY_END:
*translated_key = KEY_PAD_END; break;
case KEY_PAGE_UP:
*translated_key = KEY_PAD_PGUP; break;
case KEY_PAGE_DOWN:
*translated_key = KEY_PAD_PGDN; break;
case KEY_INSERT:
*translated_key = KEY_PAD_INS; break;
case KEY_DELETE:
*translated_key = KEY_PAD_DEL; break;
}
}
}
return success;
return translateKey(os_key, translated_key);
}
U16 LLKeyboardWin32::inverseTranslateExtendedKey(const KEY translated_key)
{
// if numlock is on, then we need to translate KEY_PAD_FOO to the corresponding number pad number
if((mNumpadDistinct == ND_NUMLOCK_ON) && (GetKeyState(VK_NUMLOCK) & 1))
if(GetKeyState(VK_NUMLOCK) & 1)
{
std::map<KEY, U16>::iterator iter = mInvTranslateNumpadMap.find(translated_key);
if (iter != mInvTranslateNumpadMap.end())

View File

@ -368,6 +368,10 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
U32 fsaa_samples)
: LLWindow(callbacks, fullscreen, flags)
{
//MAINT-516 -- force a load of opengl32.dll just in case windows went sideways
LoadLibrary(L"opengl32.dll");
mFSAASamples = fsaa_samples;
mIconResource = gIconResource;
mOverrideAspectRatio = 0.f;

View File

@ -208,7 +208,8 @@ void LLControlVariable::setValue(const LLSD& new_value, bool saved_value)
}
LLSD storable_value = getComparableValue(new_value);
bool value_changed = llsd_compare(getValue(), storable_value) == FALSE;
LLSD original_value = getValue();
bool value_changed = llsd_compare(original_value, storable_value) == FALSE;
if(saved_value)
{
// If we're going to save this value, return to default but don't fire
@ -244,7 +245,7 @@ void LLControlVariable::setValue(const LLSD& new_value, bool saved_value)
if(value_changed)
{
mCommitSignal(this, storable_value);
firePropertyChanged(original_value);
mSanitySignal(this,isSane());
}
}
@ -257,13 +258,14 @@ void LLControlVariable::setDefaultValue(const LLSD& value)
// *NOTE: Default values are not saved, only read.
LLSD comparable_value = getComparableValue(value);
bool value_changed = (llsd_compare(getValue(), comparable_value) == FALSE);
LLSD original_value = getValue();
bool value_changed = (llsd_compare(original_value, comparable_value) == FALSE);
resetToDefault(false);
mValues[0] = comparable_value;
if(value_changed)
{
mSanitySignal(this,isSane());
firePropertyChanged();
firePropertyChanged(original_value);
}
}
@ -286,6 +288,8 @@ void LLControlVariable::resetToDefault(bool fire_signal)
{
//The first setting is always the default
//Pop to it and fire off the listener
LLSD originalValue = mValues.back();
while(mValues.size() > 1)
{
mValues.pop_back();
@ -293,7 +297,7 @@ void LLControlVariable::resetToDefault(bool fire_signal)
if(fire_signal)
{
firePropertyChanged();
firePropertyChanged(originalValue);
}
}

View File

@ -110,7 +110,7 @@ class LLControlVariable : public LLRefCount
public:
typedef boost::signals2::signal<bool(LLControlVariable* control, const LLSD&), boost_boolean_combiner> validate_signal_t;
typedef boost::signals2::signal<void(LLControlVariable* control, const LLSD&)> commit_signal_t;
typedef boost::signals2::signal<void(LLControlVariable* control, const LLSD&, const LLSD&)> commit_signal_t;
typedef boost::signals2::signal<void(LLControlVariable* control, const LLSD&)> sanity_signal_t;
private:
@ -172,11 +172,11 @@ public:
void setHiddenFromSettingsEditor(bool hide);
void setComment(const std::string& comment);
void firePropertyChanged()
{
mCommitSignal(this, mValues.back());
}
private:
void firePropertyChanged(const LLSD &pPreviousValue)
{
mCommitSignal(this, mValues.back(), pPreviousValue);
}
LLSD getComparableValue(const LLSD& value);
bool llsd_compare(const LLSD& a, const LLSD & b);
};

View File

@ -13,6 +13,7 @@ include(EXPAT)
include(FMOD)
include(OPENAL)
include(FindOpenGL)
include(Hunspell)
include(JsonCpp)
include(LLAudio)
include(LLCharacter)
@ -70,8 +71,9 @@ include_directories(
${LSCRIPT_INCLUDE_DIRS}
${LSCRIPT_INCLUDE_DIRS}/lscript_compile
${LLLOGIN_INCLUDE_DIRS}
${LIBS_PREBUILT_DIR}/include/collada
${UPDATER_INCLUDE_DIRS}
${LIBS_PREBUILT_DIR}/include/collada
${LIBS_PREBUILD_DIR}/include/hunspell
${OPENAL_LIB_INCLUDE_DIRS}
${LIBS_PREBUILT_DIR}/include/collada/1.4
${GROWL_INCLUDE_DIRS}
@ -132,6 +134,7 @@ set(viewer_SOURCE_FILES
llassetuploadresponders.cpp
llattachmentsmgr.cpp
llaudiosourcevo.cpp
llautoreplace.cpp
llavataractions.cpp
llavatariconctrl.cpp
llavatarlist.cpp
@ -202,6 +205,7 @@ set(viewer_SOURCE_FILES
llfloaterabout.cpp
llfloaterbvhpreview.cpp
llfloaterauction.cpp
llfloaterautoreplacesettings.cpp
llfloateravatar.cpp
llfloateravatarpicker.cpp
llfloateravatartextures.cpp
@ -245,7 +249,6 @@ set(viewer_SOURCE_FILES
llfloatermemleak.cpp
llfloatermodelpreview.cpp
llfloatermodeluploadbase.cpp
llfloatermodelwizard.cpp
llfloaternamedesc.cpp
llfloaternearbychat.cpp
llfloaternotificationsconsole.cpp
@ -269,6 +272,7 @@ set(viewer_SOURCE_FILES
llfloatersidepanelcontainer.cpp
llfloatersnapshot.cpp
llfloatersounddevices.cpp
llfloaterspellchecksettings.cpp
llfloatertelehub.cpp
llfloatertestinspectors.cpp
llfloatertestlistview.cpp
@ -761,6 +765,7 @@ set(viewer_HEADER_FILES
llassetuploadresponders.h
llattachmentsmgr.h
llaudiosourcevo.h
llautoreplace.h
llavataractions.h
llavatariconctrl.h
llavatarlist.h
@ -831,6 +836,7 @@ set(viewer_HEADER_FILES
llfloaterabout.h
llfloaterbvhpreview.h
llfloaterauction.h
llfloaterautoreplacesettings.h
llfloateravatar.h
llfloateravatarpicker.h
llfloateravatartextures.h
@ -874,7 +880,6 @@ set(viewer_HEADER_FILES
llfloatermemleak.h
llfloatermodelpreview.h
llfloatermodeluploadbase.h
llfloatermodelwizard.h
llfloaternamedesc.h
llfloaternearbychat.h
llfloaternotificationsconsole.h
@ -898,6 +903,7 @@ set(viewer_HEADER_FILES
llfloatersidepanelcontainer.h
llfloatersnapshot.h
llfloatersounddevices.h
llfloaterspellchecksettings.h
llfloatertelehub.h
llfloatertestinspectors.h
llfloatertestlistview.h
@ -1749,6 +1755,9 @@ if (WINDOWS)
${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/msvcp100.dll
${SHARED_LIB_STAGING_DIR}/Debug/msvcr100d.dll
${SHARED_LIB_STAGING_DIR}/Debug/msvcp100d.dll
${SHARED_LIB_STAGING_DIR}/Release/libhunspell.dll
${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll
${SHARED_LIB_STAGING_DIR}/Debug/libhunspell.dll
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/SLVoice.exe
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/alut.dll
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/wrap_oal.dll
@ -1931,6 +1940,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
${LLMATH_LIBRARIES}
${LLCOMMON_LIBRARIES}
${NDOF_LIBRARY}
${HUNSPELL_LIBRARY}
${viewer_LIBRARIES}
${BOOST_PROGRAM_OPTIONS_LIBRARY}
${BOOST_REGEX_LIBRARY}

File diff suppressed because it is too large Load Diff

View File

@ -1753,7 +1753,7 @@
<key>Value</key>
<integer>1</integer>
</map>
<key>ApplyTextureImmediately</key>
<key>TextureLivePreview</key>
<map>
<key>Comment</key>
<string>Preview selections in texture picker immediately</string>
@ -1985,6 +1985,17 @@
<key>Value</key>
<integer>1</integer>
</map>
<key>AutoReplace</key>
<map>
<key>Comment</key>
<string>Replaces keywords with a configured word or phrase</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>AutoAcceptNewInventory</key>
<map>
<key>Comment</key>
@ -6032,6 +6043,17 @@
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>InventoryInboxToggleState</key>
<map>
<key>Comment</key>
<string>Stores the open/closed state of inventory Received items panel</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>InventoryLinking</key>
<map>
@ -8367,17 +8389,6 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>0</integer>
</map>
<key>NumpadControl</key>
<map>
<key>Comment</key>
<string>How numpad keys control your avatar. 0 = Like the normal arrow keys, 1 = Numpad moves avatar when numlock is off, 2 = Numpad moves avatar regardless of numlock (use this if you have no numlock)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>ObjectCacheEnabled</key>
<map>
<key>Comment</key>
@ -9329,7 +9340,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>WebContentWindowLimit</key>
<map>
<key>Comment</key>
<string>Maximum number of web brower windows that can be open at once in the Web content floater (0 for no limit)</string>
<string>Maximum number of web browser windows that can be open at once in the Web content floater (0 for no limit)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -10148,6 +10159,18 @@ Change of this parameter will affect the layout of buttons in notification toast
<real>0</real>
</map>
<key>RenderDepthPrePass</key>
<map>
<key>Comment</key>
<string>EXPERIMENTAL: Prime the depth buffer with simple prim geometry before rendering with textures.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderDepthOfField</key>
<map>
<key>Comment</key>
@ -11238,7 +11261,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>RenderUseVAO</key>
<map>
<key>Comment</key>
<string>Use GL Vertex Array Objects</string>
<string>[EXPERIMENTAL] Use GL Vertex Array Objects</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -11246,7 +11269,19 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderVBOMappingDisable</key>
<key>RenderUseTransformFeedback</key>
<map>
<key>Comment</key>
<string>[EXPERIMENTAL] Use transform feedback shaders for LoD updates</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderVBOMappingDisable</key>
<map>
<key>Comment</key>
<string>Disable VBO glMapBufferARB</string>
@ -13151,7 +13186,8 @@ Change of this parameter will affect the layout of buttons in notification toast
<string>F32</string>
<key>Value</key>
<real>0.1</real>
</map> <key>ToolTipFadeTime</key>
</map>
<key>ToolTipFadeTime</key>
<map>
<key>Comment</key>
<string>Seconds over which tooltip fades away</string>
@ -14703,6 +14739,17 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>SanityComment</key>
<string>Disabling this option might lead to severely degraded viewer performance and increased rezzing time.</string>
</map>
<key>RenderSynchronousOcclusion</key>
<map>
<key>Comment</key>
<string>Don't let occlusion queries get more than one frame behind (block until they complete).</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>RenderDelayVBUpdate</key>
<map>
<key>Comment</key>
@ -14736,6 +14783,28 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<real>10.0</real>
</map>
<key>SpellCheck</key>
<map>
<key>Comment</key>
<string>Enable spellchecking on line and text editors</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>SpellCheckDictionary</key>
<map>
<key>Comment</key>
<string>Current primary and secondary dictionaries used for spell checking</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string>English (United States),Second Life Glossary</string>
</map>
<key>UseNewWalkRun</key>
<map>
<key>Comment</key>

View File

@ -31,6 +31,8 @@ out vec4 frag_color;
#define frag_color gl_FragColor
#endif
uniform float minimum_alpha;
uniform sampler2DRect depthMap;
uniform sampler2D diffuseMap;
@ -70,9 +72,15 @@ void main()
vec4 diff= texture2D(diffuseMap,vary_texcoord0.xy);
if (diff.a < minimum_alpha)
{
discard;
}
vec4 col = vec4(vary_ambient + vary_directional.rgb, 1.0);
vec4 color = diff * col;
color.rgb = atmosLighting(color.rgb);
color.rgb = scaleSoftClip(color.rgb);

View File

@ -55,8 +55,6 @@ uniform float far_clip;
uniform vec3 proj_origin; //origin of projection to be used for angular attenuation
uniform float sun_wash;
uniform int proj_shadow_idx;
uniform float shadow_fade;
uniform vec3 center;
uniform vec3 color;
@ -143,7 +141,8 @@ void main()
discard;
}
vec3 norm = texture2DRect(normalMap, frag.xy).xyz*2.0-1.0;
vec3 norm = texture2DRect(normalMap, frag.xy).xyz;
norm = vec3((norm.xy-0.5)*2.0, norm.z);
norm = normalize(norm);
float l_dist = -dot(lv, proj_n);

View File

@ -42,12 +42,13 @@ uniform sampler2DRect depthMap;
uniform vec3 env_mat[3];
uniform float sun_wash;
uniform vec3 center;
uniform vec3 color;
uniform float falloff;
uniform float size;
VARYING vec4 vary_fragcoord;
VARYING vec3 trans_center;
uniform vec2 screen_res;
uniform mat4 inv_proj;
@ -74,7 +75,7 @@ void main()
frag.xy *= screen_res;
vec3 pos = getPosition(frag.xy).xyz;
vec3 lv = center.xyz-pos;
vec3 lv = trans_center.xyz-pos;
float dist2 = dot(lv,lv);
dist2 /= size;
if (dist2 > 1.0)

View File

@ -24,16 +24,22 @@
*/
uniform mat4 modelview_projection_matrix;
uniform mat4 modelview_matrix;
ATTRIBUTE vec3 position;
uniform vec3 center;
uniform float size;
VARYING vec4 vary_fragcoord;
VARYING vec3 trans_center;
void main()
{
//transform vertex
vec4 pos = modelview_projection_matrix * vec4(position.xyz, 1.0);
vec3 p = position*sqrt(size)+center;
vec4 pos = modelview_projection_matrix * vec4(p.xyz, 1.0);
vary_fragcoord = pos;
trans_center = (modelview_matrix*vec4(center.xyz, 1.0)).xyz;
gl_Position = pos;
}

View File

@ -0,0 +1,44 @@
/**
* @file shadowCubeV.glsl
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
uniform mat4 modelview_projection_matrix;
ATTRIBUTE vec3 position;
VARYING vec4 post_pos;
uniform vec3 box_center;
uniform vec3 box_size;
void main()
{
//transform vertex
vec3 p = position*box_size+box_center;
vec4 pos = modelview_projection_matrix*vec4(p.xyz, 1.0);
post_pos = pos;
gl_Position = vec4(pos.x, pos.y, pos.w*0.5, pos.w);
}

View File

@ -24,18 +24,21 @@
*/
#extension GL_ARB_texture_rectangle : enable
#ifdef DEFINE_GL_FRAGCOLOR
out vec4 frag_color;
#else
#define frag_color gl_FragColor
#endif
//class 1 -- no shadows
#extension GL_ARB_texture_rectangle : enable
uniform sampler2DRect diffuseRect;
uniform sampler2DRect specularRect;
uniform sampler2DRect depthMap;
uniform sampler2DRect normalMap;
uniform samplerCube environmentMap;
uniform sampler2D noiseMap;
uniform sampler2D projectionMap;
@ -46,6 +49,7 @@ uniform vec3 proj_n;
uniform float proj_focus; //distance from plane to begin blurring
uniform float proj_lod; //(number of mips in proj map)
uniform float proj_range; //range between near clip and far clip plane of projection
uniform float proj_ambient_lod;
uniform float proj_ambiance;
uniform float near_clip;
uniform float far_clip;
@ -53,19 +57,66 @@ uniform float far_clip;
uniform vec3 proj_origin; //origin of projection to be used for angular attenuation
uniform float sun_wash;
uniform vec3 center;
uniform vec3 color;
uniform float falloff;
uniform float size;
VARYING vec4 vary_fragcoord;
VARYING vec3 trans_center;
uniform vec2 screen_res;
uniform mat4 inv_proj;
vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod)
{
vec4 ret = texture2DLod(projectionMap, tc, lod);
vec2 dist = tc-vec2(0.5);
float det = max(1.0-lod/(proj_lod*0.5), 0.0);
float d = dot(dist,dist);
ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0);
return ret;
}
vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod)
{
vec4 ret = texture2DLod(projectionMap, tc, lod);
vec2 dist = vec2(0.5) - abs(tc-vec2(0.5));
float det = min(lod/(proj_lod*0.5), 1.0);
float d = min(dist.x, dist.y);
float edge = 0.25*det;
ret *= clamp(d/edge, 0.0, 1.0);
return ret;
}
vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod)
{
vec4 ret = texture2DLod(projectionMap, tc, lod);
vec2 dist = tc-vec2(0.5);
float d = dot(dist,dist);
ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0);
return ret;
}
vec4 getPosition(vec2 pos_screen)
{
float depth = texture2DRect(depthMap, pos_screen.xy).a;
float depth = texture2DRect(depthMap, pos_screen.xy).r;
vec2 sc = pos_screen.xy*2.0;
sc /= screen_res;
sc -= vec2(1.0,1.0);
@ -84,16 +135,16 @@ void main()
frag.xy *= screen_res;
vec3 pos = getPosition(frag.xy).xyz;
vec3 lv = center.xyz-pos.xyz;
vec3 lv = trans_center.xyz-pos.xyz;
float dist2 = dot(lv,lv);
dist2 /= size;
if (dist2 > 1.0)
{
discard;
}
vec3 norm = texture2DRect(normalMap, frag.xy).xyz;
norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
norm = vec3((norm.xy-0.5)*2.0, norm.z);
norm = normalize(norm);
float l_dist = -dot(lv, proj_n);
@ -107,7 +158,11 @@ void main()
proj_tc.xyz /= proj_tc.w;
float fa = falloff+1.0;
float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0);
if (dist_atten <= 0.0)
{
discard;
}
lv = proj_origin-pos.xyz;
lv = normalize(lv);
@ -125,32 +180,32 @@ void main()
proj_tc.y > 0.0)
{
float lit = 0.0;
float amb_da = proj_ambiance;
if (da > 0.0)
{
float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0);
float lod = diff * proj_lod;
vec4 plcol = texture2DLod(projectionMap, proj_tc.xy, lod);
vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod);
vec3 lcol = color.rgb * plcol.rgb * plcol.a;
lit = da * dist_atten * noise;
col = lcol*lit*diff_tex;
amb_da += (da*0.5)*proj_ambiance;
}
float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0);
float lod = diff * proj_lod;
vec4 amb_plcol = texture2DLod(projectionMap, proj_tc.xy, lod);
//float amb_da = mix(proj_ambiance, proj_ambiance*max(-da, 0.0), max(da, 0.0));
float amb_da = proj_ambiance;
//float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0);
vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod);
amb_da += (da*da*0.5+0.5)*proj_ambiance;
amb_da *= dist_atten * noise;
amb_da = min(amb_da, 1.0-lit);
col += amb_da*color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a;
}
@ -168,18 +223,22 @@ void main()
{
vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds;
vec3 stc = (proj_mat * vec4(pfinal.xyz, 1.0)).xyz;
vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0));
if (stc.z > 0.0)
{
stc.xy /= stc.z+proj_near;
stc.xy /= stc.w;
float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0);
stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5);
if (stc.x < 1.0 &&
stc.y < 1.0 &&
stc.x > 0.0 &&
stc.y > 0.0)
{
vec4 scol = texture2DLod(projectionMap, stc.xy, proj_lod-spec.a*proj_lod);
vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod);
col += dist_atten*scol.rgb*color.rgb*scol.a*spec.rgb;
}
}

View File

@ -0,0 +1,46 @@
/**
* @file debugF.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifdef DEFINE_GL_FRAGCOLOR
out vec4 frag_color;
#else
#define frag_color gl_FragColor
#endif
uniform vec4 color;
uniform vec4 clip_plane;
VARYING vec3 vary_position;
void main()
{
if (dot(vary_position,clip_plane.xyz)+clip_plane.w < 0.0)
{
discard;
}
frag_color = color;
}

View File

@ -0,0 +1,38 @@
/**
* @file debugV.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
uniform mat4 modelview_projection_matrix;
uniform mat4 modelview_matrix;
ATTRIBUTE vec3 position;
VARYING vec3 vary_position;
void main()
{
vary_position = (modelview_matrix*vec4(position.xyz,1.0)).xyz;
gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
}

View File

@ -0,0 +1,38 @@
/**
* @file occlusionCubeV.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
uniform mat4 modelview_projection_matrix;
ATTRIBUTE vec3 position;
uniform vec3 box_center;
uniform vec3 box_size;
void main()
{
vec3 p = position*box_size+box_center;
gl_Position = modelview_projection_matrix * vec4(p.xyz, 1.0);
}

View File

@ -23,9 +23,9 @@
* $/LicenseInfo$
*/
ATTRIBUTE ivec4 texture_index;
ATTRIBUTE int texture_index;
VARYING_FLAT ivec4 vary_texture_index;
VARYING_FLAT int vary_texture_index;
void passTextureIndex()
{

View File

@ -0,0 +1,36 @@
/**
* @file binormalV.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
uniform mat3 normal_matrix;
ATTRIBUTE vec3 binormal;
VARYING vec4 binormal_out;
void main()
{
binormal_out = vec4(normal_matrix * binormal, 0.0);
}

View File

@ -0,0 +1,36 @@
/**
* @file colorV.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
uniform int color_in;
ATTRIBUTE vec3 position;
VARYING int color_out;
void main()
{
color_out = color_in;
}

View File

@ -0,0 +1,36 @@
/**
* @file normalV.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
uniform mat3 normal_matrix;
ATTRIBUTE vec3 normal;
VARYING vec4 normal_out;
void main()
{
normal_out = vec4(normalize(normal_matrix * normal), 0.0);
}

View File

@ -0,0 +1,40 @@
/**
* @file positionV.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
uniform mat4 modelview_matrix;
uniform int texture_index_in;
ATTRIBUTE vec3 position;
VARYING vec3 position_out;
VARYING int texture_index_out;
void main()
{
texture_index_out = texture_index_in;
position_out = (modelview_matrix*vec4(position, 1.0)).xyz;
}

View File

@ -0,0 +1,35 @@
/**
* @file texcoordV.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
ATTRIBUTE vec2 texcoord0;
VARYING vec2 texcoord_out;
void main()
{
texcoord_out = texcoord0;
}

View File

@ -31,6 +31,8 @@ out vec4 frag_color;
#define frag_color gl_FragColor
#endif
uniform float minimum_alpha;
uniform sampler2DRectShadow shadowMap0;
uniform sampler2DRectShadow shadowMap1;
uniform sampler2DRectShadow shadowMap2;
@ -97,6 +99,13 @@ void main()
float shadow = 0.0;
vec4 pos = vec4(vary_position, 1.0);
vec4 diff = texture2D(diffuseMap,vary_texcoord0.xy);
if (diff.a < minimum_alpha)
{
discard;
}
vec4 spos = pos;
if (spos.z > -shadow_clip.w)
@ -164,8 +173,6 @@ void main()
shadow = 1.0;
}
vec4 diff = texture2D(diffuseMap,vary_texcoord0.xy);
vec4 col = vec4(vary_ambient + vary_directional.rgb*shadow, 1.0);
vec4 color = diff * col;

View File

@ -31,8 +31,6 @@ out vec4 frag_color;
#define frag_color gl_FragColor
#endif
VARYING vec4 vertex_color;
uniform sampler2DRect diffuseRect;
uniform sampler2DRect specularRect;
uniform sampler2DRect depthMap;
@ -49,6 +47,7 @@ uniform vec3 proj_n;
uniform float proj_focus; //distance from plane to begin blurring
uniform float proj_lod; //(number of mips in proj map)
uniform float proj_range; //range between near clip and far clip plane of projection
uniform float proj_ambient_lod;
uniform float proj_ambiance;
uniform float near_clip;
uniform float far_clip;
@ -58,16 +57,65 @@ uniform float sun_wash;
uniform int proj_shadow_idx;
uniform float shadow_fade;
VARYING vec4 vary_light;
uniform float size;
uniform vec3 color;
uniform float falloff;
VARYING vec3 trans_center;
VARYING vec4 vary_fragcoord;
uniform vec2 screen_res;
uniform mat4 inv_proj;
vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod)
{
vec4 ret = texture2DLod(projectionMap, tc, lod);
vec2 dist = tc-vec2(0.5);
float det = max(1.0-lod/(proj_lod*0.5), 0.0);
float d = dot(dist,dist);
ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0);
return ret;
}
vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod)
{
vec4 ret = texture2DLod(projectionMap, tc, lod);
vec2 dist = vec2(0.5) - abs(tc-vec2(0.5));
float det = min(lod/(proj_lod*0.5), 1.0);
float d = min(dist.x, dist.y);
float edge = 0.25*det;
ret *= clamp(d/edge, 0.0, 1.0);
return ret;
}
vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod)
{
vec4 ret = texture2DLod(projectionMap, tc, lod);
vec2 dist = tc-vec2(0.5);
float d = dot(dist,dist);
ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0);
return ret;
}
vec4 getPosition(vec2 pos_screen)
{
float depth = texture2DRect(depthMap, pos_screen.xy).a;
float depth = texture2DRect(depthMap, pos_screen.xy).r;
vec2 sc = pos_screen.xy*2.0;
sc /= screen_res;
sc -= vec2(1.0,1.0);
@ -85,6 +133,15 @@ void main()
frag.xyz = frag.xyz*0.5+0.5;
frag.xy *= screen_res;
vec3 pos = getPosition(frag.xy).xyz;
vec3 lv = trans_center.xyz-pos.xyz;
float dist2 = dot(lv,lv);
dist2 /= size;
if (dist2 > 1.0)
{
discard;
}
float shadow = 1.0;
if (proj_shadow_idx >= 0)
@ -96,15 +153,6 @@ void main()
shadow = min(sh[proj_shadow_idx]+shadow_fade, 1.0);
}
vec3 pos = getPosition(frag.xy).xyz;
vec3 lv = vary_light.xyz-pos.xyz;
float dist2 = dot(lv,lv);
dist2 /= vary_light.w;
if (dist2 > 1.0)
{
discard;
}
vec3 norm = texture2DRect(normalMap, frag.xy).xyz;
norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
@ -119,8 +167,12 @@ void main()
proj_tc.xyz /= proj_tc.w;
float fa = vertex_color.a+1.0;
float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
float fa = falloff+1.0;
float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0);
if (dist_atten <= 0.0)
{
discard;
}
lv = proj_origin-pos.xyz;
lv = normalize(lv);
@ -138,37 +190,33 @@ void main()
proj_tc.y > 0.0)
{
float lit = 0.0;
float amb_da = proj_ambiance;
if (da > 0.0)
{
float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0);
float lod = diff * proj_lod;
vec4 plcol = texture2DLod(projectionMap, proj_tc.xy, lod);
vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod);
vec3 lcol = vertex_color.rgb * plcol.rgb * plcol.a;
vec3 lcol = color.rgb * plcol.rgb * plcol.a;
lit = da * dist_atten * noise;
col = lcol*lit*diff_tex*shadow;
}
float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0);
float lod = diff * proj_lod;
vec4 amb_plcol = texture2DLod(projectionMap, proj_tc.xy, lod);
//float amb_da = mix(proj_ambiance, proj_ambiance*max(-da, 0.0), max(da, 0.0));
float amb_da = proj_ambiance;
if (da > 0.0)
{
amb_da += (da*0.5)*(1.0-shadow)*proj_ambiance;
}
//float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0);
vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod);
amb_da += (da*da*0.5+0.5)*proj_ambiance;
amb_da *= dist_atten * noise;
amb_da = min(amb_da, 1.0-lit);
col += amb_da*vertex_color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a;
col += amb_da*color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a;
}
@ -185,19 +233,23 @@ void main()
{
vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds;
vec3 stc = (proj_mat * vec4(pfinal.xyz, 1.0)).xyz;
vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0));
if (stc.z > 0.0)
{
stc.xy /= stc.z+proj_near;
stc.xy /= stc.w;
float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0);
stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5);
if (stc.x < 1.0 &&
stc.y < 1.0 &&
stc.x > 0.0 &&
stc.y > 0.0)
{
vec4 scol = texture2DLod(projectionMap, stc.xy, proj_lod-spec.a*proj_lod);
col += dist_atten*scol.rgb*vertex_color.rgb*scol.a*spec.rgb*shadow;
vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod);
col += dist_atten*scol.rgb*color.rgb*scol.a*spec.rgb*shadow;
}
}
}

View File

@ -524,6 +524,7 @@ Disregard128DefaultDrawDistance 1 0
list ATIOldDriver
RenderAvatarVP 0 0
RenderAvatarCloth 0 0
RenderVBOEnable 1 0
// ATI cards generally perform better when not using VBOs for streaming data

View File

@ -521,6 +521,7 @@ Disregard128DefaultDrawDistance 1 0
list ATIOldDriver
RenderAvatarVP 0 0
RenderAvatarCloth 0 0
RenderVBOEnable 1 0
// ATI cards generally perform better when not using VBOs for streaming data

View File

@ -86,6 +86,7 @@
#include "llviewermenu.h"
#include "llviewerobjectlist.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llviewerwindow.h"
#include "llvoavatarself.h"
@ -136,6 +137,105 @@ const F32 MAX_FIDGET_TIME = 20.f; // seconds
// The agent instance.
LLAgent gAgent;
class LLTeleportRequest
{
public:
enum EStatus
{
kPending,
kStarted,
kFailed,
kRestartPending
};
LLTeleportRequest();
virtual ~LLTeleportRequest();
EStatus getStatus() const {return mStatus;};
void setStatus(EStatus pStatus) {mStatus = pStatus;};
virtual bool canRestartTeleport();
virtual void startTeleport() = 0;
virtual void restartTeleport();
protected:
private:
EStatus mStatus;
};
class LLTeleportRequestViaLandmark : public LLTeleportRequest
{
public:
LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId);
virtual ~LLTeleportRequestViaLandmark();
virtual bool canRestartTeleport();
virtual void startTeleport();
virtual void restartTeleport();
protected:
inline const LLUUID &getLandmarkId() const {return mLandmarkId;};
private:
LLUUID mLandmarkId;
};
class LLTeleportRequestViaLure : public LLTeleportRequestViaLandmark
{
public:
LLTeleportRequestViaLure(const LLUUID &pLureId, BOOL pIsLureGodLike);
virtual ~LLTeleportRequestViaLure();
virtual bool canRestartTeleport();
virtual void startTeleport();
protected:
inline BOOL isLureGodLike() const {return mIsLureGodLike;};
private:
BOOL mIsLureGodLike;
};
class LLTeleportRequestViaLocation : public LLTeleportRequest
{
public:
LLTeleportRequestViaLocation(const LLVector3d &pPosGlobal);
virtual ~LLTeleportRequestViaLocation();
virtual bool canRestartTeleport();
virtual void startTeleport();
virtual void restartTeleport();
protected:
inline const LLVector3d &getPosGlobal() const {return mPosGlobal;};
private:
LLVector3d mPosGlobal;
};
class LLTeleportRequestViaLocationLookAt : public LLTeleportRequestViaLocation
{
public:
LLTeleportRequestViaLocationLookAt(const LLVector3d &pPosGlobal);
virtual ~LLTeleportRequestViaLocationLookAt();
virtual bool canRestartTeleport();
virtual void startTeleport();
virtual void restartTeleport();
protected:
private:
};
//--------------------------------------------------------------------
// Statics
//
@ -277,6 +377,17 @@ LLAgent::LLAgent() :
mAgentAccess(new LLAgentAccess(gSavedSettings)),
mCanEditParcel(false),
mTeleportSourceSLURL(new LLSLURL),
mTeleportRequest(),
mTeleportFinishedSlot(),
mTeleportFailedSlot(),
mIsMaturityRatingChangingDuringTeleport(false),
mMaturityRatingChange(0U),
mIsDoSendMaturityPreferenceToServer(false),
mMaturityPreferenceRequestId(0U),
mMaturityPreferenceResponseId(0U),
mMaturityPreferenceNumRetries(0U),
mLastKnownRequestMaturity(SIM_ACCESS_MIN),
mLastKnownResponseMaturity(SIM_ACCESS_MIN),
mTeleportState( TELEPORT_NONE ),
mRegionp(NULL),
@ -388,6 +499,9 @@ void LLAgent::init()
gSavedSettings.getControl("PreferredMaturity")->getValidateSignal()->connect(boost::bind(&LLAgent::validateMaturity, this, _2));
gSavedSettings.getControl("PreferredMaturity")->getSignal()->connect(boost::bind(&LLAgent::handleMaturity, this, _2));
mLastKnownResponseMaturity = static_cast<U8>(gSavedSettings.getU32("PreferredMaturity"));
mLastKnownRequestMaturity = mLastKnownResponseMaturity;
mIsDoSendMaturityPreferenceToServer = true;
ignorePrejump = gSavedSettings.getBOOL("FSIgnoreFinishAnimation");
gSavedSettings.getControl("FSIgnoreFinishAnimation")->getSignal()->connect(boost::bind(&LLAgent::updateIgnorePrejump, this, _2));
PhoenixForceFly = gSavedSettings.getBOOL("FSAlwaysFly");
@ -397,6 +511,15 @@ void LLAgent::init()
LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(boost::bind(&LLAgent::parcelChangedCallback));
if (!mTeleportFinishedSlot.connected())
{
mTeleportFinishedSlot = LLViewerParcelMgr::getInstance()->setTeleportFinishedCallback(boost::bind(&LLAgent::handleTeleportFinished, this));
}
if (!mTeleportFailedSlot.connected())
{
mTeleportFailedSlot = LLViewerParcelMgr::getInstance()->setTeleportFailedCallback(boost::bind(&LLAgent::handleTeleportFailed, this));
}
mInitialized = TRUE;
}
@ -406,6 +529,14 @@ void LLAgent::init()
void LLAgent::cleanup()
{
mRegionp = NULL;
if (mTeleportFinishedSlot.connected())
{
mTeleportFinishedSlot.disconnect();
}
if (mTeleportFailedSlot.connected())
{
mTeleportFailedSlot.disconnect();
}
}
//-----------------------------------------------------------------------------
@ -2656,49 +2787,278 @@ bool LLAgent::isAdult() const
return mAgentAccess->isAdult();
}
void LLAgent::setTeen(bool teen)
{
mAgentAccess->setTeen(teen);
}
//static
int LLAgent::convertTextToMaturity(char text)
{
return LLAgentAccess::convertTextToMaturity(text);
}
bool LLAgent::sendMaturityPreferenceToServer(int preferredMaturity)
class LLMaturityPreferencesResponder : public LLHTTPClient::Responder
{
if (!getRegion())
return false;
// Update agent access preference on the server
std::string url = getRegion()->getCapability("UpdateAgentInformation");
if (!url.empty())
public:
LLMaturityPreferencesResponder(LLAgent *pAgent, U8 pPreferredMaturity, U8 pPreviousMaturity);
virtual ~LLMaturityPreferencesResponder();
virtual void result(const LLSD &pContent);
virtual void error(U32 pStatus, const std::string& pReason);
protected:
private:
U8 parseMaturityFromServerResponse(const LLSD &pContent);
LLAgent *mAgent;
U8 mPreferredMaturity;
U8 mPreviousMaturity;
};
LLMaturityPreferencesResponder::LLMaturityPreferencesResponder(LLAgent *pAgent, U8 pPreferredMaturity, U8 pPreviousMaturity)
: LLHTTPClient::Responder(),
mAgent(pAgent),
mPreferredMaturity(pPreferredMaturity),
mPreviousMaturity(pPreviousMaturity)
{
}
LLMaturityPreferencesResponder::~LLMaturityPreferencesResponder()
{
}
void LLMaturityPreferencesResponder::result(const LLSD &pContent)
{
U8 actualMaturity = parseMaturityFromServerResponse(pContent);
if (actualMaturity != mPreferredMaturity)
{
// Set new access preference
LLSD access_prefs = LLSD::emptyMap();
if (preferredMaturity == SIM_ACCESS_PG)
{
access_prefs["max"] = "PG";
}
else if (preferredMaturity == SIM_ACCESS_MATURE)
{
access_prefs["max"] = "M";
}
if (preferredMaturity == SIM_ACCESS_ADULT)
{
access_prefs["max"] = "A";
}
LLSD body = LLSD::emptyMap();
body["access_prefs"] = access_prefs;
llinfos << "Sending access prefs update to " << (access_prefs["max"].asString()) << " via capability to: "
<< url << llendl;
LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); // Ignore response
return true;
llwarns << "while attempting to change maturity preference from '" << LLViewerRegion::accessToString(mPreviousMaturity)
<< "' to '" << LLViewerRegion::accessToString(mPreferredMaturity) << "', the server responded with '"
<< LLViewerRegion::accessToString(actualMaturity) << "' [value:" << static_cast<U32>(actualMaturity) << ", llsd:"
<< pContent << "]" << llendl;
}
mAgent->handlePreferredMaturityResult(actualMaturity);
}
void LLMaturityPreferencesResponder::error(U32 pStatus, const std::string& pReason)
{
llwarns << "while attempting to change maturity preference from '" << LLViewerRegion::accessToString(mPreviousMaturity)
<< "' to '" << LLViewerRegion::accessToString(mPreferredMaturity) << "', we got an error because '"
<< pReason << "' [status:" << pStatus << "]" << llendl;
mAgent->handlePreferredMaturityError();
}
U8 LLMaturityPreferencesResponder::parseMaturityFromServerResponse(const LLSD &pContent)
{
// stinson 05/24/2012 Pathfinding regions have re-defined the response behavior. In the old server code,
// if you attempted to change the preferred maturity to the same value, the response content would be an
// undefined LLSD block. In the new server code with pathfinding, the response content should always be
// defined. Thus, the check for isUndefined() can be replaced with an assert after pathfinding is merged
// into server trunk and fully deployed.
U8 maturity = SIM_ACCESS_MIN;
if (pContent.isUndefined())
{
maturity = mPreferredMaturity;
}
else
{
llassert(!pContent.isUndefined());
llassert(pContent.isMap());
if (!pContent.isUndefined() && pContent.isMap())
{
// stinson 05/24/2012 Pathfinding regions have re-defined the response syntax. The if statement catches
// the new syntax, and the else statement catches the old syntax. After pathfinding is merged into
// server trunk and fully deployed, we can remove the else statement.
if (pContent.has("access_prefs"))
{
llassert(pContent.has("access_prefs"));
llassert(pContent.get("access_prefs").isMap());
llassert(pContent.get("access_prefs").has("max"));
llassert(pContent.get("access_prefs").get("max").isString());
if (pContent.get("access_prefs").isMap() && pContent.get("access_prefs").has("max") &&
pContent.get("access_prefs").get("max").isString())
{
LLSD::String actualPreference = pContent.get("access_prefs").get("max").asString();
LLStringUtil::trim(actualPreference);
maturity = LLViewerRegion::shortStringToAccess(actualPreference);
}
}
else if (pContent.has("max"))
{
llassert(pContent.get("max").isString());
if (pContent.get("max").isString())
{
LLSD::String actualPreference = pContent.get("max").asString();
LLStringUtil::trim(actualPreference);
maturity = LLViewerRegion::shortStringToAccess(actualPreference);
}
}
}
}
return maturity;
}
void LLAgent::handlePreferredMaturityResult(U8 pServerMaturity)
{
// Update the number of responses received
++mMaturityPreferenceResponseId;
llassert(mMaturityPreferenceResponseId <= mMaturityPreferenceRequestId);
// Update the last known server maturity response
mLastKnownResponseMaturity = pServerMaturity;
// Ignore all responses if we know there are more unanswered requests that are expected
if (mMaturityPreferenceResponseId == mMaturityPreferenceRequestId)
{
// If we received a response that matches the last known request, then we are good
if (mLastKnownRequestMaturity == mLastKnownResponseMaturity)
{
mMaturityPreferenceNumRetries = 0;
reportPreferredMaturitySuccess();
llassert(static_cast<U8>(gSavedSettings.getU32("PreferredMaturity")) == mLastKnownResponseMaturity);
}
// Else, the viewer is out of sync with the server, so let's try to re-sync with the
// server by re-sending our last known request. Cap the re-tries at 3 just to be safe.
else if (++mMaturityPreferenceNumRetries <= 3)
{
llinfos << "Retrying attempt #" << mMaturityPreferenceNumRetries << " to set viewer preferred maturity to '"
<< LLViewerRegion::accessToString(mLastKnownRequestMaturity) << "'" << llendl;
sendMaturityPreferenceToServer(mLastKnownRequestMaturity);
}
// Else, the viewer is style out of sync with the server after 3 retries, so inform the user
else
{
mMaturityPreferenceNumRetries = 0;
reportPreferredMaturityError();
}
}
}
void LLAgent::handlePreferredMaturityError()
{
// Update the number of responses received
++mMaturityPreferenceResponseId;
llassert(mMaturityPreferenceResponseId <= mMaturityPreferenceRequestId);
// Ignore all responses if we know there are more unanswered requests that are expected
if (mMaturityPreferenceResponseId == mMaturityPreferenceRequestId)
{
mMaturityPreferenceNumRetries = 0;
// If we received a response that matches the last known request, then we are synced with
// the server, but not quite sure why we are
if (mLastKnownRequestMaturity == mLastKnownResponseMaturity)
{
llwarns << "Got an error but maturity preference '" << LLViewerRegion::accessToString(mLastKnownRequestMaturity)
<< "' seems to be in sync with the server" << llendl;
reportPreferredMaturitySuccess();
}
// Else, the more likely case is that the last request does not match the last response,
// so inform the user
else
{
reportPreferredMaturityError();
}
}
}
void LLAgent::reportPreferredMaturitySuccess()
{
// If there is a pending teleport request waiting for the maturity preference to be synced with
// the server, let's start the pending request
if (hasPendingTeleportRequest())
{
startTeleportRequest();
}
}
void LLAgent::reportPreferredMaturityError()
{
// If there is a pending teleport request waiting for the maturity preference to be synced with
// the server, we were unable to successfully sync with the server on maturity preference, so let's
// just raise the screen.
mIsMaturityRatingChangingDuringTeleport = false;
if (hasPendingTeleportRequest())
{
setTeleportState(LLAgent::TELEPORT_NONE);
}
// Get the last known maturity request from the user activity
std::string preferredMaturity = LLViewerRegion::accessToString(mLastKnownRequestMaturity);
LLStringUtil::toLower(preferredMaturity);
// Get the last known maturity response from the server
std::string actualMaturity = LLViewerRegion::accessToString(mLastKnownResponseMaturity);
LLStringUtil::toLower(actualMaturity);
// Notify the user
LLSD args = LLSD::emptyMap();
args["PREFERRED_MATURITY"] = preferredMaturity;
args["ACTUAL_MATURITY"] = actualMaturity;
LLNotificationsUtil::add("MaturityChangeError", args);
// Check the saved settings to ensure that we are consistent. If we are not consistent, update
// the viewer, but do not send anything to server
U8 localMaturity = static_cast<U8>(gSavedSettings.getU32("PreferredMaturity"));
if (localMaturity != mLastKnownResponseMaturity)
{
bool tmpIsDoSendMaturityPreferenceToServer = mIsDoSendMaturityPreferenceToServer;
mIsDoSendMaturityPreferenceToServer = false;
llinfos << "Setting viewer preferred maturity to '" << LLViewerRegion::accessToString(mLastKnownResponseMaturity) << "'" << llendl;
gSavedSettings.setU32("PreferredMaturity", static_cast<U32>(mLastKnownResponseMaturity));
mIsDoSendMaturityPreferenceToServer = tmpIsDoSendMaturityPreferenceToServer;
}
}
bool LLAgent::isMaturityPreferenceSyncedWithServer() const
{
return (mMaturityPreferenceRequestId == mMaturityPreferenceResponseId);
}
void LLAgent::sendMaturityPreferenceToServer(U8 pPreferredMaturity)
{
// Only send maturity preference to the server if enabled
if (mIsDoSendMaturityPreferenceToServer)
{
// Increment the number of requests. The handlers manage a separate count of responses.
++mMaturityPreferenceRequestId;
// Update the last know maturity request
mLastKnownRequestMaturity = pPreferredMaturity;
// Create a response handler
LLHTTPClient::ResponderPtr responderPtr = LLHTTPClient::ResponderPtr(new LLMaturityPreferencesResponder(this, pPreferredMaturity, mLastKnownResponseMaturity));
// If we don't have a region, report it as an error
if (getRegion() == NULL)
{
responderPtr->error(0U, "region is not defined");
}
else
{
// Find the capability to send maturity preference
std::string url = getRegion()->getCapability("UpdateAgentInformation");
// If the capability is not defined, report it as an error
if (url.empty())
{
responderPtr->error(0U, "capability 'UpdateAgentInformation' is not defined for region");
}
else
{
// Set new access preference
LLSD access_prefs = LLSD::emptyMap();
access_prefs["max"] = LLViewerRegion::accessToShortString(pPreferredMaturity);
LLSD body = LLSD::emptyMap();
body["access_prefs"] = access_prefs;
llinfos << "Sending viewer preferred maturity to '" << LLViewerRegion::accessToString(pPreferredMaturity)
<< "' via capability to: " << url << llendl;
LLSD headers;
LLHTTPClient::post(url, body, responderPtr, headers, 30.0f);
}
}
}
return false;
}
BOOL LLAgent::getAdminOverride() const
@ -2721,11 +3081,6 @@ void LLAgent::setGodLevel(U8 god_level)
mAgentAccess->setGodLevel(god_level);
}
void LLAgent::setAOTransition()
{
mAgentAccess->setTransition();
}
const LLAgentAccess& LLAgent::getAgentAccess()
{
return *mAgentAccess;
@ -2736,9 +3091,9 @@ bool LLAgent::validateMaturity(const LLSD& newvalue)
return mAgentAccess->canSetMaturity(newvalue.asInteger());
}
void LLAgent::handleMaturity(const LLSD& newvalue)
void LLAgent::handleMaturity(const LLSD &pNewValue)
{
sendMaturityPreferenceToServer(newvalue.asInteger());
sendMaturityPreferenceToServer(static_cast<U8>(pNewValue.asInteger()));
}
//----------------------------------------------------------------------------
@ -3741,7 +4096,7 @@ bool LLAgent::teleportBridgeGlobal(const LLVector3d& pos_global)
// protected
bool LLAgent::teleportCore(bool is_local)
{
if(TELEPORT_NONE != mTeleportState)
if ((TELEPORT_NONE != mTeleportState) && (mTeleportState != TELEPORT_PENDING))
{
llwarns << "Attempt to teleport when already teleporting." << llendl;
//return false; //LO - yea, lets not return here, we may be stuck in TP and if we are, letting this go through will get us out;
@ -3823,6 +4178,102 @@ bool LLAgent::teleportCore(bool is_local)
return true;
}
bool LLAgent::hasRestartableFailedTeleportRequest()
{
return ((mTeleportRequest != NULL) && (mTeleportRequest->getStatus() == LLTeleportRequest::kFailed) &&
mTeleportRequest->canRestartTeleport());
}
void LLAgent::restartFailedTeleportRequest()
{
if (hasRestartableFailedTeleportRequest())
{
mTeleportRequest->setStatus(LLTeleportRequest::kRestartPending);
startTeleportRequest();
}
}
void LLAgent::clearTeleportRequest()
{
mTeleportRequest.reset();
}
void LLAgent::setMaturityRatingChangeDuringTeleport(U8 pMaturityRatingChange)
{
mIsMaturityRatingChangingDuringTeleport = true;
mMaturityRatingChange = pMaturityRatingChange;
}
bool LLAgent::hasPendingTeleportRequest()
{
return ((mTeleportRequest != NULL) &&
((mTeleportRequest->getStatus() == LLTeleportRequest::kPending) ||
(mTeleportRequest->getStatus() == LLTeleportRequest::kRestartPending)));
}
void LLAgent::startTeleportRequest()
{
if (hasPendingTeleportRequest())
{
if (!isMaturityPreferenceSyncedWithServer())
{
gTeleportDisplay = TRUE;
setTeleportState(TELEPORT_PENDING);
}
else
{
switch (mTeleportRequest->getStatus())
{
case LLTeleportRequest::kPending :
mTeleportRequest->setStatus(LLTeleportRequest::kStarted);
mTeleportRequest->startTeleport();
break;
case LLTeleportRequest::kRestartPending :
llassert(mTeleportRequest->canRestartTeleport());
mTeleportRequest->setStatus(LLTeleportRequest::kStarted);
mTeleportRequest->restartTeleport();
break;
default :
llassert(0);
break;
}
}
}
}
void LLAgent::handleTeleportFinished()
{
clearTeleportRequest();
if (mIsMaturityRatingChangingDuringTeleport)
{
// notify user that the maturity preference has been changed
std::string maturityRating = LLViewerRegion::accessToString(mMaturityRatingChange);
LLStringUtil::toLower(maturityRating);
LLSD args;
args["RATING"] = maturityRating;
LLNotificationsUtil::add("PreferredMaturityChanged", args);
mIsMaturityRatingChangingDuringTeleport = false;
}
}
void LLAgent::handleTeleportFailed()
{
if (mTeleportRequest != NULL)
{
mTeleportRequest->setStatus(LLTeleportRequest::kFailed);
}
if (mIsMaturityRatingChangingDuringTeleport)
{
// notify user that the maturity preference has been changed
std::string maturityRating = LLViewerRegion::accessToString(mMaturityRatingChange);
LLStringUtil::toLower(maturityRating);
LLSD args;
args["RATING"] = maturityRating;
LLNotificationsUtil::add("PreferredMaturityChanged", args);
mIsMaturityRatingChangingDuringTeleport = false;
}
}
void LLAgent::teleportRequest(
const U64& region_handle,
const LLVector3& pos_local,
@ -3867,6 +4318,12 @@ void LLAgent::teleportViaLandmark(const LLUUID& landmark_asset_id)
}
// [/RLVa:KB]
mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLandmark(landmark_asset_id));
startTeleportRequest();
}
void LLAgent::doTeleportViaLandmark(const LLUUID& landmark_asset_id)
{
LLViewerRegion *regionp = getRegion();
if(regionp && teleportCore())
{
@ -3881,6 +4338,12 @@ void LLAgent::teleportViaLandmark(const LLUUID& landmark_asset_id)
}
void LLAgent::teleportViaLure(const LLUUID& lure_id, BOOL godlike)
{
mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLure(lure_id, godlike));
startTeleportRequest();
}
void LLAgent::doTeleportViaLure(const LLUUID& lure_id, BOOL godlike)
{
LLViewerRegion* regionp = getRegion();
if(regionp && teleportCore())
@ -3913,18 +4376,21 @@ void LLAgent::teleportViaLure(const LLUUID& lure_id, BOOL godlike)
// James Cook, July 28, 2005
void LLAgent::teleportCancel()
{
LLViewerRegion* regionp = getRegion();
if(regionp)
if (!hasPendingTeleportRequest())
{
// send the message
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("TeleportCancel");
msg->nextBlockFast(_PREHASH_Info);
msg->addUUIDFast(_PREHASH_AgentID, getID());
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
sendReliableMessage();
}
gTeleportDisplay = FALSE;
LLViewerRegion* regionp = getRegion();
if(regionp)
{
// send the message
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("TeleportCancel");
msg->nextBlockFast(_PREHASH_Info);
msg->addUUIDFast(_PREHASH_AgentID, getID());
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
sendReliableMessage();
}
}
clearTeleportRequest();
gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
}
@ -3950,6 +4416,12 @@ void LLAgent::teleportViaLocation(const LLVector3d& pos_global)
}
// [/RLVa:KB]
mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLocation(pos_global));
startTeleportRequest();
}
void LLAgent::doTeleportViaLocation(const LLVector3d& pos_global)
{
LLViewerRegion* regionp = getRegion();
U64 handle = to_region_handle(pos_global);
bool isLocal = (regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY]));
@ -3963,7 +4435,8 @@ void LLAgent::teleportViaLocation(const LLVector3d& pos_global)
(F32)(pos_global.mdV[VZ]));
teleportRequest(handle, pos_local);
}
else if(regionp && teleportCore(isLocal))
else if(regionp &&
teleportCore(regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY])))
{
llwarns << "Using deprecated teleportlocationrequest." << llendl;
// send the message
@ -4008,6 +4481,12 @@ void LLAgent::teleportViaLocationLookAt(const LLVector3d& pos_global)
}
// [/RLVa:KB]
mTeleportRequest = LLTeleportRequestPtr(new LLTeleportRequestViaLocationLookAt(pos_global));
startTeleportRequest();
}
void LLAgent::doTeleportViaLocationLookAt(const LLVector3d& pos_global)
{
mbTeleportKeepsLookAt = true;
gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); // detach camera form avatar, so it keeps direction
U64 region_handle = to_region_handle(pos_global);
@ -4482,5 +4961,149 @@ LLAgentQueryManager::~LLAgentQueryManager()
{
}
// EOF
//-----------------------------------------------------------------------------
// LLTeleportRequest
//-----------------------------------------------------------------------------
LLTeleportRequest::LLTeleportRequest()
: mStatus(kPending)
{
}
LLTeleportRequest::~LLTeleportRequest()
{
}
bool LLTeleportRequest::canRestartTeleport()
{
return false;
}
void LLTeleportRequest::restartTeleport()
{
llassert(0);
}
//-----------------------------------------------------------------------------
// LLTeleportRequestViaLandmark
//-----------------------------------------------------------------------------
LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId)
: LLTeleportRequest(),
mLandmarkId(pLandmarkId)
{
}
LLTeleportRequestViaLandmark::~LLTeleportRequestViaLandmark()
{
}
bool LLTeleportRequestViaLandmark::canRestartTeleport()
{
return true;
}
void LLTeleportRequestViaLandmark::startTeleport()
{
gAgent.doTeleportViaLandmark(getLandmarkId());
}
void LLTeleportRequestViaLandmark::restartTeleport()
{
gAgent.doTeleportViaLandmark(getLandmarkId());
}
//-----------------------------------------------------------------------------
// LLTeleportRequestViaLure
//-----------------------------------------------------------------------------
LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID &pLureId, BOOL pIsLureGodLike)
: LLTeleportRequestViaLandmark(pLureId),
mIsLureGodLike(pIsLureGodLike)
{
}
LLTeleportRequestViaLure::~LLTeleportRequestViaLure()
{
}
bool LLTeleportRequestViaLure::canRestartTeleport()
{
// stinson 05/17/2012 : cannot restart a teleport via lure because of server-side restrictions
// The current scenario is as follows:
// 1. User A initializes a request for User B to teleport via lure
// 2. User B accepts the teleport via lure request
// 3. The server sees the init request from User A and the accept request from User B and matches them up
// 4. The server then removes the paired requests up from the "queue"
// 5. The server then fails User B's teleport for reason of maturity level (for example)
// 6. User B's viewer prompts user to increase their maturity level profile value.
// 7. User B confirms and accepts increase in maturity level
// 8. User B's viewer then attempts to teleport via lure again
// 9. This request will time-out on the viewer-side because User A's initial request has been removed from the "queue" in step 4
return false;
}
void LLTeleportRequestViaLure::startTeleport()
{
gAgent.doTeleportViaLure(getLandmarkId(), isLureGodLike());
}
//-----------------------------------------------------------------------------
// LLTeleportRequestViaLocation
//-----------------------------------------------------------------------------
LLTeleportRequestViaLocation::LLTeleportRequestViaLocation(const LLVector3d &pPosGlobal)
: LLTeleportRequest(),
mPosGlobal(pPosGlobal)
{
}
LLTeleportRequestViaLocation::~LLTeleportRequestViaLocation()
{
}
bool LLTeleportRequestViaLocation::canRestartTeleport()
{
return true;
}
void LLTeleportRequestViaLocation::startTeleport()
{
gAgent.doTeleportViaLocation(getPosGlobal());
}
void LLTeleportRequestViaLocation::restartTeleport()
{
gAgent.doTeleportViaLocation(getPosGlobal());
}
//-----------------------------------------------------------------------------
// LLTeleportRequestViaLocationLookAt
//-----------------------------------------------------------------------------
LLTeleportRequestViaLocationLookAt::LLTeleportRequestViaLocationLookAt(const LLVector3d &pPosGlobal)
: LLTeleportRequestViaLocation(pPosGlobal)
{
}
LLTeleportRequestViaLocationLookAt::~LLTeleportRequestViaLocationLookAt()
{
}
bool LLTeleportRequestViaLocationLookAt::canRestartTeleport()
{
return true;
}
void LLTeleportRequestViaLocationLookAt::startTeleport()
{
gAgent.doTeleportViaLocationLookAt(getPosGlobal());
}
void LLTeleportRequestViaLocationLookAt::restartTeleport()
{
gAgent.doTeleportViaLocationLookAt(getPosGlobal());
}
// EOF

View File

@ -35,6 +35,8 @@
#include "llcoordframe.h" // for mFrameAgent
#include "llvoavatardefines.h"
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
extern const BOOL ANIMATE;
@ -56,6 +58,9 @@ class LLAgentAccess;
class LLSLURL;
class LLPauseRequestHandle;
class LLUIColor;
class LLTeleportRequest;
typedef boost::shared_ptr<LLTeleportRequest> LLTeleportRequestPtr;
//--------------------------------------------------------------------
// Types
@ -574,7 +579,8 @@ public:
TELEPORT_MOVING = 3, // Viewer has received destination location from source simulator
TELEPORT_START_ARRIVAL = 4, // Transition to ARRIVING. Viewer has received avatar update, etc., from destination simulator
TELEPORT_ARRIVING = 5, // Make the user wait while content "pre-caches"
TELEPORT_LOCAL = 6 // Teleporting in-sim without showing the progress screen
TELEPORT_LOCAL = 6, // Teleporting in-sim without showing the progress screen
TELEPORT_PENDING = 7
};
public:
@ -591,9 +597,6 @@ private:
// Teleport Actions
//--------------------------------------------------------------------
public:
void teleportRequest(const U64& region_handle,
const LLVector3& pos_local, // Go to a named location home
bool look_at_from_camera = false);
void teleportViaLandmark(const LLUUID& landmark_id); // Teleport to a landmark
void teleportHome() { teleportViaLandmark(LLUUID::null); } // Go home
void teleportViaLure(const LLUUID& lure_id, BOOL godlike); // To an invited location
@ -609,6 +612,44 @@ protected:
bool teleportCore(bool is_local = false); // Stuff for all teleports; returns true if the teleport can proceed
//--------------------------------------------------------------------
// Teleport State
//--------------------------------------------------------------------
public:
bool hasRestartableFailedTeleportRequest();
void restartFailedTeleportRequest();
void clearTeleportRequest();
void setMaturityRatingChangeDuringTeleport(U8 pMaturityRatingChange);
private:
friend class LLTeleportRequest;
friend class LLTeleportRequestViaLandmark;
friend class LLTeleportRequestViaLure;
friend class LLTeleportRequestViaLocation;
friend class LLTeleportRequestViaLocationLookAt;
LLTeleportRequestPtr mTeleportRequest;
boost::signals2::connection mTeleportFinishedSlot;
boost::signals2::connection mTeleportFailedSlot;
bool mIsMaturityRatingChangingDuringTeleport;
U8 mMaturityRatingChange;
bool hasPendingTeleportRequest();
void startTeleportRequest();
void teleportRequest(const U64& region_handle,
const LLVector3& pos_local, // Go to a named location home
bool look_at_from_camera = false);
void doTeleportViaLandmark(const LLUUID& landmark_id); // Teleport to a landmark
void doTeleportViaLure(const LLUUID& lure_id, BOOL godlike); // To an invited location
void doTeleportViaLocation(const LLVector3d& pos_global); // To a global location - this will probably need to be deprecated
void doTeleportViaLocationLookAt(const LLVector3d& pos_global);// To a global location, preserving camera rotation
void handleTeleportFinished();
void handleTeleportFailed();
//--------------------------------------------------------------------
// Teleport State
//--------------------------------------------------------------------
@ -654,8 +695,6 @@ public:
const LLAgentAccess& getAgentAccess();
BOOL canManageEstate() const;
BOOL getAdminOverride() const;
// ! BACKWARDS COMPATIBILITY ! This function can go away after the AO transition (see llstartup.cpp).
void setAOTransition();
private:
LLAgentAccess * mAgentAccess;
@ -690,13 +729,28 @@ public:
bool isTeen() const;
bool isMature() const;
bool isAdult() const;
void setTeen(bool teen);
void setMaturity(char text);
static int convertTextToMaturity(char text);
bool sendMaturityPreferenceToServer(int preferredMaturity); // ! "U8" instead of "int"?
static int convertTextToMaturity(char text);
private:
bool mIsDoSendMaturityPreferenceToServer;
unsigned int mMaturityPreferenceRequestId;
unsigned int mMaturityPreferenceResponseId;
unsigned int mMaturityPreferenceNumRetries;
U8 mLastKnownRequestMaturity;
U8 mLastKnownResponseMaturity;
bool isMaturityPreferenceSyncedWithServer() const;
void sendMaturityPreferenceToServer(U8 pPreferredMaturity);
friend class LLMaturityPreferencesResponder;
void handlePreferredMaturityResult(U8 pServerMaturity);
void handlePreferredMaturityError();
void reportPreferredMaturitySuccess();
void reportPreferredMaturityError();
// Maturity callbacks for PreferredMaturity control variable
void handleMaturity(const LLSD& newvalue);
void handleMaturity(const LLSD &pNewValue);
bool validateMaturity(const LLSD& newvalue);

View File

@ -33,8 +33,7 @@ LLAgentAccess::LLAgentAccess(LLControlGroup& savedSettings) :
mSavedSettings(savedSettings),
mAccess(SIM_ACCESS_PG),
mAdminOverride(false),
mGodLevel(GOD_NOT),
mAOTransition(false)
mGodLevel(GOD_NOT)
{
}
@ -133,18 +132,6 @@ bool LLAgentAccess::isAdult() const
return mAccess >= SIM_ACCESS_ADULT;
}
void LLAgentAccess::setTeen(bool teen)
{
if (teen)
{
mAccess = SIM_ACCESS_PG;
}
else
{
mAccess = SIM_ACCESS_MATURE;
}
}
//static
int LLAgentAccess::convertTextToMaturity(char text)
{
@ -182,16 +169,6 @@ void LLAgentAccess::setMaturity(char text)
mSavedSettings.setU32("PreferredMaturity", preferred_access);
}
void LLAgentAccess::setTransition()
{
mAOTransition = true;
}
bool LLAgentAccess::isInTransition() const
{
return mAOTransition;
}
bool LLAgentAccess::canSetMaturity(S32 maturity)
{
if (isGodlike()) // Gods can always set their Maturity level

View File

@ -59,13 +59,10 @@ public:
bool isMature() const;
bool isAdult() const;
void setTeen(bool teen);
void setMaturity(char text);
static int convertTextToMaturity(char text);
void setTransition(); // sets the transition bit, which defaults to false
bool isInTransition() const;
bool canSetMaturity(S32 maturity);
private:
@ -73,13 +70,6 @@ private:
U8 mGodLevel;
bool mAdminOverride;
// this should be deleted after the 60-day AO transition.
// It should be safe to remove it in Viewer 2009
// It's set by a special short-term flag in login.cgi
// called ao_transition. When that's gone, this can go, along with
// all of the code that depends on it.
bool mAOTransition;
LLControlGroup& mSavedSettings;
};

View File

@ -242,12 +242,13 @@ LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit
LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
{
LL_INFOS("Avatar") << self_av_string() << "done update appearance on destroy" << LL_ENDL;
selfStopPhase("update_appearance_on_destroy");
if (!LLApp::isExiting())
{
// speculative fix for MAINT-1150
LL_INFOS("Avatar") << self_av_string() << "done update appearance on destroy" << LL_ENDL;
selfStopPhase("update_appearance_on_destroy");
LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder);
}
}

View File

@ -29,8 +29,8 @@
#include "llappviewer.h"
// Viewer includes
#include "llversionviewer.h"
#include "llversioninfo.h"
#include "llversionviewer.h"
#include "llfeaturemanager.h"
#include "lluictrlfactory.h"
#include "lltexteditor.h"
@ -98,6 +98,7 @@
#include "llupdaterservice.h"
#include "llcallfloater.h"
#include "llfloatertexturefetchdebugger.h"
#include "llspellcheck.h"
// Linden library includes
#include "llavatarnamecache.h"
@ -122,6 +123,7 @@
// Third party library includes
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
@ -665,7 +667,7 @@ LLAppViewer::LLAppViewer() :
mPurgeOnExit(false),
mSecondInstance(false),
mSavedFinalSnapshot(false),
mSavePerAccountSettings(false), // don't save settings on logout unless login succeeded. -Zi
mSavePerAccountSettings(false), // don't save settings on logout unless login succeeded.
mForceGraphicsDetail(false),
mQuitRequested(false),
mLogoutRequestSent(false),
@ -1288,6 +1290,8 @@ void LLAppViewer::checkMemory()
static LLFastTimer::DeclareTimer FTM_MESSAGES("System Messages");
static LLFastTimer::DeclareTimer FTM_SLEEP("Sleep");
static LLFastTimer::DeclareTimer FTM_YIELD("Yield");
static LLFastTimer::DeclareTimer FTM_TEXTURE_CACHE("Texture Cache");
static LLFastTimer::DeclareTimer FTM_DECODE("Image Decode");
static LLFastTimer::DeclareTimer FTM_VFS("VFS Thread");
@ -1473,6 +1477,7 @@ bool LLAppViewer::mainLoop()
// yield some time to the os based on command line option
if(mYieldTime >= 0)
{
LLFastTimer t(FTM_YIELD);
ms_sleep(mYieldTime);
}
@ -1963,7 +1968,7 @@ bool LLAppViewer::cleanup()
}
// Only save per account settings if the previous login succeeded, otherwise
// we might end up with a cleared out settings file in case a previous login
// failed after loading per account settings. -Zi
// failed after loading per account settings.
else if (!mSavePerAccountSettings)
{
llinfos << "Not saving per-account settings; last login was not successful." << llendl;
@ -2789,6 +2794,19 @@ bool LLAppViewer::initConfiguration()
gDirUtilp->setSkinThemeFolder(themefolder->getValue().asString());
}
if (gSavedSettings.getBOOL("SpellCheck"))
{
std::list<std::string> dict_list;
std::string dict_setting = gSavedSettings.getString("SpellCheckDictionary");
boost::split(dict_list, dict_setting, boost::is_any_of(std::string(",")));
if (!dict_list.empty())
{
LLSpellChecker::setUseSpellCheck(dict_list.front());
dict_list.pop_front();
LLSpellChecker::instance().setSecondaryDictionaries(dict_list);
}
}
mYieldTime = gSavedSettings.getS32("YieldTime");
// Read skin/branding settings if specified.
@ -5415,7 +5433,7 @@ void LLAppViewer::handleLoginComplete()
}
// </FS:AO>
// we logged in successfully, so save settings on logout -Zi
// we logged in successfully, so save settings on logout
lldebugs << "Login successful, per account settings will be saved on logout." << llendl;
mSavePerAccountSettings=TRUE;
}

View File

@ -251,7 +251,7 @@ private:
bool mPurgeOnExit;
bool mSavedFinalSnapshot;
bool mSavePerAccountSettings; // only save per account settings if login succeeded -Zi
bool mSavePerAccountSettings; // only save per account settings if login succeeded
bool mForceGraphicsDetail;

View File

@ -0,0 +1,771 @@
/**
* @file llautoreplace.cpp
* @brief Auto Replace Manager
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llautoreplace.h"
#include "llsdserialize.h"
#include "llboost.h"
#include "llcontrol.h"
#include "llviewercontrol.h"
#include "llnotificationsutil.h"
LLAutoReplace* LLAutoReplace::sInstance;
const char* LLAutoReplace::SETTINGS_FILE_NAME = "autoreplace.xml";
LLAutoReplace::LLAutoReplace()
{
}
LLAutoReplace::~LLAutoReplace()
{
sInstance = NULL;
}
void LLAutoReplace::autoreplaceCallback(LLUIString& inputText, S32& cursorPos)
{
static LLCachedControl<bool> perform_autoreplace(gSavedSettings, "AutoReplace");
if(perform_autoreplace)
{
S32 wordEnd = cursorPos-1;
LLWString text = inputText.getWString();
bool atSpace = (text[wordEnd] == ' ');
bool haveWord = (LLWStringUtil::isPartOfWord(text[wordEnd]));
if (atSpace || haveWord)
{
if (atSpace && wordEnd > 0)
{
// find out if this space immediately follows a word
wordEnd--;
haveWord = (LLWStringUtil::isPartOfWord(text[wordEnd]));
}
if (haveWord)
{
// wordEnd points to the end of a word, now find the start of the word
std::string word;
S32 wordStart = wordEnd;
for ( S32 backOne = wordStart - 1;
backOne >= 0 && LLWStringUtil::isPartOfWord(text[backOne]);
backOne--
)
{
wordStart--; // walk wordStart back to the beginning of the word
}
LL_DEBUGS("AutoReplace")<<"wordStart: "<<wordStart<<" wordEnd: "<<wordEnd<<LL_ENDL;
std::string strText = std::string(text.begin(), text.end());
std::string lastWord = strText.substr(wordStart, wordEnd-wordStart+1);
std::string replacementWord( mSettings.replaceWord( lastWord ) );
if ( replacementWord != lastWord )
{
// The last word is one for which we have a replacement
if (atSpace)
{
// replace the last word in the input
LLWString strNew = utf8str_to_wstring(replacementWord);
LLWString strOld = utf8str_to_wstring(lastWord);
int size_change = strNew.size() - strOld.size();
text.replace(wordStart,lastWord.length(),strNew);
inputText = wstring_to_utf8str(text);
cursorPos+=size_change;
}
}
}
}
}
}
LLAutoReplace* LLAutoReplace::getInstance()
{
if(!sInstance)
{
sInstance = new LLAutoReplace();
sInstance->loadFromSettings();
}
return sInstance;
}
std::string LLAutoReplace::getUserSettingsFileName()
{
std::string path=gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "");
if (!path.empty())
{
path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, SETTINGS_FILE_NAME);
}
return path;
}
std::string LLAutoReplace::getAppSettingsFileName()
{
std::string path=gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "");
if (!path.empty())
{
path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, SETTINGS_FILE_NAME);
}
else
{
LL_ERRS("AutoReplace") << "Failed to get app settings directory name" << LL_ENDL;
}
return path;
}
LLAutoReplaceSettings LLAutoReplace::getSettings()
{
return mSettings;
}
void LLAutoReplace::setSettings(const LLAutoReplaceSettings& newSettings)
{
mSettings.set(newSettings);
/// Make the newSettings active and write them to user storage
saveToUserSettings();
}
void LLAutoReplace::loadFromSettings()
{
std::string filename=getUserSettingsFileName();
if (filename.empty())
{
LL_INFOS("AutoReplace") << "no valid user settings directory." << LL_ENDL;
}
if(gDirUtilp->fileExists(filename))
{
LLSD userSettings;
llifstream file;
file.open(filename.c_str());
if (file.is_open())
{
LLSDSerialize::fromXML(userSettings, file);
}
file.close();
if ( mSettings.setFromLLSD(userSettings) )
{
LL_INFOS("AutoReplace") << "settings loaded from '" << filename << "'" << LL_ENDL;
}
else
{
LL_WARNS("AutoReplace") << "invalid settings found in '" << filename << "'" << LL_ENDL;
}
}
else // no user settings found, try application settings
{
std::string defaultName = getAppSettingsFileName();
LL_INFOS("AutoReplace") << " user settings file '" << filename << "' not found"<< LL_ENDL;
bool gotSettings = false;
if(gDirUtilp->fileExists(defaultName))
{
LLSD appDefault;
llifstream file;
file.open(defaultName.c_str());
if (file.is_open())
{
LLSDSerialize::fromXMLDocument(appDefault, file);
}
file.close();
if ( mSettings.setFromLLSD(appDefault) )
{
LL_INFOS("AutoReplace") << "settings loaded from '" << defaultName.c_str() << "'" << LL_ENDL;
gotSettings = true;
}
else
{
LL_WARNS("AutoReplace") << "invalid settings found in '" << defaultName.c_str() << "'" << LL_ENDL;
}
}
if ( ! gotSettings )
{
if (mSettings.setFromLLSD(mSettings.getExampleLLSD()))
{
LL_WARNS("AutoReplace") << "no settings found; loaded example." << LL_ENDL;
}
else
{
LL_WARNS("AutoReplace") << "no settings found and example invalid!" << LL_ENDL;
}
}
}
}
void LLAutoReplace::saveToUserSettings()
{
std::string filename=getUserSettingsFileName();
llofstream file;
file.open(filename.c_str());
LLSDSerialize::toPrettyXML(mSettings.getAsLLSD(), file);
file.close();
LL_INFOS("AutoReplace") << "settings saved to '" << filename << "'" << LL_ENDL;
}
// ================================================================
// LLAutoReplaceSettings
// ================================================================
const std::string LLAutoReplaceSettings::AUTOREPLACE_LIST_NAME = "name"; ///< key for looking up list names
const std::string LLAutoReplaceSettings::AUTOREPLACE_LIST_REPLACEMENTS = "replacements"; ///< key for looking up replacement map
LLAutoReplaceSettings::LLAutoReplaceSettings()
{
}
LLAutoReplaceSettings::LLAutoReplaceSettings(const LLAutoReplaceSettings& settings)
{
// copy all values through fundamental type intermediates for thread safety
mLists = LLSD::emptyArray();
for ( LLSD::array_const_iterator list = settings.mLists.beginArray(), listEnd = settings.mLists.endArray();
list != listEnd;
list++
)
{
if ( (*list).isMap() ) // can fail due to LLSD-30: ignore it
{
LLSD listMap = LLSD::emptyMap();
std::string listName = (*list)[AUTOREPLACE_LIST_NAME];
listMap[AUTOREPLACE_LIST_NAME] = listName;
listMap[AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();
for ( LLSD::map_const_iterator
entry = (*list)[AUTOREPLACE_LIST_REPLACEMENTS].beginMap(),
entriesEnd = (*list)[AUTOREPLACE_LIST_REPLACEMENTS].endMap();
entry != entriesEnd;
entry++
)
{
std::string keyword = entry->first;
std::string replacement = entry->second.asString();
listMap[AUTOREPLACE_LIST_REPLACEMENTS].insert(keyword, LLSD(replacement));
}
mLists.append(listMap);
}
}
}
void LLAutoReplaceSettings::set(const LLAutoReplaceSettings& newSettings)
{
mLists = newSettings.mLists;
}
bool LLAutoReplaceSettings::setFromLLSD(const LLSD& settingsFromLLSD)
{
bool settingsValid = true;
if ( settingsFromLLSD.isArray() )
{
for ( LLSD::array_const_iterator
list = settingsFromLLSD.beginArray(),
listEnd = settingsFromLLSD.endArray();
settingsValid && list != listEnd;
list++
)
{
if ( (*list).isDefined() ) // can be undef due to LLSD-30: ignore it
{
settingsValid = listIsValid(*list);
}
}
}
else
{
settingsValid = false;
LL_WARNS("AutoReplace") << "settings are not an array" << LL_ENDL;
}
if ( settingsValid )
{
mLists = settingsFromLLSD;
}
else
{
LL_WARNS("AutoReplace") << "invalid settings discarded; using hard coded example" << LL_ENDL;
}
return settingsValid;
}
bool LLAutoReplaceSettings::listNameMatches( const LLSD& list, const std::string name )
{
return list.isMap()
&& list.has(AUTOREPLACE_LIST_NAME)
&& list[AUTOREPLACE_LIST_NAME].asString() == name;
}
const LLSD* LLAutoReplaceSettings::getListEntries(std::string listName)
{
const LLSD* returnedEntries = NULL;
for( LLSD::array_const_iterator list = mLists.beginArray(), endList = mLists.endArray();
returnedEntries == NULL && list != endList;
list++
)
{
const LLSD& thisList = *list;
if ( listNameMatches(thisList, listName) )
{
returnedEntries = &thisList[AUTOREPLACE_LIST_REPLACEMENTS];
}
}
return returnedEntries;
}
std::string LLAutoReplaceSettings::replacementFor(std::string keyword, std::string listName)
{
std::string replacement;
bool foundList = false;
for( LLSD::array_const_iterator list = mLists.beginArray(), endList = mLists.endArray();
! foundList && list != endList;
list++
)
{
const LLSD& thisList = *list;
if ( listNameMatches(thisList, listName) )
{
foundList = true; // whether there is a replacement or not, we're done
if ( thisList.isMap()
&& thisList.has(AUTOREPLACE_LIST_REPLACEMENTS)
&& thisList[AUTOREPLACE_LIST_REPLACEMENTS].has(keyword)
)
{
replacement = thisList[AUTOREPLACE_LIST_REPLACEMENTS][keyword].asString();
LL_DEBUGS("AutoReplace")<<"'"<<keyword<<"' -> '"<<replacement<<"'"<<LL_ENDL;
}
}
if (!foundList)
{
LL_WARNS("AutoReplace")<<"failed to find list '"<<listName<<"'"<<LL_ENDL;
}
}
if (replacement.empty())
{
LL_WARNS("AutoReplace")<<"failed to find '"<<keyword<<"'"<<LL_ENDL;
}
return replacement;
}
LLSD LLAutoReplaceSettings::getListNames()
{
LL_DEBUGS("AutoReplace")<<"====="<<LL_ENDL;
LLSD toReturn = LLSD::emptyArray();
S32 counter=0;
for( LLSD::array_const_iterator list = mLists.beginArray(), endList = mLists.endArray();
list != endList;
list++
)
{
const LLSD& thisList = *list;
if ( thisList.isMap() )
{
if ( thisList.has(AUTOREPLACE_LIST_NAME) )
{
std::string name = thisList[AUTOREPLACE_LIST_NAME].asString();
LL_DEBUGS("AutoReplace")<<counter<<" '"<<name<<"'"<<LL_ENDL;
toReturn.append(LLSD(name));
}
else
{
LL_ERRS("AutoReplace") <<counter<<" ! MISSING "<<AUTOREPLACE_LIST_NAME<< LL_ENDL;
}
}
else
{
LL_WARNS("AutoReplace")<<counter<<" ! not a map: "<<LLSD::typeString(thisList.type())<< LL_ENDL;
}
counter++;
}
LL_DEBUGS("AutoReplace")<<"^^^^^^"<<LL_ENDL;
return toReturn;
}
bool LLAutoReplaceSettings::listIsValid(const LLSD& list)
{
bool listValid = true;
if ( ! list.isMap() )
{
listValid = false;
LL_WARNS("AutoReplace") << "list is not a map" << LL_ENDL;
}
else if ( ! list.has(AUTOREPLACE_LIST_NAME)
|| ! list[AUTOREPLACE_LIST_NAME].isString()
|| list[AUTOREPLACE_LIST_NAME].asString().empty()
)
{
listValid = false;
LL_WARNS("AutoReplace")
<< "list found without " << AUTOREPLACE_LIST_NAME
<< " (or it is empty)"
<< LL_ENDL;
}
else if ( ! list.has(AUTOREPLACE_LIST_REPLACEMENTS) || ! list[AUTOREPLACE_LIST_REPLACEMENTS].isMap() )
{
listValid = false;
LL_WARNS("AutoReplace") << "list '" << list[AUTOREPLACE_LIST_NAME].asString() << "' without " << AUTOREPLACE_LIST_REPLACEMENTS << LL_ENDL;
}
else
{
for ( LLSD::map_const_iterator
entry = list[AUTOREPLACE_LIST_REPLACEMENTS].beginMap(),
entriesEnd = list[AUTOREPLACE_LIST_REPLACEMENTS].endMap();
listValid && entry != entriesEnd;
entry++
)
{
if ( ! entry->second.isString() )
{
listValid = false;
LL_WARNS("AutoReplace")
<< "non-string replacement value found in list '"
<< list[AUTOREPLACE_LIST_NAME].asString() << "'"
<< LL_ENDL;
}
}
}
return listValid;
}
const LLSD* LLAutoReplaceSettings::exportList(std::string listName)
{
const LLSD* exportedList = NULL;
for ( LLSD::array_const_iterator list = mLists.beginArray(), listEnd = mLists.endArray();
exportedList == NULL && list != listEnd;
list++
)
{
if ( listNameMatches(*list, listName) )
{
const LLSD& namedList = (*list);
exportedList = &namedList;
}
}
return exportedList;
}
bool LLAutoReplaceSettings::listNameIsUnique(const LLSD& newList)
{
bool nameIsUnique = true;
// this must always be called with a valid list, so it is safe to assume it has a name
std::string newListName = newList[AUTOREPLACE_LIST_NAME].asString();
for ( LLSD::array_const_iterator list = mLists.beginArray(), listEnd = mLists.endArray();
nameIsUnique && list != listEnd;
list++
)
{
if ( listNameMatches(*list, newListName) )
{
LL_WARNS("AutoReplace")<<"duplicate list name '"<<newListName<<"'"<<LL_ENDL;
nameIsUnique = false;
}
}
return nameIsUnique;
}
/* static */
void LLAutoReplaceSettings::createEmptyList(LLSD& emptyList)
{
emptyList = LLSD::emptyMap();
emptyList[AUTOREPLACE_LIST_NAME] = "Empty";
emptyList[AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();
}
/* static */
void LLAutoReplaceSettings::setListName(LLSD& list, const std::string& newName)
{
list[AUTOREPLACE_LIST_NAME] = newName;
}
/* static */
std::string LLAutoReplaceSettings::getListName(LLSD& list)
{
std::string name;
if ( list.isMap() && list.has(AUTOREPLACE_LIST_NAME) && list[AUTOREPLACE_LIST_NAME].isString() )
{
name = list[AUTOREPLACE_LIST_NAME].asString();
}
return name;
}
LLAutoReplaceSettings::AddListResult LLAutoReplaceSettings::addList(const LLSD& newList)
{
AddListResult result;
if ( listIsValid( newList ) )
{
if ( listNameIsUnique( newList ) )
{
mLists.append(newList);
result = AddListOk;
}
else
{
LL_WARNS("AutoReplace") << "attempt to add duplicate name" << LL_ENDL;
result = AddListDuplicateName;
}
}
else
{
LL_WARNS("AutoReplace") << "attempt to add invalid list" << LL_ENDL;
result = AddListInvalidList;
}
return result;
}
bool LLAutoReplaceSettings::removeReplacementList(std::string listName)
{
bool found = false;
for( S32 index = 0; !found && mLists[index].isDefined(); index++ )
{
if( listNameMatches(mLists.get(index), listName) )
{
LL_DEBUGS("AutoReplace")<<"list '"<<listName<<"'"<<LL_ENDL;
mLists.erase(index);
found = true;
}
}
return found;
}
/// Move the named list up in the priority order
bool LLAutoReplaceSettings::increaseListPriority(std::string listName)
{
LL_DEBUGS("AutoReplace")<<listName<<LL_ENDL;
bool found = false;
S32 search_index, previous_index;
LLSD targetList;
// The following is working around the fact that LLSD arrays containing maps also seem to have undefined entries... see LLSD-30
previous_index = -1;
for ( search_index = 0, targetList = mLists[0];
!found && search_index < mLists.size();
search_index += 1, targetList = mLists[search_index]
)
{
if ( targetList.isMap() )
{
if ( listNameMatches( targetList, listName) )
{
LL_DEBUGS("AutoReplace")<<"found at "<<search_index<<", previous is "<<previous_index<<LL_ENDL;
found = true;
if (previous_index >= 0)
{
LL_DEBUGS("AutoReplace") << "erase "<<search_index<<LL_ENDL;
mLists.erase(search_index);
LL_DEBUGS("AutoReplace") << "insert at "<<previous_index<<LL_ENDL;
mLists.insert(previous_index, targetList);
}
else
{
LL_WARNS("AutoReplace") << "attempted to move top list up" << LL_ENDL;
}
}
else
{
previous_index = search_index;
}
}
else
{
LL_DEBUGS("AutoReplace") << search_index<<" is "<<LLSD::typeString(targetList.type())<<LL_ENDL;
}
}
return found;
}
/// Move the named list down in the priority order
bool LLAutoReplaceSettings::decreaseListPriority(std::string listName)
{
LL_DEBUGS("AutoReplace")<<listName<<LL_ENDL;
S32 found_index = -1;
S32 search_index;
for ( search_index = 0;
found_index == -1 && search_index < mLists.size();
search_index++
)
{
if ( listNameMatches( mLists[search_index], listName) )
{
LL_DEBUGS("AutoReplace")<<"found at index "<<search_index<<LL_ENDL;
found_index = search_index;
}
}
if (found_index != -1)
{
S32 next_index;
for ( next_index = found_index+1;
next_index < mLists.size() && ! mLists[next_index].isMap();
next_index++
)
{
// skipping over any undefined slots (see LLSD-30)
LL_WARNS("AutoReplace")<<next_index<<" ! not a map: "<<LLSD::typeString(mLists[next_index].type())<< LL_ENDL;
}
if ( next_index < mLists.size() )
{
LLSD next_list = mLists[next_index];
LL_DEBUGS("AutoReplace") << "erase "<<next_index<<LL_ENDL;
mLists.erase(next_index);
LL_DEBUGS("AutoReplace") << "insert at "<<found_index<<LL_ENDL;
mLists.insert(found_index, next_list);
}
else
{
LL_WARNS("AutoReplace") << "attempted to move bottom list down" << LL_ENDL;
}
}
else
{
LL_WARNS("AutoReplace") << "not found" << LL_ENDL;
}
return (found_index != -1);
}
std::string LLAutoReplaceSettings::replaceWord(const std::string currentWord)
{
std::string returnedWord = currentWord; // in case no replacement is found
static LLCachedControl<bool> autoreplace_enabled(gSavedSettings, "AutoReplace");
if ( autoreplace_enabled )
{
LL_DEBUGS("AutoReplace")<<"checking '"<<currentWord<<"'"<< LL_ENDL;
//loop through lists in order
bool found = false;
for( LLSD::array_const_iterator list = mLists.beginArray(), endLists = mLists.endArray();
! found && list != endLists;
list++
)
{
const LLSD& checkList = *list;
const LLSD& replacements = checkList[AUTOREPLACE_LIST_REPLACEMENTS];
if ( replacements.has(currentWord) )
{
found = true;
LL_DEBUGS("AutoReplace")
<< " found in list '" << checkList[AUTOREPLACE_LIST_NAME].asString()
<< " => '" << replacements[currentWord].asString() << "'"
<< LL_ENDL;
returnedWord = replacements[currentWord].asString();
}
}
}
return returnedWord;
}
bool LLAutoReplaceSettings::addEntryToList(LLWString keyword, LLWString replacement, std::string listName)
{
bool added = false;
if ( ! keyword.empty() && ! replacement.empty() )
{
bool isOneWord = true;
for (S32 character = 0; isOneWord && character < keyword.size(); character++ )
{
if ( ! LLWStringUtil::isPartOfWord(keyword[character]) )
{
LL_WARNS("AutoReplace") << "keyword '" << wstring_to_utf8str(keyword) << "' not a single word (len "<<keyword.size()<<" '"<<character<<"')" << LL_ENDL;
isOneWord = false;
}
}
if ( isOneWord )
{
bool listFound = false;
for( LLSD::array_iterator list = mLists.beginArray(), endLists = mLists.endArray();
! listFound && list != endLists;
list++
)
{
if ( listNameMatches(*list, listName) )
{
listFound = true;
(*list)[AUTOREPLACE_LIST_REPLACEMENTS][wstring_to_utf8str(keyword)]=wstring_to_utf8str(replacement);
}
}
if (listFound)
{
added = true;
}
else
{
LL_WARNS("AutoReplace") << "list '" << listName << "' not found" << LL_ENDL;
}
}
}
return added;
}
bool LLAutoReplaceSettings::removeEntryFromList(std::string keyword, std::string listName)
{
bool found = false;
for( LLSD::array_iterator list = mLists.beginArray(), endLists = mLists.endArray();
! found && list != endLists;
list++
)
{
if ( listNameMatches(*list, listName) )
{
found = true;
(*list)[AUTOREPLACE_LIST_REPLACEMENTS].erase(keyword);
}
}
if (!found)
{
LL_WARNS("AutoReplace") << "list '" << listName << "' not found" << LL_ENDL;
}
return found;
}
LLSD LLAutoReplaceSettings::getExampleLLSD()
{
LL_DEBUGS("AutoReplace")<<LL_ENDL;
LLSD example = LLSD::emptyArray();
example[0] = LLSD::emptyMap();
example[0][AUTOREPLACE_LIST_NAME] = "Example List 1";
example[0][AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();
example[0][AUTOREPLACE_LIST_REPLACEMENTS]["keyword1"] = "replacement string 1";
example[0][AUTOREPLACE_LIST_REPLACEMENTS]["keyword2"] = "replacement string 2";
example[1] = LLSD::emptyMap();
example[1][AUTOREPLACE_LIST_NAME] = "Example List 2";
example[1][AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();
example[1][AUTOREPLACE_LIST_REPLACEMENTS]["mistake1"] = "correction 1";
example[1][AUTOREPLACE_LIST_REPLACEMENTS]["mistake2"] = "correction 2";
return example;
}
const LLSD& LLAutoReplaceSettings::getAsLLSD()
{
return mLists;
}
LLAutoReplaceSettings::~LLAutoReplaceSettings()
{
}

View File

@ -0,0 +1,228 @@
/**
* @file llautoreplace.h
* @brief Auto Replace Manager
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef LLAUTOREPLACE_H
#define LLAUTOREPLACE_H
#include "lllineeditor.h"
class LLAutoReplace;
/** The configuration data for the LLAutoReplace object
*
* This is a separate class so that the LLFloaterAutoReplaceSettings
* can have a copy of the configuration to manipulate before committing
* the changes back to the LLAutoReplace singleton that provides the
* autoreplace callback.
*/
class LLAutoReplaceSettings
{
public:
LLAutoReplaceSettings();
~LLAutoReplaceSettings();
/// Constructor for creating a tempory copy of the current settings
LLAutoReplaceSettings(const LLAutoReplaceSettings& settings);
/// Replace the current settings with new ones and save them to the user settings file
void set(const LLAutoReplaceSettings& newSettings);
/// Load the current settings read from an LLSD file
bool setFromLLSD(const LLSD& settingsFromLLSD);
///< @returns whether or not the settingsFromLLSD were valid
// ================================================================
///@{ @name List Operations
// ================================================================
/// @returns the configured list names as an LLSD Array of strings
LLSD getListNames();
/// Status values returned from the addList method
typedef enum
{
AddListOk,
AddListDuplicateName,
AddListInvalidList,
} AddListResult;
/// Inserts a new list at the end of the priority order
AddListResult addList(const LLSD& newList);
/// Removes the named list, @returns false if not found
bool removeReplacementList(std::string listName);
/// Move the named list up in the priority order
bool increaseListPriority(std::string listName);
///< @returns false if the list is not found
/// Move the named list down in the priority order
bool decreaseListPriority(std::string listName);
///< @returns false if the list is not found
/// Get a copy of just one list (for saving to an export file)
const LLSD* exportList(std::string listName);
/// @returns an LLSD map
/// Checks for required elements, and that each has the correct type.
bool listIsValid(const LLSD& listSettings);
/// Checks for required elements, and that each has the correct type.
bool listNameIs(const LLSD& listSettings);
/// Checks to see if a new lists name conflicts with one in the settings
bool listNameIsUnique(const LLSD& newList);
/// @note must be called with LLSD that has passed listIsValid
/// Initializes emptyList to an empty list named 'Empty'
static void createEmptyList(LLSD& emptyList);
/// Resets the name of a list to a new value
static void setListName(LLSD& list, const std::string& newName);
/// Gets the name of a list
static std::string getListName(LLSD& list);
///@}
// ================================================================
///@{ @name Replacement Entry Operations
// ================================================================
/// Get the replacements specified by a given list
const LLSD* getListEntries(std::string listName);
///< @returns an LLSD Map of keyword -> replacement test pairs
/// Get the replacement for the keyword from the specified list
std::string replacementFor(std::string keyword, std::string listName);
/// Adds a keywword/replacement pair to the named list
bool addEntryToList(LLWString keyword, LLWString replacement, std::string listName);
/// Removes the keywword and its replacement from the named list
bool removeEntryFromList(std::string keyword, std::string listName);
/**
* Look for currentWord in the lists in order, returning any substitution found
* If no configured substitution is found, returns currentWord
*/
std::string replaceWord(const std::string currentWord /**< word to search for */ );
/// Provides a hard-coded example of settings
LLSD getExampleLLSD();
/// Get the actual settings as LLSD
const LLSD& getAsLLSD();
///< @note for use only in AutoReplace::saveToUserSettings
private:
/// Efficiently and safely compare list names
bool listNameMatches( const LLSD& list, const std::string name );
/// The actual llsd data structure
LLSD mLists;
static const std::string AUTOREPLACE_LIST_NAME; ///< key for looking up list names
static const std::string AUTOREPLACE_LIST_REPLACEMENTS; ///< key for looking up replacement map
/**<
* LLSD structure of the lists
* - The configuration is an array (mLists),
* - Each entry in the array is a replacement list
* - Each replacement list is a map with three keys:
* @verbatim
* "name" String the name of the list
* "replacements" Map keyword -> replacement pairs
*
* <llsd>
* <array>
* <map>
* <key>name</key> <string>List 1</string>
* <key>data</key>
* <map>
* <key>keyword1</key> <string>replacement1</string>
* <key>keyword2</key> <string>replacement2</string>
* </map>
* </map>
* <map>
* <key>name</key> <string>List 2</string>
* <key>data</key>
* <map>
* <key>keyword1</key> <string>replacement1</string>
* <key>keyword2</key> <string>replacement2</string>
* </map>
* </map>
* </array>
* </llsd>
* @endverbatim
*/
};
/** Provides a facility to auto-replace text dynamically as it is entered.
*
* When the end of a word is detected (defined as any punctuation character,
* or any whitespace except newline or return), the preceding word is used
* as a lookup key in an ordered list of maps. If a match is found in any
* map, the keyword is replaced by the associated value from the map.
*
* See the autoreplaceCallback method for how to add autoreplace functionality
* to a text entry tool.
*/
class LLAutoReplace : public LLSingleton<LLAutoReplace>
{
public:
LLAutoReplace();
~LLAutoReplace();
/// @return a pointer to the active instance
static LLAutoReplace* getInstance();
/// Callback that provides the hook for use in text entry methods
void autoreplaceCallback(LLUIString& inputText, S32& cursorPos);
/// Get a copy of the current settings
LLAutoReplaceSettings getSettings();
/// Commit new settings after making changes
void setSettings(const LLAutoReplaceSettings& settings);
private:
friend class LLSingleton<LLAutoReplace>;
static LLAutoReplace* sInstance; ///< the active settings instance
LLAutoReplaceSettings mSettings; ///< configuration information
/// Read settings from persistent storage
void loadFromSettings();
/// Make the newSettings active and write them to user storage
void saveToUserSettings();
/// Compute the user settings file name
std::string getUserSettingsFileName();
/// Compute the (read-ony) application settings file name
std::string getAppSettingsFileName();
/// basename for the settings files
static const char* SETTINGS_FILE_NAME;
};
#endif /* LLAUTOREPLACE_H */

View File

@ -627,9 +627,9 @@ protected:
void showInfoCtrl()
{
// if (mAvatarID.isNull() || mFrom.empty() || SYSTEM_FROM == mFrom) return;
// if (mAvatarID.isNull() || mFrom.empty() || CHAT_SOURCE_SYSTEM == mSourceType) return;
// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.2a) | Added: RLVa-1.2.0f
if ( (!mShowInfoCtrl) || (mAvatarID.isNull() || mFrom.empty() || SYSTEM_FROM == mFrom) ) return;
if ( (!mShowInfoCtrl) || (mAvatarID.isNull() || mFrom.empty() || CHAT_SOURCE_SYSTEM == mSourceType) ) return;
// [/RLVa:KB]
if (!sInfoCtrl)
@ -834,6 +834,8 @@ void LLChatHistory::clear()
mLastFromID = LLUUID::null;
}
static LLFastTimer::DeclareTimer FTM_APPEND_MESSAGE("Append Chat Message");
void LLChatHistory::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
{
mDisplayName = av_name.mDisplayName;
@ -842,6 +844,7 @@ void LLChatHistory::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName
void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LLStyle::Params& input_append_params)
{
LLFastTimer _(FTM_APPEND_MESSAGE);
bool use_plain_text_chat_history = args["use_plain_text_chat_history"].asBoolean();
bool hide_timestamps_nearby_chat = args["hide_timestamps_nearby_chat"].asBoolean();
// AO: Do any display name lookups in plaintext chat headers as early as possible to give the cache maximal
@ -1075,7 +1078,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
//</FS:HG> FS-1734 seperate name and text styles for moderator
if (use_plain_text_chat_history)
{
{
LLStyle::Params timestamp_style(style_params);
if (!message_from_log)
{
@ -1087,17 +1090,17 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
{
timestamp_style.font.style(moderator_name_style);
}
//</FS:HG> FS-1734 seperate name and text styles for moderator
//</FS:HG> FS-1734 seperate name and text styles for moderator
}
// [FIRE-1641 : SJ]: Option to hide timestamps in nearby chat - only add timestamps when hide_timestamps_nearby_chat not TRUE
// mEditor->appendText("[" + chat.mTimeStr + "] ", mEditor->getText().size() != 0, timestamp_style);
// mEditor->appendText("[" + chat.mTimeStr + "] ", mEditor->getLength() != 0, timestamp_style);
if (!hide_timestamps_nearby_chat)
{
mEditor->appendText("[" + chat.mTimeStr + "] ", mEditor->getText().size() != 0, timestamp_style);
mEditor->appendText("[" + chat.mTimeStr + "] ", mEditor->getLength() != 0, timestamp_style);
}
else
{
mEditor->appendLineBreakSegment(timestamp_style);
mEditor->appendLineBreakSegment(timestamp_style);
}
if (utf8str_trim(chat.mFromName).size() != 0)
@ -1276,7 +1279,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
style_params.color(txt_color);
style_params.readonly_color(txt_color);
if (mEditor->getText().size() == 0)
if (mEditor->getLength() == 0)
p.top_pad = 0;
else
p.top_pad = mTopHeaderPad;

View File

@ -450,7 +450,7 @@ void LLDrawable::makeStatic(BOOL warning_enabled)
{
if (isState(ACTIVE))
{
clearState(ACTIVE);
clearState(ACTIVE | ANIMATED_CHILD);
if (mParent.notNull() && mParent->isActive() && warning_enabled)
{
@ -538,9 +538,9 @@ F32 LLDrawable::updateXform(BOOL undamped)
target_rot = new_rot;
target_scale = new_scale;
}
else
else if (mVObjp->getAngularVelocity().isExactlyZero())
{
// snap to final position
// snap to final position (only if no target omega is applied)
dist_squared = 0.0f;
if (getVOVolume() && !isRoot())
{ //child prim snapping to some position, needs a rebuild
@ -549,15 +549,25 @@ F32 LLDrawable::updateXform(BOOL undamped)
}
}
if ((mCurrentScale != target_scale) ||
(!isRoot() &&
(dist_squared >= MIN_INTERPOLATE_DISTANCE_SQUARED ||
!mVObjp->getAngularVelocity().isExactlyZero() ||
target_pos != mXform.getPosition() ||
target_rot != mXform.getRotation())))
{ //child prim moving or scale change requires immediate rebuild
LLVector3 vec = mCurrentScale-target_scale;
if (vec*vec > MIN_INTERPOLATE_DISTANCE_SQUARED)
{ //scale change requires immediate rebuild
mCurrentScale = target_scale;
gPipeline.markRebuild(this, LLDrawable::REBUILD_POSITION, TRUE);
}
else if (!isRoot() &&
(!mVObjp->getAngularVelocity().isExactlyZero() ||
dist_squared > 0.f))
{ //child prim moving relative to parent, tag as needing to be rendered atomically and rebuild
dist_squared = 1.f; //keep this object on the move list
if (!isState(LLDrawable::ANIMATED_CHILD))
{
setState(LLDrawable::ANIMATED_CHILD);
gPipeline.markRebuild(this, LLDrawable::REBUILD_ALL, TRUE);
mVObjp->dirtySpatialGroup();
}
}
else if (!getVOVolume() && !isAvatar())
{
movePartition();
@ -568,9 +578,7 @@ F32 LLDrawable::updateXform(BOOL undamped)
mXform.setRotation(target_rot);
mXform.setScale(LLVector3(1,1,1)); //no scale in drawable transforms (IT'S A RULE!)
mXform.updateMatrix();
mCurrentScale = target_scale;
if (mSpatialBridge)
{
gPipeline.markMoved(mSpatialBridge, FALSE);
@ -596,7 +604,11 @@ void LLDrawable::moveUpdatePipeline(BOOL moved)
// Update the face centers.
for (S32 i = 0; i < getNumFaces(); i++)
{
getFace(i)->updateCenterAgent();
LLFace* face = getFace(i);
if (face)
{
face->updateCenterAgent();
}
}
}
@ -651,7 +663,6 @@ BOOL LLDrawable::updateMoveUndamped()
}
mVObjp->clearChanged(LLXform::MOVED);
return TRUE;
}
@ -727,7 +738,8 @@ void LLDrawable::updateDistance(LLCamera& camera, bool force_update)
for (S32 i = 0; i < getNumFaces(); i++)
{
LLFace* facep = getFace(i);
if (force_update || facep->getPoolType() == LLDrawPool::POOL_ALPHA)
if (facep &&
(force_update || facep->getPoolType() == LLDrawPool::POOL_ALPHA))
{
LLVector4a box;
box.setSub(facep->mExtents[1], facep->mExtents[0]);
@ -826,13 +838,16 @@ void LLDrawable::shiftPos(const LLVector4a &shift_vector)
for (S32 i = 0; i < getNumFaces(); i++)
{
LLFace *facep = getFace(i);
facep->mCenterAgent += LLVector3(shift_vector.getF32ptr());
facep->mExtents[0].add(shift_vector);
facep->mExtents[1].add(shift_vector);
if (!volume && facep->hasGeometry())
if (facep)
{
facep->clearVertexBuffer();
facep->mCenterAgent += LLVector3(shift_vector.getF32ptr());
facep->mExtents[0].add(shift_vector);
facep->mExtents[1].add(shift_vector);
if (!volume && facep->hasGeometry())
{
facep->clearVertexBuffer();
}
}
}
@ -954,7 +969,10 @@ void LLDrawable::setSpatialGroup(LLSpatialGroup *groupp)
for (S32 i = 0; i < getNumFaces(); ++i)
{
LLFace* facep = getFace(i);
facep->clearVertexBuffer();
if (facep)
{
facep->clearVertexBuffer();
}
}
}
@ -1535,10 +1553,10 @@ BOOL LLDrawable::isAnimating() const
return TRUE;
}
if (!isRoot() && !mVObjp->getAngularVelocity().isExactlyZero())
{
/*if (!isRoot() && !mVObjp->getAngularVelocity().isExactlyZero())
{ //target omega
return TRUE;
}
}*/
return FALSE;
}

View File

@ -277,6 +277,7 @@ public:
HAS_ALPHA = 0x04000000,
RIGGED = 0x08000000,
PARTITION_MOVE = 0x10000000,
ANIMATED_CHILD = 0x20000000,
} EDrawableFlags;
private: //aligned members
@ -333,35 +334,14 @@ inline LLFace* LLDrawable::getFace(const S32 i) const
if ((U32) i >= mFaces.size())
{
LLUUID objectID=getVObj()->getID();
// if our face list is empty, we have no real choice. -Zi
if(mFaces.empty())
{
llwarns << objectID << ": Empty face list." << llendl;
return NULL;
}
// otherwise try to return a valid face to avoid crashing. -Zi
llwarns << objectID << ": Invalid face index " << (U32) i << ". Max faces is: " << mFaces.size() << ". Returning face index 0." << llendl;
return mFaces[0];
llwarns << "Invalid face index." << llendl;
return NULL;
}
if (!mFaces[i])
{
LLUUID objectID=getVObj()->getID();
llwarns << objectID << ": Null face found at index " << (U32) i << ". Max faces is: " << mFaces.size() << "." << llendl;
if(i==0)
{
S32 max=getNumFaces();
// try to return a valid face to avoid crashing. If we only have one face, return NULL as last resort. -Zi
if(max>1)
return mFaces[max-1];
else
return NULL;
}
llwarns << "Null face found." << llendl;
return NULL;
}
return mFaces[i];

View File

@ -405,6 +405,12 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask)
{
LLDrawInfo& params = **k;
if ((params.mVertexBuffer->getTypeMask() & mask) != mask)
{ //FIXME!
llwarns << "Missing required components, skipping render batch." << llendl;
continue;
}
LLRenderPass::applyModelMatrix(params);

View File

@ -261,7 +261,9 @@ void LLDrawPoolAvatar::beginPostDeferredAlpha()
sRenderingSkinned = TRUE;
gPipeline.bindDeferredShader(*sVertexProgram);
sVertexProgram->setMinimumAlpha(0.2f);
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
}

File diff suppressed because it is too large Load Diff

View File

@ -83,6 +83,8 @@ public:
static void initClass();
static void cacheFaceInVRAM(const LLVolumeFace& vf);
public:
LLFace(LLDrawable* drawablep, LLViewerObject* objp) { init(drawablep, objp); }
~LLFace() { destroy(); }
@ -222,7 +224,7 @@ public:
//vertex buffer tracking
void setVertexBuffer(LLVertexBuffer* buffer);
void clearVertexBuffer(); //sets mVertexBuffer and mLastVertexBuffer to NULL
void clearVertexBuffer(); //sets mVertexBuffer to NULL
LLVertexBuffer* getVertexBuffer() const { return mVertexBuffer; }
U32 getRiggedVertexBufferDataMask() const;
S32 getRiggedIndex(U32 type) const;
@ -255,8 +257,7 @@ public:
private:
LLPointer<LLVertexBuffer> mVertexBuffer;
LLPointer<LLVertexBuffer> mLastVertexBuffer;
U32 mState;
LLFacePool* mDrawPoolp;
U32 mPoolType;
@ -269,12 +270,6 @@ private:
U32 mIndicesIndex; // index into draw pool for indices (yeah, I know!)
S32 mIndexInTex ;
//previous rebuild's geometry info
U16 mLastGeomCount;
U16 mLastGeomIndex;
U32 mLastIndicesCount;
U32 mLastIndicesIndex;
LLXformMatrix* mXform;
LLPointer<LLViewerTexture> mTexture;
LLPointer<LLDrawable> mDrawablep;

View File

@ -1191,7 +1191,7 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata)
}
else if (action == "paste")
{
pastFromClipboard();
pasteFromClipboard();
}
else if (action == "delete")
{
@ -1239,7 +1239,7 @@ BOOL LLFavoritesBarCtrl::isClipboardPasteable() const
return TRUE;
}
void LLFavoritesBarCtrl::pastFromClipboard() const
void LLFavoritesBarCtrl::pasteFromClipboard() const
{
LLInventoryModel* model = &gInventory;
if(model && isClipboardPasteable())

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