summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/lllineeditor.cpp26
-rw-r--r--indra/llui/llmenugl.cpp4
-rw-r--r--indra/llui/llspellcheck.cpp152
-rw-r--r--indra/llui/llspellcheck.h14
-rw-r--r--indra/llui/lltextbase.cpp14
-rw-r--r--indra/llui/lltexteditor.cpp4
6 files changed, 195 insertions, 19 deletions
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 8fc0465acf..48d49af588 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -599,7 +599,9 @@ 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;
}
@@ -609,7 +611,9 @@ 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;
}
@@ -1896,7 +1900,9 @@ void LLLineEditor::draw()
// 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();
@@ -1912,17 +1918,23 @@ void LLLineEditor::draw()
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;
@@ -1934,19 +1946,27 @@ void LLLineEditor::draw()
{
// 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());
@@ -1955,7 +1975,9 @@ void LLLineEditor::draw()
{
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;
}
}
@@ -2582,9 +2604,13 @@ void LLLineEditor::showContextMenu(S32 x, S32 y)
if (hasSelection())
{
if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) )
+ {
deselect();
+ }
else
+ {
setCursor(llmax(mSelectionStart, mSelectionEnd));
+ }
}
bool use_spellcheck = getSpellCheck(), is_misspelled = false;
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 3c14bf5415..efb9848a90 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -3909,9 +3909,13 @@ void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view)
arrange();
if (spawning_view)
+ {
mSpawningViewHandle = spawning_view->getHandle();
+ }
else
+ {
mSpawningViewHandle.markDead();
+ }
LLView::setVisible(TRUE);
}
diff --git a/indra/llui/llspellcheck.cpp b/indra/llui/llspellcheck.cpp
index 04c8a4fed0..887714d720 100644
--- a/indra/llui/llspellcheck.cpp
+++ b/indra/llui/llspellcheck.cpp
@@ -41,6 +41,9 @@ static const std::string DICT_DIR = "dictionaries";
static const std::string DICT_CUSTOM_SUFFIX = "_custom";
static const std::string DICT_IGNORE_SUFFIX = "_ignore";
+static const std::string DICT_FILE_MAIN = "dictionaries.xml";
+static const std::string DICT_FILE_USER = "user_dictionaries.xml";
+
LLSD LLSpellChecker::sDictMap;
LLSpellChecker::settings_change_signal_t LLSpellChecker::sSettingsChangeSignal;
@@ -75,13 +78,17 @@ S32 LLSpellChecker::getSuggestions(const std::string& word, std::vector<std::str
{
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();
@@ -94,17 +101,28 @@ const LLSD LLSpellChecker::getDictionaryData(const std::string& dict_language)
{
const LLSD& dict_entry = *it;
if (dict_language == dict_entry["language"].asString())
+ {
return dict_entry;
+ }
}
return LLSD();
}
// static
+bool LLSpellChecker::hasDictionary(const std::string& dict_language, bool check_installed)
+{
+ const LLSD dict_info = getDictionaryData(dict_language);
+ return dict_info.has("language") && ( (!check_installed) || (dict_info["installed"].asBoolean()) );
+}
+
+// static
void LLSpellChecker::setDictionaryData(const LLSD& dict_info)
{
const std::string dict_language = dict_info["language"].asString();
if (dict_language.empty())
+ {
return;
+ }
for (LLSD::array_iterator it = sDictMap.beginArray(); it != sDictMap.endArray(); ++it)
{
@@ -126,22 +144,28 @@ void LLSpellChecker::refreshDictionaryMap()
const std::string user_path = getDictionaryUserPath();
// Load dictionary information (file name, friendly name, ...)
- llifstream user_file(user_path + "dictionaries.xml", std::ios::binary);
+ llifstream user_file(user_path + DICT_FILE_MAIN, std::ios::binary);
if ( (!user_file.is_open()) || (0 == LLSDSerialize::fromXMLDocument(sDictMap, user_file)) || (0 == sDictMap.size()) )
{
- llifstream app_file(app_path + "dictionaries.xml", std::ios::binary);
+ llifstream app_file(app_path + DICT_FILE_MAIN, std::ios::binary);
if ( (!app_file.is_open()) || (0 == LLSDSerialize::fromXMLDocument(sDictMap, app_file)) || (0 == sDictMap.size()) )
+ {
return;
+ }
}
// Load user installed dictionary information
- llifstream custom_file(user_path + "user_dictionaries.xml", std::ios::binary);
+ llifstream custom_file(user_path + DICT_FILE_USER, std::ios::binary);
if (custom_file.is_open())
{
LLSD custom_dict_map;
LLSDSerialize::fromXMLDocument(custom_dict_map, custom_file);
- for (LLSD::array_const_iterator it = custom_dict_map.beginArray(); it != custom_dict_map.endArray(); ++it)
- setDictionaryData(*it);
+ for (LLSD::array_iterator it = custom_dict_map.beginArray(); it != custom_dict_map.endArray(); ++it)
+ {
+ LLSD& dict_info = *it;
+ dict_info["user_installed"] = true;
+ setDictionaryData(dict_info);
+ }
custom_file.close();
}
@@ -197,7 +221,9 @@ void LLSpellChecker::addToDictFile(const std::string& dict_path, const std::stri
{
// Skip over the first line since that's just a line count
if (0 != line_num)
+ {
word_list.push_back(word);
+ }
line_num++;
}
}
@@ -215,11 +241,20 @@ void LLSpellChecker::addToDictFile(const std::string& dict_path, const std::stri
{
file_out << word_list.size() << std::endl;
for (std::vector<std::string>::const_iterator itWord = word_list.begin(); itWord != word_list.end(); ++itWord)
+ {
file_out << *itWord << std::endl;
+ }
file_out.close();
}
}
+bool LLSpellChecker::isActiveDictionary(const std::string& dict_language) const
+{
+ return
+ (mDictLanguage == dict_language) ||
+ (mDictSecondary.end() != std::find(mDictSecondary.begin(), mDictSecondary.end(), dict_language));
+}
+
void LLSpellChecker::setSecondaryDictionaries(dict_list_t dict_list)
{
if (!getUseSpellCheck())
@@ -238,8 +273,8 @@ void LLSpellChecker::setSecondaryDictionaries(dict_list_t dict_list)
{
mDictSecondary = dict_list;
- std::string dict_name = mDictName;
- initHunspell(dict_name);
+ std::string dict_language = mDictLanguage;
+ initHunspell(dict_language);
}
else if (end_added != dict_add.begin()) // Add the new secondary dictionaries one by one
{
@@ -249,31 +284,37 @@ void LLSpellChecker::setSecondaryDictionaries(dict_list_t dict_list)
{
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)
+void LLSpellChecker::initHunspell(const std::string& dict_language)
{
if (mHunspell)
{
delete mHunspell;
mHunspell = NULL;
- mDictName.clear();
+ mDictLanguage.clear();
mDictFile.clear();
mIgnoreList.clear();
}
- const LLSD dict_entry = (!dict_name.empty()) ? getDictionaryData(dict_name) : LLSD();
+ const LLSD dict_entry = (!dict_language.empty()) ? getDictionaryData(dict_language) : LLSD();
if ( (!dict_entry.isDefined()) || (!dict_entry["installed"].asBoolean()) || (!dict_entry["is_primary"].asBoolean()))
{
sSettingsChangeSignal();
@@ -287,13 +328,19 @@ void LLSpellChecker::initHunspell(const std::string& dict_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;
+ mDictLanguage = dict_language;
mDictFile = dict_entry["name"].asString();
if (dict_entry["has_custom"].asBoolean())
@@ -325,13 +372,19 @@ void LLSpellChecker::initHunspell(const std::string& dict_name)
{
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());
+ }
}
}
@@ -363,18 +416,85 @@ bool LLSpellChecker::getUseSpellCheck()
}
// static
+bool LLSpellChecker::canRemoveDictionary(const std::string& dict_language)
+{
+ // Only user-installed inactive dictionaries can be removed
+ const LLSD dict_info = getDictionaryData(dict_language);
+ return
+ (dict_info["user_installed"].asBoolean()) &&
+ ( (!getUseSpellCheck()) || (!LLSpellChecker::instance().isActiveDictionary(dict_language)) );
+}
+
+// static
+void LLSpellChecker::removeDictionary(const std::string& dict_language)
+{
+ if (!canRemoveDictionary(dict_language))
+ {
+ return;
+ }
+
+ LLSD dict_map = loadUserDictionaryMap();
+ for (LLSD::array_const_iterator it = dict_map.beginArray(); it != dict_map.endArray(); ++it)
+ {
+ const LLSD& dict_info = *it;
+ if (dict_info["language"].asString() == dict_language)
+ {
+ const std::string dict_dic = getDictionaryUserPath() + dict_info["name"].asString() + ".dic";
+ if (gDirUtilp->fileExists(dict_dic))
+ {
+ LLFile::remove(dict_dic);
+ }
+ const std::string dict_aff = getDictionaryUserPath() + dict_info["name"].asString() + ".aff";
+ if (gDirUtilp->fileExists(dict_aff))
+ {
+ LLFile::remove(dict_aff);
+ }
+ dict_map.erase(it - dict_map.beginArray());
+ break;
+ }
+ }
+ saveUserDictionaryMap(dict_map);
+
+ refreshDictionaryMap();
+}
+
+// static
+LLSD LLSpellChecker::loadUserDictionaryMap()
+{
+ LLSD dict_map;
+ llifstream dict_file(getDictionaryUserPath() + DICT_FILE_USER, std::ios::binary);
+ if (dict_file.is_open())
+ {
+ LLSDSerialize::fromXMLDocument(dict_map, dict_file);
+ dict_file.close();
+ }
+ return dict_map;
+}
+
+// static
+void LLSpellChecker::saveUserDictionaryMap(const LLSD& dict_map)
+{
+ llofstream dict_file(getDictionaryUserPath() + DICT_FILE_USER, std::ios::trunc);
+ if (dict_file.is_open())
+ {
+ LLSDSerialize::toPrettyXML(dict_map, dict_file);
+ dict_file.close();
+ }
+}
+
+// static
boost::signals2::connection LLSpellChecker::setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb)
{
return sSettingsChangeSignal.connect(cb);
}
// static
-void LLSpellChecker::setUseSpellCheck(const std::string& dict_name)
+void LLSpellChecker::setUseSpellCheck(const std::string& dict_language)
{
- if ( (((dict_name.empty()) && (getUseSpellCheck())) || (!dict_name.empty())) &&
- (LLSpellChecker::instance().mDictName != dict_name) )
+ if ( (((dict_language.empty()) && (getUseSpellCheck())) || (!dict_language.empty())) &&
+ (LLSpellChecker::instance().mDictLanguage != dict_language) )
{
- LLSpellChecker::instance().initHunspell(dict_name);
+ LLSpellChecker::instance().initHunspell(dict_language);
}
}
@@ -382,5 +502,7 @@ void LLSpellChecker::setUseSpellCheck(const std::string& dict_name)
void LLSpellChecker::initClass()
{
if (sDictMap.isUndefined())
+ {
refreshDictionaryMap();
+ }
}
diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h
index acb121dd19..4ab80195ea 100644
--- a/indra/llui/llspellcheck.h
+++ b/indra/llui/llspellcheck.h
@@ -48,24 +48,30 @@ public:
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);
+ void initHunspell(const std::string& dict_language);
public:
typedef std::list<std::string> dict_list_t;
- const std::string& getActiveDictionary() const { return mDictName; }
+ const std::string& getPrimaryDictionary() const { return mDictLanguage; }
const dict_list_t& getSecondaryDictionaries() const { return mDictSecondary; }
+ bool isActiveDictionary(const std::string& dict_language) const;
void setSecondaryDictionaries(dict_list_t dict_list);
+ static bool canRemoveDictionary(const std::string& dict_language);
static const std::string getDictionaryAppPath();
static const std::string getDictionaryUserPath();
static const LLSD getDictionaryData(const std::string& dict_language);
static const LLSD& getDictionaryMap() { return sDictMap; }
static bool getUseSpellCheck();
+ static bool hasDictionary(const std::string& dict_language, bool check_installed = false);
static void refreshDictionaryMap();
- static void setUseSpellCheck(const std::string& dict_name);
+ static void removeDictionary(const std::string& dict_language);
+ static void setUseSpellCheck(const std::string& dict_language);
protected:
+ static LLSD loadUserDictionaryMap();
static void setDictionaryData(const LLSD& dict_info);
+ static void saveUserDictionaryMap(const LLSD& dict_map);
public:
typedef boost::signals2::signal<void()> settings_change_signal_t;
@@ -75,7 +81,7 @@ protected:
protected:
Hunspell* mHunspell;
- std::string mDictName;
+ std::string mDictLanguage;
std::string mDictFile;
dict_list_t mDictSecondary;
std::vector<std::string> mIgnoreList;
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 4db1efdd20..5821cc3593 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -582,7 +582,9 @@ void LLTextBase::drawText()
// 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)
@@ -597,7 +599,9 @@ void LLTextBase::drawText()
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));
@@ -609,7 +613,9 @@ void LLTextBase::drawText()
// Find the start of the next word
word_start = word_end + 1;
while ( (word_start < seg_end) && (!LLWStringUtil::isPartOfWord(wstrText[word_start])) )
+ {
word_start++;
+ }
}
}
@@ -695,12 +701,16 @@ void LLTextBase::drawText()
{
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;
}
@@ -1297,7 +1307,9 @@ 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;
}
@@ -1307,7 +1319,9 @@ 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;
}
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index b860f0b44f..144b6960a1 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -1960,9 +1960,13 @@ void LLTextEditor::showContextMenu(S32 x, S32 y)
if (hasSelection())
{
if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) )
+ {
deselect();
+ }
else
+ {
setCursorPos(llmax(mSelectionStart, mSelectionEnd));
+ }
}
bool use_spellcheck = getSpellCheck(), is_misspelled = false;