diff options
| author | Kitty Barnett <develop@catznip.com> | 2012-01-20 18:06:32 +0100 | 
|---|---|---|
| committer | Kitty Barnett <develop@catznip.com> | 2012-01-20 18:06:32 +0100 | 
| commit | 9c66ac87fd46db3987e60ae50989b2497099480b (patch) | |
| tree | 0b2e6d98f3790ee2c1394017567ef75b9cd426ab /indra | |
| parent | 4c5141c5677a2e98c1331026d4e119abee6ab2ae (diff) | |
STORM-276 Basic spellchecking framework
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/cmake/Copy3rdPartyLibs.cmake | 3 | ||||
| -rw-r--r-- | indra/cmake/ViewerMiscLibs.cmake | 1 | ||||
| -rw-r--r-- | indra/llui/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | indra/llui/llmenugl.cpp | 6 | ||||
| -rw-r--r-- | indra/llui/llmenugl.h | 6 | ||||
| -rw-r--r-- | indra/llui/llspellcheck.cpp | 345 | ||||
| -rw-r--r-- | indra/llui/llspellcheck.h | 77 | ||||
| -rw-r--r-- | indra/llui/llspellcheckmenuhandler.h | 46 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 22 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 14 | ||||
| -rw-r--r-- | indra/newview/llstartup.cpp | 1 | ||||
| -rw-r--r-- | indra/newview/llviewercontrol.cpp | 24 | ||||
| -rw-r--r-- | indra/newview/llviewermenu.cpp | 86 | ||||
| -rw-r--r-- | indra/newview/llviewermenu.h | 1 | ||||
| -rw-r--r-- | indra/newview/viewer_manifest.py | 8 | 
15 files changed, 641 insertions, 2 deletions
| diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 394db362b1..ebeae3e5be 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.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) @@ -215,6 +217,7 @@ elseif(DARWIN)      libllqtwebkit.dylib      libminizip.a          libndofdev.dylib +        libhunspell-1.3.dylib          libexception_handler.dylib      libcollada14dom.dylib         ) diff --git a/indra/cmake/ViewerMiscLibs.cmake b/indra/cmake/ViewerMiscLibs.cmake index df013b1665..f907181929 100644 --- a/indra/cmake/ViewerMiscLibs.cmake +++ b/indra/cmake/ViewerMiscLibs.cmake @@ -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) diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 772f173f17..1377336bb4 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -87,6 +87,7 @@ set(llui_SOURCE_FILES      llsearcheditor.cpp      llslider.cpp      llsliderctrl.cpp +    llspellcheck.cpp      llspinctrl.cpp      llstatbar.cpp      llstatgraph.cpp @@ -192,6 +193,8 @@ set(llui_HEADER_FILES      llsdparam.h      llsliderctrl.h      llslider.h +    llspellcheck.h +    llspellcheckmenuhandler.h      llspinctrl.h      llstatbar.h      llstatgraph.h diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 95ecbb1c94..2a65262bbb 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -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);  } diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 36f3ba34b9..67b3e1fbe6 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -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;  }; diff --git a/indra/llui/llspellcheck.cpp b/indra/llui/llspellcheck.cpp new file mode 100644 index 0000000000..433ca02852 --- /dev/null +++ b/indra/llui/llspellcheck.cpp @@ -0,0 +1,345 @@ +/**  + * @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"; + +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(); +} + +const LLSD LLSpellChecker::getDictionaryData(const std::string& dict_name) const +{ +	for (LLSD::array_const_iterator it = mDictMap.beginArray(); it != mDictMap.endArray(); ++it) +	{ +		const LLSD& dict_entry = *it; +		if (dict_name == dict_entry["language"].asString()) +			return dict_entry; +	} +	return LLSD(); +} + +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(mDictMap, user_map)) || (0 == mDictMap.size()) ) +	{ +		llifstream app_map(app_path + "dictionaries.xml", std::ios::binary); +		if ( (!app_map.is_open()) || (0 == LLSDSerialize::fromXMLDocument(mDictMap, app_map)) || (0 == mDictMap.size()) ) +			return; +	} + +	// Look for installed dictionaries +	std::string tmp_app_path, tmp_user_path; +	for (LLSD::array_iterator it = mDictMap.beginArray(); it != mDictMap.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 + ".aff")) && (gDirUtilp->fileExists(tmp_user_path + ".dic"))) || +			  ((gDirUtilp->fileExists(tmp_app_path + ".aff")) && (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(std::list<std::string> dict_list) +{ +	if (!getUseSpellCheck()) +	{ +		return; +	} + +	// Check if we're only adding secondary dictionaries, or removing them +	std::list<std::string> dict_add(llmax(dict_list.size(), mDictSecondary.size())), dict_rem(llmax(dict_list.size(), mDictSecondary.size())); +	dict_list.sort(); +	mDictSecondary.sort(); +	std::list<std::string>::iterator end_added = std::set_difference(dict_list.begin(), dict_list.end(), mDictSecondary.begin(), mDictSecondary.end(), dict_add.begin()); +	std::list<std::string>::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 (std::list<std::string>::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()) ) +	{ +		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 (std::list<std::string>::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); +	} +} diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h new file mode 100644 index 0000000000..affdac2907 --- /dev/null +++ b/indra/llui/llspellcheck.h @@ -0,0 +1,77 @@ +/**  + * @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 <boost/signals2.hpp> + +class Hunspell; + +class LLSpellChecker : public LLSingleton<LLSpellChecker> +{ +	friend class LLSingleton<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; + +public: +	const LLSD	getDictionaryData(const std::string& dict_name) const; +	const LLSD&	getDictionaryMap() const { return mDictMap; } +	void		refreshDictionaryMap(); +	void		setSecondaryDictionaries(std::list<std::string> dictList); +protected: +	void		addToDictFile(const std::string& dict_path, const std::string& word); +	void		initHunspell(const std::string& dict_name); + +public: +	static const std::string getDictionaryAppPath(); +	static const std::string getDictionaryUserPath(); +	static bool				 getUseSpellCheck(); +	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: +	Hunspell*				 mHunspell; +	std::string				 mDictName; +	std::string				 mDictFile; +	LLSD					 mDictMap; +	std::list<std::string>	 mDictSecondary; +	std::vector<std::string> mIgnoreList; + +	static settings_change_signal_t	sSettingsChangeSignal; +}; + +#endif // LLSPELLCHECK_H diff --git a/indra/llui/llspellcheckmenuhandler.h b/indra/llui/llspellcheckmenuhandler.h new file mode 100644 index 0000000000..d5c95bad39 --- /dev/null +++ b/indra/llui/llspellcheckmenuhandler.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 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 1ea623791d..1ad3ee1dd1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12082,6 +12082,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)</string> +    </map>      <key>UseNewWalkRun</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 0861fe85a8..698f2469a3 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -91,6 +91,7 @@  #include "llsecondlifeurls.h"  #include "llupdaterservice.h"  #include "llcallfloater.h" +#include "llspellcheck.h"  // Linden library includes  #include "llavatarnamecache.h" @@ -112,6 +113,7 @@  // Third party library includes  #include <boost/bind.hpp>  #include <boost/foreach.hpp> +#include <boost/algorithm/string.hpp> @@ -2488,6 +2490,18 @@ bool LLAppViewer::initConfiguration()  		//gDirUtilp->setSkinFolder("default");      } +	if (gSavedSettings.getBOOL("SpellCheck")) +	{ +		std::list<std::string> dict_list; +		boost::split(dict_list, gSavedSettings.getString("SpellCheckDictionary"), 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. diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 7e02a41e7e..89360778d1 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -739,6 +739,7 @@ bool idle_startup()  		{  			display_startup();  			initialize_edit_menu(); +			initialize_spellcheck_menu();  			display_startup();  			init_menus();  			display_startup(); diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 093b84413a..7b6dbfaa0b 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -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 @@ -498,6 +502,24 @@ bool handleForceShowGrid(const LLSD& newvalue)  	return true;  } +bool handleSpellCheckChanged() +{ +	if (gSavedSettings.getBOOL("SpellCheck")) +	{ +		std::list<std::string> dict_list; +		boost::split(dict_list, gSavedSettings.getString("SpellCheckDictionary"), 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() ) @@ -704,6 +726,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 diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 0104d35e53..2a11f3cc16 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -81,6 +81,7 @@  #include "llrootview.h"  #include "llsceneview.h"  #include "llselectmgr.h" +#include "llspellcheckmenuhandler.h"  #include "llstatusbar.h"  #include "lltextureview.h"  #include "lltoolcomp.h" @@ -4984,6 +4985,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 =  @@ -7933,6 +8006,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. diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index 87cb4efbc4..8c40762865 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -39,6 +39,7 @@ class LLObjectSelection;  class LLSelectNode;  void initialize_edit_menu(); +void initialize_spellcheck_menu();  void init_menus();  void cleanup_menus(); diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 0931c4ec9b..1b732676e4 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -91,6 +91,8 @@ class ViewerManifest(LLManifest):                  # ... and the entire windlight directory                  self.path("windlight") +                # ... and the pre-installed spell checking dictionaries +                self.path("dictionaries")                  self.end_prefix("app_settings")              if self.prefix(src="character"): @@ -393,6 +395,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 +664,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.dylib", dst="Resources/libhunspell-1.3.dylib")              self.path("../viewer_components/updater/scripts/darwin/update_install", "MacOS/update_install") @@ -1053,6 +1059,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 | 
