From 60e39343b17f29c65e8a60a415f7ed83ff069a12 Mon Sep 17 00:00:00 2001
From: Kitty Barnett <develop@catznip.com>
Date: Tue, 14 Feb 2012 21:48:22 +0100
Subject: [PATCH] STORM-276 Reworked the spell check preferences to be more
 robust and less error-prone

---
 indra/llui/llspellcheck.cpp                   |  20 ++-
 indra/llui/llspellcheck.h                     |  14 +-
 indra/newview/llfloaterpreference.cpp         | 121 +++++++++++-------
 indra/newview/llfloaterpreference.h           |   2 +-
 .../xui/en/panel_preferences_spellcheck.xml   |   4 +
 5 files changed, 104 insertions(+), 57 deletions(-)

diff --git a/indra/llui/llspellcheck.cpp b/indra/llui/llspellcheck.cpp
index 65207144f89..46df44cdba8 100644
--- a/indra/llui/llspellcheck.cpp
+++ b/indra/llui/llspellcheck.cpp
@@ -41,6 +41,7 @@ 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()
@@ -86,9 +87,10 @@ S32 LLSpellChecker::getSuggestions(const std::string& word, std::vector<std::str
 	return suggestions.size();
 }
 
-const LLSD LLSpellChecker::getDictionaryData(const std::string& dict_name) const
+// static
+const LLSD LLSpellChecker::getDictionaryData(const std::string& dict_name)
 {
-	for (LLSD::array_const_iterator it = mDictMap.beginArray(); it != mDictMap.endArray(); ++it)
+	for (LLSD::array_const_iterator it = sDictMap.beginArray(); it != sDictMap.endArray(); ++it)
 	{
 		const LLSD& dict_entry = *it;
 		if (dict_name == dict_entry["language"].asString())
@@ -97,6 +99,7 @@ const LLSD LLSpellChecker::getDictionaryData(const std::string& dict_name) const
 	return LLSD();
 }
 
+// static
 void LLSpellChecker::refreshDictionaryMap()
 {
 	const std::string app_path = getDictionaryAppPath();
@@ -104,16 +107,16 @@ void LLSpellChecker::refreshDictionaryMap()
 
 	// 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()) )
+	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(mDictMap, app_map)) || (0 == mDictMap.size()) )
+		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 = mDictMap.beginArray(); it != mDictMap.endArray(); ++it)
+	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;
@@ -343,3 +346,10 @@ void LLSpellChecker::setUseSpellCheck(const std::string& dict_name)
 		LLSpellChecker::instance().initHunspell(dict_name);
 	}
 }
+
+// static
+void LLSpellChecker::initClass()
+{
+	if (sDictMap.isUndefined())
+		refreshDictionaryMap();
+}
diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h
index 8351655b49a..d736a7f082b 100644
--- a/indra/llui/llspellcheck.h
+++ b/indra/llui/llspellcheck.h
@@ -28,13 +28,15 @@
 #define LLSPELLCHECK_H
 
 #include "llsingleton.h"
+#include "llui.h"
 #include <boost/signals2.hpp>
 
 class Hunspell;
 
-class LLSpellChecker : public LLSingleton<LLSpellChecker>
+class LLSpellChecker : public LLSingleton<LLSpellChecker>, public LLInitClass<LLSpellChecker>
 {
 	friend class LLSingleton<LLSpellChecker>;
+	friend class LLInitClass<LLSpellChecker>;
 protected:
 	LLSpellChecker();
 	~LLSpellChecker();
@@ -52,28 +54,30 @@ class LLSpellChecker : public LLSingleton<LLSpellChecker>
 	typedef std::list<std::string> dict_list_t;
 
 	const std::string&	getActiveDictionary() const { return mDictName; }
-	const LLSD			getDictionaryData(const std::string& dict_name) const;
-	const LLSD&			getDictionaryMap() const { return mDictMap; }
 	const dict_list_t&	getSecondaryDictionaries() const { return mDictSecondary; }
-	void				refreshDictionaryMap();
 	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;
-	LLSD		mDictMap;
 	dict_list_t	mDictSecondary;
 	std::vector<std::string> mIgnoreList;
 
+	static LLSD						sDictMap;
 	static settings_change_signal_t	sSettingsChangeSignal;
 };
 
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 29b07d24798..c41488ce916 100755
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -448,6 +448,8 @@ BOOL LLFloaterPreference::postBuild()
 
 	getChild<LLComboBox>("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this));
 
+	gSavedSettings.getControl("SpellCheck")->getSignal()->connect(boost::bind(&LLFloaterPreference::refreshDictLists, this, false));
+	getChild<LLUICtrl>("combo_spellcheck_dict")->setCommitCallback(boost::bind(&LLFloaterPreference::refreshDictLists, this, false));
 	getChild<LLUICtrl>("btn_spellcheck_moveleft")->setCommitCallback(boost::bind(&LLFloaterPreference::onClickDictMove, this, "list_spellcheck_active", "list_spellcheck_available"));
 	getChild<LLUICtrl>("btn_spellcheck_moveright")->setCommitCallback(boost::bind(&LLFloaterPreference::onClickDictMove, this, "list_spellcheck_available", "list_spellcheck_active"));
 
@@ -585,14 +587,19 @@ void LLFloaterPreference::apply()
 
 	if (hasChild("check_spellcheck"), TRUE)
 	{
-		LLScrollListCtrl* list_ctrl = findChild<LLScrollListCtrl>("list_spellcheck_active");
-		std::vector<LLScrollListItem*> list_items = list_ctrl->getAllData();
-
 		std::list<std::string> list_dict;
-		list_dict.push_back(LLSpellChecker::instance().getActiveDictionary());
-		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());
 
+		LLComboBox* dict_combo = findChild<LLComboBox>("combo_spellcheck_dict");
+		const std::string dict_name = dict_combo->getSelectedItemLabel();
+		if (!dict_name.empty())
+		{
+			list_dict.push_back(dict_name);
+
+			LLScrollListCtrl* list_ctrl = findChild<LLScrollListCtrl>("list_spellcheck_active");
+			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, ","));
 	}
 
