merge changes for storm-276
commit
925db41711
|
|
@ -138,6 +138,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
|
||||
# =================================================================
|
||||
|
|
|
|||
106
autobuild.xml
106
autobuild.xml
|
|
@ -363,6 +363,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>cefa829b9eca8f22ecd595921e189c11</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/oz_3p-dictionaries/rev/256063/arch/Darwin/installer/dictionaries-1-darwin-20120508.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin</string>
|
||||
</map>
|
||||
<key>linux</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>dbc8fd886fb639ddbd6614c00654e7b1</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/oz_3p-dictionaries/rev/256063/arch/Linux/installer/dictionaries-1-linux-20120508.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>linux</string>
|
||||
</map>
|
||||
<key>windows</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>983fd8afd02b975f85603545983713ed</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/oz_3p-dictionaries/rev/256063/arch/CYGWIN/installer/dictionaries-1-windows-20120508.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
</map>
|
||||
</map>
|
||||
</map>
|
||||
<key>elfio</key>
|
||||
<map>
|
||||
<key>license</key>
|
||||
|
|
@ -999,6 +1047,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>a3f0bea9e014773353e2c32223aae054</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/oz_3p-hunspell/rev/255153/arch/Darwin/installer/libhunspell-1.3.2-darwin-20120430.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin</string>
|
||||
</map>
|
||||
<key>linux</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>add23aa863505b1fc168d660ffc4be62</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/oz_3p-hunspell/rev/255153/arch/Linux/installer/libhunspell-1.3.2-linux-20120430.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>linux</string>
|
||||
</map>
|
||||
<key>windows</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>82df72ddf653392e7a65c96c0e0c6b5d</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/oz_3p-hunspell/rev/255153/arch/CYGWIN/installer/libhunspell-1.3.2-windows-20120430.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
</map>
|
||||
</map>
|
||||
</map>
|
||||
<key>libpng</key>
|
||||
<map>
|
||||
<key>license</key>
|
||||
|
|
@ -1099,8 +1195,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>
|
||||
|
|
@ -1762,8 +1856,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>
|
||||
|
|
@ -2473,6 +2571,8 @@
|
|||
<string>windows</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>version</key>
|
||||
<string>1.0</string>
|
||||
</map>
|
||||
<key>type</key>
|
||||
<string>autobuild</string>
|
||||
|
|
|
|||
21
build.sh
21
build.sh
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ set(cmake_SOURCE_FILES
|
|||
GLOD.cmake
|
||||
GStreamer010Plugin.cmake
|
||||
GooglePerfTools.cmake
|
||||
Hunspell.cmake
|
||||
JPEG.cmake
|
||||
LLAddBuildTest.cmake
|
||||
LLAudio.cmake
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ if(WINDOWS)
|
|||
libeay32.dll
|
||||
libcollada14dom22-d.dll
|
||||
glod.dll
|
||||
libhunspell.dll
|
||||
)
|
||||
|
||||
set(release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")
|
||||
|
|
@ -53,6 +54,7 @@ if(WINDOWS)
|
|||
libeay32.dll
|
||||
libcollada14dom22.dll
|
||||
glod.dll
|
||||
libhunspell.dll
|
||||
)
|
||||
|
||||
if(USE_GOOGLE_PERFTOOLS)
|
||||
|
|
@ -212,11 +214,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
|
||||
)
|
||||
|
||||
# fmod is statically linked on darwin
|
||||
|
|
@ -257,14 +260,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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
# -*- cmake -*-
|
||||
|
||||
# - Find HUNSPELL
|
||||
# This module defines
|
||||
# HUNSPELL_INCLUDE_DIR, where to find libhunspell.h, etc.
|
||||
# HUNSPELL_LIBRARY, the library needed to use HUNSPELL.
|
||||
# HUNSPELL_FOUND, If false, do not try to use HUNSPELL.
|
||||
|
||||
find_path(HUNSPELL_INCLUDE_DIR hunspell.h
|
||||
PATH_SUFFIXES hunspell
|
||||
)
|
||||
|
||||
set(HUNSPELL_NAMES ${HUNSPELL_NAMES} libhunspell-1.3.0 libhunspell)
|
||||
find_library(HUNSPELL_LIBRARY
|
||||
NAMES ${HUNSPELL_NAMES}
|
||||
)
|
||||
|
||||
if (HUNSPELL_LIBRARY AND HUNSPELL_INCLUDE_DIR)
|
||||
set(HUNSPELL_FOUND "YES")
|
||||
else (HUNSPELL_LIBRARY AND HUNSPELL_INCLUDE_DIR)
|
||||
set(HUNSPELL_FOUND "NO")
|
||||
endif (HUNSPELL_LIBRARY AND HUNSPELL_INCLUDE_DIR)
|
||||
|
||||
|
||||
if (HUNSPELL_FOUND)
|
||||
if (NOT HUNSPELL_FIND_QUIETLY)
|
||||
message(STATUS "Found Hunspell: Library in '${HUNSPELL_LIBRARY}' and header in '${HUNSPELL_INCLUDE_DIR}' ")
|
||||
endif (NOT HUNSPELL_FIND_QUIETLY)
|
||||
else (HUNSPELL_FOUND)
|
||||
if (HUNSPELL_FIND_REQUIRED)
|
||||
message(FATAL_ERROR " * * *\nCould not find HUNSPELL library! * * *")
|
||||
endif (HUNSPELL_FIND_REQUIRED)
|
||||
endif (HUNSPELL_FOUND)
|
||||
|
||||
mark_as_advanced(
|
||||
HUNSPELL_LIBRARY
|
||||
HUNSPELL_INCLUDE_DIR
|
||||
)
|
||||
|
|
@ -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)
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
include(Prebuilt)
|
||||
|
||||
if (NOT STANDALONE)
|
||||
use_prebuilt_binary(libhunspell)
|
||||
use_prebuilt_binary(libuuid)
|
||||
use_prebuilt_binary(slvoice)
|
||||
use_prebuilt_binary(fontconfig)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ include(LLUI)
|
|||
include(LLVFS) # ugh, needed for LLDir
|
||||
include(LLXML)
|
||||
include(LLXUIXML)
|
||||
include(Hunspell)
|
||||
include(Linking)
|
||||
# include(Tut)
|
||||
|
||||
|
|
@ -33,6 +34,7 @@ include_directories(
|
|||
${LLWINDOW_INCLUDE_DIRS}
|
||||
${LLXML_INCLUDE_DIRS}
|
||||
${LLXUIXML_INCLUDE_DIRS}
|
||||
${LIBS_PREBUILD_DIR}/include/hunspell
|
||||
)
|
||||
|
||||
set(llui_libtest_SOURCE_FILES
|
||||
|
|
@ -80,6 +82,7 @@ target_link_libraries(llui_libtest
|
|||
${LLIMAGEJ2COJ_LIBRARIES}
|
||||
${OS_LIBRARIES}
|
||||
${GOOGLE_PERFTOOLS_LIBRARIES}
|
||||
${HUNSPELL_LIBRARY}
|
||||
)
|
||||
|
||||
if (WINDOWS)
|
||||
|
|
|
|||
|
|
@ -182,6 +182,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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ include_directories(
|
|||
${LLVFS_INCLUDE_DIRS}
|
||||
${LLXML_INCLUDE_DIRS}
|
||||
${LLXUIXML_INCLUDE_DIRS}
|
||||
${LIBS_PREBUILD_DIR}/include/hunspell
|
||||
)
|
||||
|
||||
set(llui_SOURCE_FILES
|
||||
|
|
@ -87,6 +88,7 @@ set(llui_SOURCE_FILES
|
|||
llsearcheditor.cpp
|
||||
llslider.cpp
|
||||
llsliderctrl.cpp
|
||||
llspellcheck.cpp
|
||||
llspinctrl.cpp
|
||||
llstatbar.cpp
|
||||
llstatgraph.cpp
|
||||
|
|
@ -192,6 +194,8 @@ set(llui_HEADER_FILES
|
|||
llsdparam.h
|
||||
llsliderctrl.h
|
||||
llslider.h
|
||||
llspellcheck.h
|
||||
llspellcheckmenuhandler.h
|
||||
llspinctrl.h
|
||||
llstatbar.h
|
||||
llstatgraph.h
|
||||
|
|
@ -258,6 +262,7 @@ target_link_libraries(llui
|
|||
${LLXUIXML_LIBRARIES}
|
||||
${LLXML_LIBRARIES}
|
||||
${LLMATH_LIBRARIES}
|
||||
${HUNSPELL_LIBRARY}
|
||||
${LLCOMMON_LIBRARIES} # must be after llimage, llwindow, llrender
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -65,6 +66,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
|
||||
|
||||
|
|
@ -88,6 +90,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),
|
||||
|
|
@ -134,6 +137,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),
|
||||
|
|
@ -178,6 +184,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());
|
||||
|
||||
|
|
@ -519,6 +531,95 @@ 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)
|
||||
{
|
||||
|
|
@ -1064,9 +1165,8 @@ void LLLineEditor::cut()
|
|||
LLUI::reportBadKeystroke();
|
||||
}
|
||||
else
|
||||
if( mKeystrokeCallback )
|
||||
{
|
||||
mKeystrokeCallback( this );
|
||||
onKeystroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1193,9 +1293,8 @@ void LLLineEditor::pasteHelper(bool is_primary)
|
|||
LLUI::reportBadKeystroke();
|
||||
}
|
||||
else
|
||||
if( mKeystrokeCallback )
|
||||
{
|
||||
mKeystrokeCallback( this );
|
||||
onKeystroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1448,9 +1547,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1503,12 +1603,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;
|
||||
|
|
@ -1537,9 +1636,7 @@ void LLLineEditor::doDelete()
|
|||
|
||||
if (!prevalidateInput(text_to_delete))
|
||||
{
|
||||
if( mKeystrokeCallback )
|
||||
mKeystrokeCallback( this );
|
||||
|
||||
onKeystroke();
|
||||
return;
|
||||
}
|
||||
setCursor(getCursor() + 1);
|
||||
|
|
@ -1555,10 +1652,9 @@ void LLLineEditor::doDelete()
|
|||
}
|
||||
else
|
||||
{
|
||||
if( mKeystrokeCallback )
|
||||
{
|
||||
mKeystrokeCallback( this );
|
||||
}
|
||||
onKeystroke();
|
||||
|
||||
mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1630,6 +1726,10 @@ void LLLineEditor::draw()
|
|||
background.stretch( -mBorderThickness );
|
||||
|
||||
S32 lineeditor_v_pad = (background.getHeight() - mGLFont->getLineHeight()) / 2;
|
||||
if (mSpellCheck)
|
||||
{
|
||||
lineeditor_v_pad += 1;
|
||||
}
|
||||
|
||||
drawBackground();
|
||||
|
||||
|
|
@ -1704,14 +1804,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;
|
||||
}
|
||||
|
||||
|
|
@ -1755,7 +1855,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,
|
||||
|
|
@ -1769,7 +1869,7 @@ void LLLineEditor::draw()
|
|||
}
|
||||
else
|
||||
{
|
||||
mGLFont->render(
|
||||
rendered_text = mGLFont->render(
|
||||
mText, mScrollHPos,
|
||||
rendered_pixels_right, text_bottom,
|
||||
text_color,
|
||||
|
|
@ -1784,6 +1884,83 @@ 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())
|
||||
{
|
||||
|
|
@ -2115,6 +2292,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)
|
||||
{
|
||||
|
|
@ -2237,10 +2423,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
|
||||
|
|
@ -2392,7 +2577,34 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -226,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);
|
||||
|
||||
|
|
@ -325,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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,10 @@ void LLContextMenu::show(S32 x, S32 y)
|
|||
setRect(rect);
|
||||
arrange();
|
||||
|
||||
if (spawning_view)
|
||||
mSpawningViewHandle = spawning_view->getHandle();
|
||||
else
|
||||
mSpawningViewHandle.markDead();
|
||||
LLView::setVisible(TRUE);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,353 @@
|
|||
/**
|
||||
* @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_CUSTOM_SUFFIX = "_custom";
|
||||
static const std::string DICT_IGNORE_SUFFIX = "_ignore";
|
||||
|
||||
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_name)
|
||||
{
|
||||
for (LLSD::array_const_iterator it = sDictMap.beginArray(); it != sDictMap.endArray(); ++it)
|
||||
{
|
||||
const LLSD& dict_entry = *it;
|
||||
if (dict_name == dict_entry["language"].asString())
|
||||
return dict_entry;
|
||||
}
|
||||
return LLSD();
|
||||
}
|
||||
|
||||
// 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_map(user_path + "dictionaries.xml", std::ios::binary);
|
||||
if ( (!user_map.is_open()) || (0 == LLSDSerialize::fromXMLDocument(sDictMap, user_map)) || (0 == sDictMap.size()) )
|
||||
{
|
||||
llifstream app_map(app_path + "dictionaries.xml", std::ios::binary);
|
||||
if ( (!app_map.is_open()) || (0 == LLSDSerialize::fromXMLDocument(sDictMap, app_map)) || (0 == sDictMap.size()) )
|
||||
return;
|
||||
}
|
||||
|
||||
// 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")));
|
||||
sdDict["has_custom"] = (!tmp_user_path.empty()) && (gDirUtilp->fileExists(tmp_user_path + DICT_CUSTOM_SUFFIX + ".dic"));
|
||||
sdDict["has_ignore"] = (!tmp_user_path.empty()) && (gDirUtilp->fileExists(tmp_user_path + DICT_IGNORE_SUFFIX + ".dic"));
|
||||
}
|
||||
}
|
||||
|
||||
void LLSpellChecker::addToCustomDictionary(const std::string& word)
|
||||
{
|
||||
if (mHunspell)
|
||||
{
|
||||
mHunspell->add(word.c_str());
|
||||
}
|
||||
addToDictFile(getDictionaryUserPath() + mDictFile + DICT_CUSTOM_SUFFIX + ".dic", 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() + mDictFile + DICT_IGNORE_SUFFIX + ".dic", 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();
|
||||
}
|
||||
}
|
||||
|
||||
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_name = mDictName;
|
||||
initHunspell(dict_name);
|
||||
}
|
||||
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_name)
|
||||
{
|
||||
if (mHunspell)
|
||||
{
|
||||
delete mHunspell;
|
||||
mHunspell = NULL;
|
||||
mDictName.clear();
|
||||
mDictFile.clear();
|
||||
mIgnoreList.clear();
|
||||
}
|
||||
|
||||
const LLSD dict_entry = (!dict_name.empty()) ? getDictionaryData(dict_name) : 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;
|
||||
|
||||
mDictName = dict_name;
|
||||
mDictFile = dict_entry["name"].asString();
|
||||
|
||||
if (dict_entry["has_custom"].asBoolean())
|
||||
{
|
||||
const std::string filename_dic = user_path + mDictFile + DICT_CUSTOM_SUFFIX + ".dic";
|
||||
mHunspell->add_dic(filename_dic.c_str());
|
||||
}
|
||||
|
||||
if (dict_entry["has_ignore"].asBoolean())
|
||||
{
|
||||
llifstream file_in(user_path + mDictFile + DICT_IGNORE_SUFFIX + ".dic", 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
|
||||
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_name)
|
||||
{
|
||||
if ( (((dict_name.empty()) && (getUseSpellCheck())) || (!dict_name.empty())) &&
|
||||
(LLSpellChecker::instance().mDictName != dict_name) )
|
||||
{
|
||||
LLSpellChecker::instance().initHunspell(dict_name);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void LLSpellChecker::initClass()
|
||||
{
|
||||
if (sDictMap.isUndefined())
|
||||
refreshDictionaryMap();
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* @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_name);
|
||||
|
||||
public:
|
||||
typedef std::list<std::string> dict_list_t;
|
||||
|
||||
const std::string& getActiveDictionary() const { return mDictName; }
|
||||
const dict_list_t& getSecondaryDictionaries() const { return mDictSecondary; }
|
||||
void setSecondaryDictionaries(dict_list_t dict_list);
|
||||
|
||||
static const std::string getDictionaryAppPath();
|
||||
static const std::string getDictionaryUserPath();
|
||||
static const LLSD getDictionaryData(const std::string& dict_name);
|
||||
static const LLSD& getDictionaryMap() { return sDictMap; }
|
||||
static bool getUseSpellCheck();
|
||||
static void refreshDictionaryMap();
|
||||
static void setUseSpellCheck(const std::string& dict_name);
|
||||
|
||||
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 mDictName;
|
||||
std::string mDictFile;
|
||||
dict_list_t mDictSecondary;
|
||||
std::vector<std::string> mIgnoreList;
|
||||
|
||||
static LLSD sDictMap;
|
||||
static settings_change_signal_t sSettingsChangeSignal;
|
||||
};
|
||||
|
||||
#endif // LLSPELLCHECK_H
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -155,6 +156,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),
|
||||
|
|
@ -181,6 +183,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 ),
|
||||
|
|
@ -246,6 +251,12 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
|
|||
addChild(mDocumentView);
|
||||
}
|
||||
|
||||
if (mSpellCheck)
|
||||
{
|
||||
LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLTextBase::onSpellCheckSettingsChange, this));
|
||||
}
|
||||
mSpellCheckTimer.reset();
|
||||
|
||||
createDefaultSegment();
|
||||
|
||||
updateRects();
|
||||
|
|
@ -530,8 +541,86 @@ 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;
|
||||
|
|
@ -566,7 +655,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
|
||||
|
|
@ -578,6 +668,42 @@ 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();
|
||||
|
|
@ -1103,6 +1229,95 @@ 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()
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "v4color.h"
|
||||
#include "lleditmenuhandler.h"
|
||||
#include "llspellcheckmenuhandler.h"
|
||||
#include "llstyle.h"
|
||||
#include "llkeywords.h"
|
||||
#include "llpanel.h"
|
||||
|
|
@ -230,7 +231,8 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
|
|||
///
|
||||
class LLTextBase
|
||||
: public LLUICtrl,
|
||||
protected LLEditMenuHandler
|
||||
protected LLEditMenuHandler,
|
||||
public LLSpellCheckMenuHandler
|
||||
{
|
||||
public:
|
||||
friend class LLTextSegment;
|
||||
|
|
@ -259,6 +261,7 @@ public:
|
|||
border_visible,
|
||||
track_end,
|
||||
read_only,
|
||||
spellcheck,
|
||||
allow_scroll,
|
||||
plain_text,
|
||||
wrap,
|
||||
|
|
@ -311,6 +314,24 @@ public:
|
|||
/*virtual*/ BOOL canDeselect() const;
|
||||
/*virtual*/ void deselect();
|
||||
|
||||
// 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; }
|
||||
|
|
@ -540,6 +561,14 @@ protected:
|
|||
|
||||
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
|
||||
S32 mVPad; // padding above text
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
@ -1953,7 +1955,34 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2838,6 +2867,9 @@ void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& cal
|
|||
void LLTextEditor::onKeyStroke()
|
||||
{
|
||||
mKeystrokeSignal(this);
|
||||
|
||||
mSpellCheckStart = mSpellCheckEnd = -1;
|
||||
mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
|
||||
}
|
||||
|
||||
//virtual
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ include(EXPAT)
|
|||
include(FMOD)
|
||||
include(OPENAL)
|
||||
include(FindOpenGL)
|
||||
include(Hunspell)
|
||||
include(JsonCpp)
|
||||
include(LLAudio)
|
||||
include(LLCharacter)
|
||||
|
|
@ -72,6 +73,7 @@ include_directories(
|
|||
${LLLOGIN_INCLUDE_DIRS}
|
||||
${UPDATER_INCLUDE_DIRS}
|
||||
${LIBS_PREBUILT_DIR}/include/collada
|
||||
${LIBS_PREBUILD_DIR}/include/hunspell
|
||||
${OPENAL_LIB_INCLUDE_DIRS}
|
||||
${LIBS_PREBUILT_DIR}/include/collada/1.4
|
||||
)
|
||||
|
|
@ -236,6 +238,7 @@ set(viewer_SOURCE_FILES
|
|||
llfloatersidepanelcontainer.cpp
|
||||
llfloatersnapshot.cpp
|
||||
llfloatersounddevices.cpp
|
||||
llfloaterspellchecksettings.cpp
|
||||
llfloatertelehub.cpp
|
||||
llfloatertestinspectors.cpp
|
||||
llfloatertestlistview.cpp
|
||||
|
|
@ -794,6 +797,7 @@ set(viewer_HEADER_FILES
|
|||
llfloatersidepanelcontainer.h
|
||||
llfloatersnapshot.h
|
||||
llfloatersounddevices.h
|
||||
llfloaterspellchecksettings.h
|
||||
llfloatertelehub.h
|
||||
llfloatertestinspectors.h
|
||||
llfloatertestlistview.h
|
||||
|
|
@ -1576,6 +1580,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}/vivoxsdk.dll
|
||||
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ortp.dll
|
||||
|
|
@ -1755,6 +1762,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
|
|||
${LLMATH_LIBRARIES}
|
||||
${LLCOMMON_LIBRARIES}
|
||||
${NDOF_LIBRARY}
|
||||
${HUNSPELL_LIBRARY}
|
||||
${viewer_LIBRARIES}
|
||||
${BOOST_PROGRAM_OPTIONS_LIBRARY}
|
||||
${BOOST_REGEX_LIBRARY}
|
||||
|
|
|
|||
|
|
@ -12192,6 +12192,28 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@
|
|||
#include "llsecondlifeurls.h"
|
||||
#include "llupdaterservice.h"
|
||||
#include "llcallfloater.h"
|
||||
#include "llspellcheck.h"
|
||||
|
||||
// Linden library includes
|
||||
#include "llavatarnamecache.h"
|
||||
|
|
@ -114,6 +115,7 @@
|
|||
// Third party library includes
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
|
||||
|
||||
|
|
@ -2497,6 +2499,19 @@ bool LLAppViewer::initConfiguration()
|
|||
//gDirUtilp->setSkinFolder("default");
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -347,6 +347,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
|
|||
mCommitCallbackRegistrar.add("Pref.Proxy", boost::bind(&LLFloaterPreference::onClickProxySettings, this));
|
||||
mCommitCallbackRegistrar.add("Pref.TranslationSettings", boost::bind(&LLFloaterPreference::onClickTranslationSettings, this));
|
||||
mCommitCallbackRegistrar.add("Pref.AutoReplace", boost::bind(&LLFloaterPreference::onClickAutoReplace, this));
|
||||
mCommitCallbackRegistrar.add("Pref.SpellChecker", boost::bind(&LLFloaterPreference::onClickSpellChecker, this));
|
||||
|
||||
sSkin = gSavedSettings.getString("SkinCurrent");
|
||||
|
||||
|
|
@ -935,7 +936,6 @@ void LLFloaterPreference::refreshSkin(void* data)
|
|||
self->getChild<LLRadioGroup>("skin_selection", true)->setValue(sSkin);
|
||||
}
|
||||
|
||||
|
||||
void LLFloaterPreference::buildPopupLists()
|
||||
{
|
||||
LLScrollListCtrl& disabled_popups =
|
||||
|
|
@ -1524,6 +1524,11 @@ void LLFloaterPreference::onClickAutoReplace()
|
|||
LLFloaterReg::showInstance("prefs_autoreplace");
|
||||
}
|
||||
|
||||
void LLFloaterPreference::onClickSpellChecker()
|
||||
{
|
||||
LLFloaterReg::showInstance("prefs_spellchecker");
|
||||
}
|
||||
|
||||
void LLFloaterPreference::onClickActionChange()
|
||||
{
|
||||
mClickActionDirty = true;
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ public:
|
|||
void onClickProxySettings();
|
||||
void onClickTranslationSettings();
|
||||
void onClickAutoReplace();
|
||||
void onClickSpellChecker();
|
||||
void applyUIColor(LLUICtrl* ctrl, const LLSD& param);
|
||||
void getUIColor(LLUICtrl* ctrl, const LLSD& param);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* @file llfloaterspellchecksettings.h
|
||||
* @brief Spell checker settings floater
|
||||
*
|
||||
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
|
||||
#include "llcombobox.h"
|
||||
#include "llfloaterspellchecksettings.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
#include "llspellcheck.h"
|
||||
#include "llviewercontrol.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
LLFloaterSpellCheckerSettings::LLFloaterSpellCheckerSettings(const LLSD& key)
|
||||
: LLFloater(key)
|
||||
{
|
||||
}
|
||||
|
||||
BOOL LLFloaterSpellCheckerSettings::postBuild(void)
|
||||
{
|
||||
gSavedSettings.getControl("SpellCheck")->getSignal()->connect(boost::bind(&LLFloaterSpellCheckerSettings::refreshDictionaryLists, this, false));
|
||||
getChild<LLUICtrl>("spellcheck_main_combo")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::refreshDictionaryLists, this, false));
|
||||
getChild<LLUICtrl>("spellcheck_moveleft_btn")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onClickDictMove, this, "spellcheck_active_list", "spellcheck_available_list"));
|
||||
getChild<LLUICtrl>("spellcheck_moveright_btn")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onClickDictMove, this, "spellcheck_available_list", "spellcheck_active_list"));
|
||||
getChild<LLUICtrl>("spellcheck_ok")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onOK, this));
|
||||
getChild<LLUICtrl>("spellcheck_cancel")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onCancel, this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LLFloaterSpellCheckerSettings::onCancel()
|
||||
{
|
||||
closeFloater(false);
|
||||
}
|
||||
|
||||
void LLFloaterSpellCheckerSettings::onClickDictMove(const std::string& from, const std::string& to)
|
||||
{
|
||||
LLScrollListCtrl* from_ctrl = findChild<LLScrollListCtrl>(from);
|
||||
LLScrollListCtrl* to_ctrl = findChild<LLScrollListCtrl>(to);
|
||||
|
||||
LLSD row;
|
||||
row["columns"][0]["column"] = "name";
|
||||
row["columns"][0]["font"]["name"] = "SANSSERIF_SMALL";
|
||||
row["columns"][0]["font"]["style"] = "NORMAL";
|
||||
|
||||
std::vector<LLScrollListItem*> sel_items = from_ctrl->getAllSelected();
|
||||
for (std::vector<LLScrollListItem*>::const_iterator sel_it = sel_items.begin(); sel_it != sel_items.end(); ++sel_it)
|
||||
{
|
||||
row["columns"][0]["value"] = (*sel_it)->getColumn(0)->getValue();
|
||||
to_ctrl->addElement(row);
|
||||
}
|
||||
from_ctrl->deleteSelectedItems();
|
||||
}
|
||||
|
||||
void LLFloaterSpellCheckerSettings::onOK()
|
||||
{
|
||||
std::list<std::string> list_dict;
|
||||
|
||||
LLComboBox* dict_combo = findChild<LLComboBox>("spellcheck_main_combo");
|
||||
const std::string dict_name = dict_combo->getSelectedItemLabel();
|
||||
if (!dict_name.empty())
|
||||
{
|
||||
list_dict.push_back(dict_name);
|
||||
|
||||
LLScrollListCtrl* list_ctrl = findChild<LLScrollListCtrl>("spellcheck_active_list");
|
||||
std::vector<LLScrollListItem*> list_items = list_ctrl->getAllData();
|
||||
for (std::vector<LLScrollListItem*>::const_iterator item_it = list_items.begin(); item_it != list_items.end(); ++item_it)
|
||||
list_dict.push_back((*item_it)->getColumn(0)->getValue().asString());
|
||||
}
|
||||
gSavedSettings.setString("SpellCheckDictionary", boost::join(list_dict, ","));
|
||||
|
||||
closeFloater(false);
|
||||
}
|
||||
|
||||
void LLFloaterSpellCheckerSettings::onOpen(const LLSD& key)
|
||||
{
|
||||
refreshDictionaryLists(true);
|
||||
}
|
||||
|
||||
void LLFloaterSpellCheckerSettings::refreshDictionaryLists(bool from_settings)
|
||||
{
|
||||
bool enabled = gSavedSettings.getBOOL("SpellCheck");
|
||||
getChild<LLUICtrl>("spellcheck_moveleft_btn")->setEnabled(enabled);
|
||||
getChild<LLUICtrl>("spellcheck_moveright_btn")->setEnabled(enabled);
|
||||
|
||||
// Populate the dictionary combobox
|
||||
LLComboBox* dict_combo = findChild<LLComboBox>("spellcheck_main_combo");
|
||||
std::string dict_cur = dict_combo->getSelectedItemLabel();
|
||||
if ((dict_cur.empty() || from_settings) && (LLSpellChecker::getUseSpellCheck()))
|
||||
dict_cur = LLSpellChecker::instance().getActiveDictionary();
|
||||
dict_combo->clearRows();
|
||||
dict_combo->setEnabled(enabled);
|
||||
|
||||
const LLSD& dict_map = LLSpellChecker::getDictionaryMap();
|
||||
if (dict_map.size())
|
||||
{
|
||||
for (LLSD::array_const_iterator dict_it = dict_map.beginArray(); dict_it != dict_map.endArray(); ++dict_it)
|
||||
{
|
||||
const LLSD& dict = *dict_it;
|
||||
if ( (dict["installed"].asBoolean()) && (dict["is_primary"].asBoolean()) && (dict.has("language")) )
|
||||
dict_combo->add(dict["language"].asString());
|
||||
}
|
||||
if (!dict_combo->selectByValue(dict_cur))
|
||||
dict_combo->clear();
|
||||
}
|
||||
|
||||
// Populate the available and active dictionary list
|
||||
LLScrollListCtrl* avail_ctrl = findChild<LLScrollListCtrl>("spellcheck_available_list");
|
||||
LLScrollListCtrl* active_ctrl = findChild<LLScrollListCtrl>("spellcheck_active_list");
|
||||
|
||||
LLSpellChecker::dict_list_t active_list;
|
||||
if ( ((!avail_ctrl->getItemCount()) && (!active_ctrl->getItemCount())) || (from_settings) )
|
||||
{
|
||||
if (LLSpellChecker::getUseSpellCheck())
|
||||
active_list = LLSpellChecker::instance().getSecondaryDictionaries();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<LLScrollListItem*> active_items = active_ctrl->getAllData();
|
||||
for (std::vector<LLScrollListItem*>::const_iterator item_it = active_items.begin(); item_it != active_items.end(); ++item_it)
|
||||
{
|
||||
std::string dict = (*item_it)->getColumn(0)->getValue().asString();
|
||||
if (dict_cur != dict)
|
||||
active_list.push_back(dict);
|
||||
}
|
||||
}
|
||||
|
||||
LLSD row;
|
||||
row["columns"][0]["column"] = "name";
|
||||
row["columns"][0]["font"]["name"] = "SANSSERIF_SMALL";
|
||||
row["columns"][0]["font"]["style"] = "NORMAL";
|
||||
|
||||
active_ctrl->clearRows();
|
||||
active_ctrl->setEnabled(enabled);
|
||||
active_ctrl->sortByColumnIndex(0, true);
|
||||
for (LLSpellChecker::dict_list_t::const_iterator it = active_list.begin(); it != active_list.end(); ++it)
|
||||
{
|
||||
row["columns"][0]["value"] = *it;
|
||||
active_ctrl->addElement(row);
|
||||
}
|
||||
active_list.push_back(dict_cur);
|
||||
|
||||
avail_ctrl->clearRows();
|
||||
avail_ctrl->setEnabled(enabled);
|
||||
avail_ctrl->sortByColumnIndex(0, true);
|
||||
for (LLSD::array_const_iterator dict_it = dict_map.beginArray(); dict_it != dict_map.endArray(); ++dict_it)
|
||||
{
|
||||
const LLSD& dict = *dict_it;
|
||||
if ( (dict["installed"].asBoolean()) && (dict.has("language")) &&
|
||||
(active_list.end() == std::find(active_list.begin(), active_list.end(), dict["language"].asString())) )
|
||||
{
|
||||
row["columns"][0]["value"] = dict["language"].asString();
|
||||
avail_ctrl->addElement(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* @file llfloaterspellchecksettings.h
|
||||
* @brief Spell checker settings floater
|
||||
*
|
||||
* $LicenseInfo:firstyear=2011&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 LLFLOATERAUTOREPLACESETTINGS_H
|
||||
#define LLFLOATERAUTOREPLACESETTINGS_H
|
||||
|
||||
#include "llfloater.h"
|
||||
|
||||
class LLFloaterSpellCheckerSettings : public LLFloater
|
||||
{
|
||||
public:
|
||||
LLFloaterSpellCheckerSettings(const LLSD& key);
|
||||
|
||||
/*virtual*/ BOOL postBuild();
|
||||
/*virtual*/ void onOpen(const LLSD& key);
|
||||
|
||||
protected:
|
||||
void onCancel();
|
||||
void onClickDictMove(const std::string& from, const std::string& to);
|
||||
void onOK();
|
||||
void onSave();
|
||||
void refreshDictionaryLists(bool from_settings);
|
||||
};
|
||||
|
||||
#endif // LLFLOATERAUTOREPLACESETTINGS_H
|
||||
|
|
@ -739,6 +739,7 @@ bool idle_startup()
|
|||
{
|
||||
display_startup();
|
||||
initialize_edit_menu();
|
||||
initialize_spellcheck_menu();
|
||||
display_startup();
|
||||
init_menus();
|
||||
display_startup();
|
||||
|
|
|
|||
|
|
@ -71,8 +71,12 @@
|
|||
#include "llpaneloutfitsinventory.h"
|
||||
#include "llpanellogin.h"
|
||||
#include "llpaneltopinfobar.h"
|
||||
#include "llspellcheck.h"
|
||||
#include "llupdaterservice.h"
|
||||
|
||||
// Third party library includes
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
|
||||
BOOL gHackGodmode = FALSE;
|
||||
#endif
|
||||
|
|
@ -508,6 +512,25 @@ bool handleForceShowGrid(const LLSD& newvalue)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool handleSpellCheckChanged()
|
||||
{
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LLSpellChecker::setUseSpellCheck(LLStringUtil::null);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool toggle_agent_pause(const LLSD& newvalue)
|
||||
{
|
||||
if ( newvalue.asBoolean() )
|
||||
|
|
@ -714,6 +737,8 @@ void settings_setup_listeners()
|
|||
gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(boost::bind(&toggle_updater_service_active, _2));
|
||||
gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2));
|
||||
gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2));
|
||||
gSavedSettings.getControl("SpellCheck")->getSignal()->connect(boost::bind(&handleSpellCheckChanged));
|
||||
gSavedSettings.getControl("SpellCheckDictionary")->getSignal()->connect(boost::bind(&handleSpellCheckChanged));
|
||||
}
|
||||
|
||||
#if TEST_CACHED_CONTROL
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@
|
|||
#include "llfloatersidepanelcontainer.h"
|
||||
#include "llfloatersnapshot.h"
|
||||
#include "llfloatersounddevices.h"
|
||||
#include "llfloaterspellchecksettings.h"
|
||||
#include "llfloatertelehub.h"
|
||||
#include "llfloatertestinspectors.h"
|
||||
#include "llfloatertestlistview.h"
|
||||
|
|
@ -248,6 +249,7 @@ void LLViewerFloaterReg::registerFloaters()
|
|||
LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreference>);
|
||||
LLFloaterReg::add("prefs_proxy", "floater_preferences_proxy.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreferenceProxy>);
|
||||
LLFloaterReg::add("prefs_hardware_settings", "floater_hardware_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHardwareSettings>);
|
||||
LLFloaterReg::add("prefs_spellchecker", "floater_spellcheck.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSpellCheckerSettings>);
|
||||
LLFloaterReg::add("prefs_translation", "floater_translation_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTranslationSettings>);
|
||||
LLFloaterReg::add("prefs_autoreplace", "floater_autoreplace.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAutoReplaceSettings>);
|
||||
LLFloaterReg::add("perm_prefs", "floater_perm_prefs.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPerms>);
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@
|
|||
#include "llrootview.h"
|
||||
#include "llsceneview.h"
|
||||
#include "llselectmgr.h"
|
||||
#include "llspellcheckmenuhandler.h"
|
||||
#include "llstatusbar.h"
|
||||
#include "lltextureview.h"
|
||||
#include "lltoolcomp.h"
|
||||
|
|
@ -5053,6 +5054,78 @@ class LLEditDelete : public view_listener_t
|
|||
}
|
||||
};
|
||||
|
||||
void handle_spellcheck_replace_with_suggestion(const LLUICtrl* ctrl, const LLSD& param)
|
||||
{
|
||||
const LLContextMenu* menu = dynamic_cast<const LLContextMenu*>(ctrl->getParent());
|
||||
LLSpellCheckMenuHandler* spellcheck_handler = (menu) ? dynamic_cast<LLSpellCheckMenuHandler*>(menu->getSpawningView()) : NULL;
|
||||
if ( (!spellcheck_handler) || (!spellcheck_handler->getSpellCheck()) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
U32 index = 0;
|
||||
if ( (!LLStringUtil::convertToU32(param.asString(), index)) || (index >= spellcheck_handler->getSuggestionCount()) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
spellcheck_handler->replaceWithSuggestion(index);
|
||||
}
|
||||
|
||||
bool visible_spellcheck_suggestion(LLUICtrl* ctrl, const LLSD& param)
|
||||
{
|
||||
LLMenuItemGL* item = dynamic_cast<LLMenuItemGL*>(ctrl);
|
||||
const LLContextMenu* menu = (item) ? dynamic_cast<const LLContextMenu*>(item->getParent()) : NULL;
|
||||
const LLSpellCheckMenuHandler* spellcheck_handler = (menu) ? dynamic_cast<const LLSpellCheckMenuHandler*>(menu->getSpawningView()) : NULL;
|
||||
if ( (!spellcheck_handler) || (!spellcheck_handler->getSpellCheck()) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 index = 0;
|
||||
if ( (!LLStringUtil::convertToU32(param.asString(), index)) || (index >= spellcheck_handler->getSuggestionCount()) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
item->setLabel(spellcheck_handler->getSuggestion(index));
|
||||
return true;
|
||||
}
|
||||
|
||||
void handle_spellcheck_add_to_dictionary(const LLUICtrl* ctrl)
|
||||
{
|
||||
const LLContextMenu* menu = dynamic_cast<const LLContextMenu*>(ctrl->getParent());
|
||||
LLSpellCheckMenuHandler* spellcheck_handler = (menu) ? dynamic_cast<LLSpellCheckMenuHandler*>(menu->getSpawningView()) : NULL;
|
||||
if ( (spellcheck_handler) && (spellcheck_handler->canAddToDictionary()) )
|
||||
{
|
||||
spellcheck_handler->addToDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
bool enable_spellcheck_add_to_dictionary(const LLUICtrl* ctrl)
|
||||
{
|
||||
const LLContextMenu* menu = dynamic_cast<const LLContextMenu*>(ctrl->getParent());
|
||||
const LLSpellCheckMenuHandler* spellcheck_handler = (menu) ? dynamic_cast<const LLSpellCheckMenuHandler*>(menu->getSpawningView()) : NULL;
|
||||
return (spellcheck_handler) && (spellcheck_handler->canAddToDictionary());
|
||||
}
|
||||
|
||||
void handle_spellcheck_add_to_ignore(const LLUICtrl* ctrl)
|
||||
{
|
||||
const LLContextMenu* menu = dynamic_cast<const LLContextMenu*>(ctrl->getParent());
|
||||
LLSpellCheckMenuHandler* spellcheck_handler = (menu) ? dynamic_cast<LLSpellCheckMenuHandler*>(menu->getSpawningView()) : NULL;
|
||||
if ( (spellcheck_handler) && (spellcheck_handler->canAddToIgnore()) )
|
||||
{
|
||||
spellcheck_handler->addToIgnore();
|
||||
}
|
||||
}
|
||||
|
||||
bool enable_spellcheck_add_to_ignore(const LLUICtrl* ctrl)
|
||||
{
|
||||
const LLContextMenu* menu = dynamic_cast<const LLContextMenu*>(ctrl->getParent());
|
||||
const LLSpellCheckMenuHandler* spellcheck_handler = (menu) ? dynamic_cast<const LLSpellCheckMenuHandler*>(menu->getSpawningView()) : NULL;
|
||||
return (spellcheck_handler) && (spellcheck_handler->canAddToIgnore());
|
||||
}
|
||||
|
||||
bool enable_object_delete()
|
||||
{
|
||||
bool new_value =
|
||||
|
|
@ -8003,6 +8076,19 @@ void initialize_edit_menu()
|
|||
|
||||
}
|
||||
|
||||
void initialize_spellcheck_menu()
|
||||
{
|
||||
LLUICtrl::CommitCallbackRegistry::Registrar& commit = LLUICtrl::CommitCallbackRegistry::currentRegistrar();
|
||||
LLUICtrl::EnableCallbackRegistry::Registrar& enable = LLUICtrl::EnableCallbackRegistry::currentRegistrar();
|
||||
|
||||
commit.add("SpellCheck.ReplaceWithSuggestion", boost::bind(&handle_spellcheck_replace_with_suggestion, _1, _2));
|
||||
enable.add("SpellCheck.VisibleSuggestion", boost::bind(&visible_spellcheck_suggestion, _1, _2));
|
||||
commit.add("SpellCheck.AddToDictionary", boost::bind(&handle_spellcheck_add_to_dictionary, _1));
|
||||
enable.add("SpellCheck.EnableAddToDictionary", boost::bind(&enable_spellcheck_add_to_dictionary, _1));
|
||||
commit.add("SpellCheck.AddToIgnore", boost::bind(&handle_spellcheck_add_to_ignore, _1));
|
||||
enable.add("SpellCheck.EnableAddToIgnore", boost::bind(&enable_spellcheck_add_to_ignore, _1));
|
||||
}
|
||||
|
||||
void initialize_menus()
|
||||
{
|
||||
// A parameterized event handler used as ctrl-8/9/0 zoom controls below.
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class LLObjectSelection;
|
|||
class LLSelectNode;
|
||||
|
||||
void initialize_edit_menu();
|
||||
void initialize_spellcheck_menu();
|
||||
void init_menus();
|
||||
void cleanup_menus();
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ with the same filename but different name
|
|||
|
||||
<texture name="Arrow_Down" file_name="widgets/Arrow_Down.png" preload="true" />
|
||||
<texture name="Arrow_Up" file_name="widgets/Arrow_Up.png" preload="true" />
|
||||
<texture name="Arrow_Left" file_name="widgets/Arrow_Left.png" preload="true" />
|
||||
<texture name="Arrow_Right" file_name="widgets/Arrow_Right.png" preload="true" />
|
||||
|
||||
<texture name="AudioMute_Off" file_name="icons/AudioMute_Off.png" preload="false" />
|
||||
<texture name="AudioMute_Over" file_name="icons/AudioMute_Over.png" preload="false" />
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 311 B |
Binary file not shown.
|
After Width: | Height: | Size: 313 B |
|
|
@ -46,6 +46,7 @@
|
|||
left="0"
|
||||
max_length_bytes="1023"
|
||||
name="chat_box"
|
||||
spellcheck="true"
|
||||
text_pad_left="5"
|
||||
text_pad_right="25"
|
||||
tool_tip="Press Enter to say, Ctrl+Enter to shout"
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
label="To"
|
||||
layout="bottomleft"
|
||||
name="chat_editor"
|
||||
spellcheck="true"
|
||||
tab_group="3"
|
||||
width="249">
|
||||
</line_editor>
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@
|
|||
max_length="65536"
|
||||
name="Notecard Editor"
|
||||
parse_urls="false"
|
||||
spellcheck="true"
|
||||
tab_group="1"
|
||||
top="46"
|
||||
width="392"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<floater
|
||||
border="true"
|
||||
can_close="true"
|
||||
can_minimize="true"
|
||||
bottom="275"
|
||||
left="300"
|
||||
can_resize="false"
|
||||
height="330"
|
||||
width="490"
|
||||
name="spellcheck_floater"
|
||||
title="Spell Checker Settings">
|
||||
<check_box
|
||||
bottom_delta="30"
|
||||
control_name="SpellCheck"
|
||||
left_delta="15"
|
||||
height="16"
|
||||
width="100"
|
||||
follows="left|top"
|
||||
label="Enable spell checker"
|
||||
name="spellcheck_enable" />
|
||||
<view_border
|
||||
top_pad="10"
|
||||
left="2"
|
||||
height="0"
|
||||
width="491"
|
||||
follows="left|top"
|
||||
bevel_style="none"
|
||||
border_thickness="1"
|
||||
mouse_opaque="false"
|
||||
name="divisor1"/>
|
||||
<text
|
||||
enabled_control="SpellCheck"
|
||||
follows="top|left"
|
||||
height="10"
|
||||
layout="topleft"
|
||||
left="38"
|
||||
mouse_opaque="false"
|
||||
name="spellcheck_main"
|
||||
top_pad="15"
|
||||
type="string"
|
||||
width="90"
|
||||
>
|
||||
Main dictionary :
|
||||
</text>
|
||||
<combo_box
|
||||
enabled_control="SpellCheck"
|
||||
follows="top|left"
|
||||
height="23"
|
||||
layout="topleft"
|
||||
left_pad="10"
|
||||
name="spellcheck_main_combo"
|
||||
top_pad="-15"
|
||||
width="175"
|
||||
/>
|
||||
<text
|
||||
enabled_control="SpellCheck"
|
||||
follows="top|left"
|
||||
height="10"
|
||||
label="Logs:"
|
||||
layout="topleft"
|
||||
left="38"
|
||||
mouse_opaque="false"
|
||||
name="spellcheck_additional"
|
||||
top_pad="15"
|
||||
type="string"
|
||||
width="190"
|
||||
>
|
||||
Additional dictionaries :
|
||||
</text>
|
||||
<text
|
||||
follows="top|left"
|
||||
height="12"
|
||||
layout="topleft"
|
||||
left="55"
|
||||
length="1"
|
||||
name="spellcheck_available"
|
||||
top_pad="10"
|
||||
type="string"
|
||||
width="175">
|
||||
Available
|
||||
</text>
|
||||
<text
|
||||
follows="top|left"
|
||||
height="12"
|
||||
type="string"
|
||||
left_pad="45"
|
||||
length="1"
|
||||
layout="topleft"
|
||||
name="spellcheck_active"
|
||||
width="175">
|
||||
Active
|
||||
</text>
|
||||
<scroll_list
|
||||
enabled_control="SpellCheck"
|
||||
follows="top|left"
|
||||
height="155"
|
||||
layout="topleft"
|
||||
left="55"
|
||||
multi_select="true"
|
||||
name="spellcheck_available_list"
|
||||
sort_column="0"
|
||||
sort_ascending="true"
|
||||
width="175" />
|
||||
<button
|
||||
enabled_control="SpellCheck"
|
||||
follows="top|left"
|
||||
height="26"
|
||||
image_overlay="Arrow_Right"
|
||||
hover_glow_amount="0.15"
|
||||
layout="topleft"
|
||||
left_pad="10"
|
||||
name="spellcheck_moveright_btn"
|
||||
top_delta="50"
|
||||
width="25">
|
||||
</button>
|
||||
<button
|
||||
enabled_control="SpellCheck"
|
||||
follows="top|left"
|
||||
height="26"
|
||||
image_overlay="Arrow_Left"
|
||||
hover_glow_amount="0.15"
|
||||
layout="topleft"
|
||||
name="spellcheck_moveleft_btn"
|
||||
top_delta="30"
|
||||
width="25">
|
||||
</button>
|
||||
<scroll_list
|
||||
enabled_control="SpellCheck"
|
||||
follows="top|left"
|
||||
height="155"
|
||||
layout="topleft"
|
||||
left_pad="10"
|
||||
multi_select="true"
|
||||
name="spellcheck_active_list"
|
||||
sort_column="0"
|
||||
sort_ascending="true"
|
||||
top_pad="-105"
|
||||
width="175"
|
||||
/>
|
||||
<view_border
|
||||
top_pad="10"
|
||||
left="2"
|
||||
height="0"
|
||||
width="491"
|
||||
follows="left|top"
|
||||
bevel_style="none"
|
||||
border_thickness="1"
|
||||
mouse_opaque="false"
|
||||
name="divisor4"/>
|
||||
<button
|
||||
top_pad="10"
|
||||
right="380"
|
||||
height="22"
|
||||
width="90"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="spellcheck_ok"
|
||||
label="OK" />
|
||||
<button
|
||||
top_delta="0"
|
||||
right="480"
|
||||
height="22"
|
||||
width="90"
|
||||
enabled="true"
|
||||
follows="left|top"
|
||||
mouse_opaque="true"
|
||||
halign="center"
|
||||
scale_image="true"
|
||||
name="spellcheck_cancel"
|
||||
label="Cancel" />
|
||||
</floater>
|
||||
|
|
@ -1,6 +1,85 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<context_menu
|
||||
name="Text editor context menu">
|
||||
<menu_item_call
|
||||
label="(unknown)"
|
||||
layout="topleft"
|
||||
name="Suggestion 1">
|
||||
<menu_item_call.on_click
|
||||
function="SpellCheck.ReplaceWithSuggestion"
|
||||
parameter="0" />
|
||||
<menu_item_call.on_visible
|
||||
function="SpellCheck.VisibleSuggestion"
|
||||
parameter="0" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="(unknown)"
|
||||
layout="topleft"
|
||||
name="Suggestion 2">
|
||||
<menu_item_call.on_click
|
||||
function="SpellCheck.ReplaceWithSuggestion"
|
||||
parameter="1" />
|
||||
<menu_item_call.on_visible
|
||||
function="SpellCheck.VisibleSuggestion"
|
||||
parameter="1" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="(unknown)"
|
||||
layout="topleft"
|
||||
name="Suggestion 3">
|
||||
<menu_item_call.on_click
|
||||
function="SpellCheck.ReplaceWithSuggestion"
|
||||
parameter="2" />
|
||||
<menu_item_call.on_visible
|
||||
function="SpellCheck.VisibleSuggestion"
|
||||
parameter="2" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="(unknown)"
|
||||
layout="topleft"
|
||||
name="Suggestion 4">
|
||||
<menu_item_call.on_click
|
||||
function="SpellCheck.ReplaceWithSuggestion"
|
||||
parameter="3" />
|
||||
<menu_item_call.on_visible
|
||||
function="SpellCheck.VisibleSuggestion"
|
||||
parameter="3" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="(unknown)"
|
||||
layout="topleft"
|
||||
name="Suggestion 5">
|
||||
<menu_item_call.on_click
|
||||
function="SpellCheck.ReplaceWithSuggestion"
|
||||
parameter="4" />
|
||||
<menu_item_call.on_visible
|
||||
function="SpellCheck.VisibleSuggestion"
|
||||
parameter="4" />
|
||||
</menu_item_call>
|
||||
<menu_item_separator
|
||||
layout="topleft"
|
||||
name="Suggestion Separator" />
|
||||
<menu_item_call
|
||||
label="Add to Dictionary"
|
||||
layout="topleft"
|
||||
name="Add to Dictionary">
|
||||
<menu_item_call.on_click
|
||||
function="SpellCheck.AddToDictionary" />
|
||||
<menu_item_call.on_enable
|
||||
function="SpellCheck.EnableAddToDictionary" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="Add to Ignore"
|
||||
layout="topleft"
|
||||
name="Add to Ignore">
|
||||
<menu_item_call.on_click
|
||||
function="SpellCheck.AddToIgnore" />
|
||||
<menu_item_call.on_enable
|
||||
function="SpellCheck.EnableAddToIgnore" />
|
||||
</menu_item_call>
|
||||
<menu_item_separator
|
||||
layout="topleft"
|
||||
name="Spellcheck Separator" />
|
||||
<menu_item_call
|
||||
label="Cut"
|
||||
layout="topleft"
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@
|
|||
top_pad="2"
|
||||
max_length="1023"
|
||||
name="pick_desc"
|
||||
spellcheck="true"
|
||||
text_color="black"
|
||||
word_wrap="true" />
|
||||
<text
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ Maximum 200 per group daily
|
|||
max_length_bytes="63"
|
||||
name="create_subject"
|
||||
prevalidate_callback="ascii"
|
||||
spellcheck="true"
|
||||
width="218" />
|
||||
<text
|
||||
follows="left|top"
|
||||
|
|
@ -161,6 +162,7 @@ Maximum 200 per group daily
|
|||
left_pad="3"
|
||||
max_length="511"
|
||||
name="create_message"
|
||||
spellcheck="true"
|
||||
top_delta="0"
|
||||
width="218"
|
||||
word_wrap="true" />
|
||||
|
|
@ -309,6 +311,7 @@ Maximum 200 per group daily
|
|||
left_pad="3"
|
||||
max_length_bytes="63"
|
||||
name="view_subject"
|
||||
spellcheck="true"
|
||||
top_delta="-1"
|
||||
visible="false"
|
||||
width="200" />
|
||||
|
|
@ -333,6 +336,7 @@ Maximum 200 per group daily
|
|||
right="-1"
|
||||
max_length="511"
|
||||
name="view_message"
|
||||
spellcheck="true"
|
||||
top_delta="-40"
|
||||
width="313"
|
||||
word_wrap="true" />
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
left="0"
|
||||
max_length_bytes="1023"
|
||||
name="chat_box"
|
||||
spellcheck="true"
|
||||
text_pad_left="5"
|
||||
text_pad_right="25"
|
||||
tool_tip="Press Enter to say, Ctrl+Enter to shout"
|
||||
|
|
|
|||
|
|
@ -227,4 +227,16 @@
|
|||
label="AutoReplace Settings"
|
||||
width="150">
|
||||
</button>
|
||||
<button
|
||||
follows="top|left"
|
||||
height="23"
|
||||
layout="topleft"
|
||||
top_pad="-23"
|
||||
left_pad="5"
|
||||
name="spellcheck_showgui"
|
||||
commit_callback.function="Pref.SpellChecker"
|
||||
label="Spell Checker Settings"
|
||||
width="150">
|
||||
</button>
|
||||
|
||||
</panel>
|
||||
|
|
|
|||
|
|
@ -91,6 +91,13 @@ class ViewerManifest(LLManifest):
|
|||
|
||||
# ... and the entire windlight directory
|
||||
self.path("windlight")
|
||||
|
||||
# ... and the included spell checking dictionaries
|
||||
pkgdir = os.path.join(self.args['build'], os.pardir, 'packages')
|
||||
if self.prefix(src=pkgdir,dst=""):
|
||||
self.path("dictionaries")
|
||||
self.end_prefix(pkgdir)
|
||||
|
||||
self.end_prefix("app_settings")
|
||||
|
||||
if self.prefix(src="character"):
|
||||
|
|
@ -393,6 +400,9 @@ class WindowsManifest(ViewerManifest):
|
|||
self.path("ssleay32.dll")
|
||||
self.path("libeay32.dll")
|
||||
|
||||
# Hunspell
|
||||
self.path("libhunspell.dll")
|
||||
|
||||
# For google-perftools tcmalloc allocator.
|
||||
try:
|
||||
if self.args['configuration'].lower() == 'debug':
|
||||
|
|
@ -659,6 +669,7 @@ class DarwinManifest(ViewerManifest):
|
|||
|
||||
# copy additional libs in <bundle>/Contents/MacOS/
|
||||
self.path("../packages/lib/release/libndofdev.dylib", dst="Resources/libndofdev.dylib")
|
||||
self.path("../packages/lib/release/libhunspell-1.3.0.dylib", dst="Resources/libhunspell-1.3.0.dylib")
|
||||
|
||||
self.path("../viewer_components/updater/scripts/darwin/update_install", "MacOS/update_install")
|
||||
|
||||
|
|
@ -1057,6 +1068,8 @@ class Linux_i686Manifest(LinuxManifest):
|
|||
self.path("libopenjpeg.so.1.4.0")
|
||||
self.path("libopenjpeg.so.1")
|
||||
self.path("libopenjpeg.so")
|
||||
self.path("libhunspell-1.3.so")
|
||||
self.path("libhunspell-1.3.so.0")
|
||||
self.path("libalut.so")
|
||||
self.path("libopenal.so", "libopenal.so.1")
|
||||
self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname
|
||||
|
|
|
|||
Loading…
Reference in New Issue