@@ -706,7 +713,7 @@ void LLFloaterPreference::onOpen(const LLSD& key)
 	// Load (double-)click to walk/teleport settings.
 	updateClickActionControls();
 	
-	buildDictLists();
+	refreshDictLists(true);
 
 	// Enabled/disabled popups, might have been changed by user actions
 	// while preferences floater was closed.
@@ -970,58 +977,80 @@ void LLFloaterPreference::refreshSkin(void* data)
 	self->getChild<LLRadioGroup>("skin_selection", true)->setValue(sSkin);
 }
 
-void LLFloaterPreference::buildDictLists()
+void LLFloaterPreference::refreshDictLists(bool from_settings)
 {
+	bool enabled = gSavedSettings.getBOOL("SpellCheck");
+	getChild<LLUICtrl>("btn_spellcheck_moveleft")->setEnabled(enabled);
+	getChild<LLUICtrl>("btn_spellcheck_moveright")->setEnabled(enabled);
+
+	// Populate the dictionary combobox
 	LLComboBox* dict_combo = findChild<LLComboBox>("combo_spellcheck_dict");
+	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);
 
-	LLScrollListCtrl* active_ctrl = findChild<LLScrollListCtrl>("list_spellcheck_active");
-	active_ctrl->clearRows();
+	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.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>("list_spellcheck_available");
-	avail_ctrl->clearRows();
+	LLScrollListCtrl* active_ctrl = findChild<LLScrollListCtrl>("list_spellcheck_active");
 
-	if (LLSpellChecker::getUseSpellCheck())
+	LLSpellChecker::dict_list_t active_list;
+	if ( ((!avail_ctrl->getItemCount()) && (!active_ctrl->getItemCount())) || (from_settings) )
 	{
-		// Populate the main dictionary combobox
-		const LLSD& dict_map = LLSpellChecker::instance().getDictionaryMap();
-		if (dict_map.size())
+		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)
 		{
-			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")) )
-					dict_combo->add(dict["language"].asString());
-			}
-			dict_combo->selectByValue(LLSpellChecker::instance().getActiveDictionary());
+			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";
-
-		// Populate the active dictionary list
-		LLSpellChecker::dict_list_t active_list = LLSpellChecker::instance().getSecondaryDictionaries();
-		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(LLSpellChecker::instance().getActiveDictionary());
+	LLSD row;
+	row["columns"][0]["column"] = "name";
+	row["columns"][0]["font"]["name"] = "SANSSERIF_SMALL";
+	row["columns"][0]["font"]["style"] = "NORMAL";
 
-		// Populate the available dictionary list
-		avail_ctrl->sortByColumnIndex(0, true);
-		for (LLSD::array_const_iterator dict_it = dict_map.beginArray(); dict_it != dict_map.endArray(); ++dict_it)
+	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())) )
 		{
-			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);
-			}
+			row["columns"][0]["value"] = dict["language"].asString();
+			avail_ctrl->addElement(row);
 		}
 	}
 }
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index cd258b5614b..f75f71cc3de 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -161,8 +161,8 @@ class LLFloaterPreference : public LLFloater, public LLAvatarPropertiesObserver
 	void applyUIColor(LLUICtrl* ctrl, const LLSD& param);
 	void getUIColor(LLUICtrl* ctrl, const LLSD& param);
 	
-	void buildDictLists();
 	void buildPopupLists();
+	void refreshDictLists(bool from_settings);
 	static void refreshSkin(void* data);
 private:
 	static std::string sSkin;
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_spellcheck.xml b/indra/newview/skins/default/xui/en/panel_preferences_spellcheck.xml
index 9b5d4298469..f1b16c5d0d6 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_spellcheck.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_spellcheck.xml
@@ -86,6 +86,7 @@
         Active
     </text>
     <scroll_list
+     enabled_control="SpellCheck"
      follows="top|left"
      height="155"
      layout="topleft"
@@ -96,6 +97,7 @@
      sort_ascending="true" 
      width="175" />
     <button
+     enabled_control="SpellCheck"
      follows="top|left"
      height="26"
      image_overlay="Arrow_Right"
@@ -107,6 +109,7 @@
      width="25">
     </button>
     <button
+     enabled_control="SpellCheck"
      follows="top|left"
      height="26"
      image_overlay="Arrow_Left"
@@ -117,6 +120,7 @@
      width="25">
     </button>
     <scroll_list
+     enabled_control="SpellCheck"
      follows="top|left"
      height="155"
      layout="topleft"
-- 
GitLab