phoenix-firestorm/indra/llui/llhunspell.cpp

293 lines
9.7 KiB
C++

/**
*
* Copyright (c) 2010, Kitty Barnett
*
* The source code in this file is provided to you under the terms of the
* GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt
* in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt
*
* By copying, modifying or distributing this software, you acknowledge that
* you have read and understood your obligations described above, and agree to
* abide by those obligations.
*
*/
#include "linden_common.h"
#include "lldir.h"
#include "llsdserialize.h"
#include "llhunspell.h"
#if LL_WINDOWS
#include <hunspell/hunspelldll.h>
#pragma comment(lib, "libhunspell.lib")
#else
#include <hunspell/hunspell.hxx>
#endif
// ============================================================================
// Static variables
//
static const std::string c_strDictCustomSuffix = "_custom";
static const std::string c_strDictIgnoreSuffix = "_ignore";
// ============================================================================
LLHunspellWrapper::LLHunspellWrapper()
: m_pHunspell(NULL)
{
m_strDictionaryAppPath = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "dictionaries", "");
m_strDictionaryUserPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries", "");
if (!gDirUtilp->fileExists(m_strDictionaryUserPath))
LLFile::mkdir(m_strDictionaryUserPath);
// Load dictionary information (file name, friendly name, ...)
llifstream fileDictMap(m_strDictionaryAppPath + "dictionaries.xml", std::ios::binary);
if (fileDictMap.is_open())
LLSDSerialize::fromXMLDocument(m_sdDictionaryMap, fileDictMap);
// Look for installed dictionaries
std::string strTempAppPath, strTempUserPath;
for (LLSD::array_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
itDictInfo != endDictInfo; ++itDictInfo)
{
LLSD& sdDict = *itDictInfo;
strTempAppPath = (sdDict.has("name")) ? m_strDictionaryAppPath + sdDict["name"].asString() : LLStringUtil::null;
strTempUserPath = (sdDict.has("name")) ? m_strDictionaryUserPath + sdDict["name"].asString() : LLStringUtil::null;
sdDict["installed"] =
(!strTempAppPath.empty()) && (gDirUtilp->fileExists(strTempAppPath + ".aff")) && (gDirUtilp->fileExists(strTempAppPath + ".dic"));
sdDict["has_custom"] = (!strTempUserPath.empty()) && (gDirUtilp->fileExists(strTempUserPath + c_strDictCustomSuffix + ".dic"));
sdDict["has_ignore"] = (!strTempUserPath.empty()) && (gDirUtilp->fileExists(strTempUserPath + c_strDictIgnoreSuffix + ".dic"));
}
}
LLHunspellWrapper::~LLHunspellWrapper()
{
delete m_pHunspell;
}
// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
bool LLHunspellWrapper::checkSpelling(const std::string& strWord) const
{
if ( (!m_pHunspell) || (strWord.length() < 3) || (0 != m_pHunspell->spell(strWord.c_str())) )
{
return true;
}
if (m_IgnoreList.size() > 0)
{
std::string strWordLower(strWord);
LLStringUtil::toLower(strWordLower);
return (std::find(m_IgnoreList.begin(), m_IgnoreList.end(), strWordLower) != m_IgnoreList.end());
}
return false;
}
S32 LLHunspellWrapper::getSuggestions(const std::string& strWord, std::vector<std::string>& strSuggestionList) const
{
if ( (!m_pHunspell) || (strWord.length() < 3) )
return 0;
strSuggestionList.clear();
char** ppstrSuggestionList; int cntSuggestion = 0;
if ( (cntSuggestion = m_pHunspell->suggest(&ppstrSuggestionList, strWord.c_str())) != 0 )
{
for (int idxSuggestion = 0; idxSuggestion < cntSuggestion; idxSuggestion++)
strSuggestionList.push_back(ppstrSuggestionList[idxSuggestion]);
m_pHunspell->free_list(&ppstrSuggestionList, cntSuggestion);
}
return strSuggestionList.size();
}
// ============================================================================
// Dictionary related functions
//
// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
S32 LLHunspellWrapper::getDictionaries(std::vector<std::string>& strDictionaryList) const
{
strDictionaryList.clear();
for (LLSD::array_const_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
itDictInfo != endDictInfo; ++itDictInfo)
{
const LLSD& sdDict = *itDictInfo;
strDictionaryList.push_back(sdDict["language"].asString());
}
return strDictionaryList.size();
}
// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
S32 LLHunspellWrapper::getInstalledDictionaries(std::vector<std::string>& strDictionaryList) const
{
strDictionaryList.clear();
for (LLSD::array_const_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
itDictInfo != endDictInfo; ++itDictInfo)
{
const LLSD& sdDict = *itDictInfo;
if (sdDict["installed"].asBoolean())
strDictionaryList.push_back(sdDict["language"].asString());
}
return strDictionaryList.size();
}
// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
bool LLHunspellWrapper::setCurrentDictionary(const std::string& strDictionary)
{
if (strDictionary == m_strDictionaryName)
return false;
s_SettingsChangeSignal();
if (m_pHunspell)
{
delete m_pHunspell;
m_pHunspell = NULL;
m_strDictionaryName = m_strDictionaryFile = "";
m_IgnoreList.clear();
}
if (strDictionary.empty())
return false;
LLSD sdDictInfo;
for (LLSD::array_const_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
itDictInfo != endDictInfo; ++itDictInfo)
{
const LLSD& sdDict = *itDictInfo;
if ( (sdDict["installed"].asBoolean()) && (strDictionary == sdDict["language"].asString()) )
sdDictInfo = sdDict;
}
if (sdDictInfo.has("name"))
{
std::string strPathAff = m_strDictionaryAppPath + sdDictInfo["name"].asString() + ".aff";
std::string strPathDic = m_strDictionaryAppPath + sdDictInfo["name"].asString() + ".dic";
m_pHunspell = new Hunspell(strPathAff.c_str(), strPathDic.c_str());
if (!m_pHunspell)
return false;
m_strDictionaryName = strDictionary;
m_strDictionaryFile = sdDictInfo["name"].asString();
// Add the custom dictionary (if there is one)
if (sdDictInfo["has_custom"].asBoolean())
{
std::string strPathCustomDic = m_strDictionaryUserPath + m_strDictionaryFile + c_strDictCustomSuffix + ".dic";
m_pHunspell->add_dic(strPathCustomDic.c_str());
}
// Load the ignore list (if there is one)
if (sdDictInfo["has_ignore"].asBoolean())
{
llifstream fileDictIgnore(m_strDictionaryUserPath + m_strDictionaryFile + c_strDictIgnoreSuffix + ".dic", std::ios::in);
if (fileDictIgnore.is_open())
{
std::string strWord; int idxLine = 0;
while (getline(fileDictIgnore, strWord))
{
// Skip over the first line since that's just a line count
if (0 != idxLine)
{
LLStringUtil::toLower(strWord);
m_IgnoreList.push_back(strWord);
}
idxLine++;
}
}
}
}
return (NULL != m_pHunspell);
}
// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
void LLHunspellWrapper::addToCustomDictionary(const std::string& strWord)
{
if (m_pHunspell)
m_pHunspell->add(strWord.c_str());
addToDictFile(m_strDictionaryUserPath + m_strDictionaryFile + c_strDictCustomSuffix + ".dic", strWord);
s_SettingsChangeSignal();
}
// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
void LLHunspellWrapper::addToIgnoreList(const std::string& strWord)
{
std::string strWordLower(strWord);
LLStringUtil::toLower(strWordLower);
if (std::find(m_IgnoreList.begin(), m_IgnoreList.end(), strWordLower) == m_IgnoreList.end())
{
m_IgnoreList.push_back(strWordLower);
addToDictFile(m_strDictionaryUserPath + m_strDictionaryFile + c_strDictIgnoreSuffix + ".dic", strWordLower);
s_SettingsChangeSignal();
}
}
// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
void LLHunspellWrapper::addToDictFile(const std::string& strDictPath, const std::string& strWord)
{
// TODO-Catznip: has to be a better way to add one word to the end and increment the line count?
std::vector<std::string> wordList;
// Read any existing words
if (gDirUtilp->fileExists(strDictPath))
{
llifstream fileDictIn(strDictPath, std::ios::in);
if (fileDictIn.is_open())
{
std::string strWord; int idxLine = 0;
while (getline(fileDictIn, strWord))
{
// Skip over the first line since that's just a line count
if (0 != idxLine)
wordList.push_back(strWord);
idxLine++;
}
}
else
{
// TODO-Catznip: show error message?
return;
}
}
// Add the new word to the end
wordList.push_back(strWord);
// Recreate the file with the new line count and new word added
llofstream fileDictOut(strDictPath, std::ios::out | std::ios::trunc);
if (fileDictOut.is_open())
{
fileDictOut << wordList.size() << std::endl;
for (std::vector<std::string>::const_iterator itWord = wordList.begin(); itWord != wordList.end(); ++itWord)
fileDictOut << *itWord << std::endl;
fileDictOut.close();
}
}
// ============================================================================
// Static member functions
//
LLHunspellWrapper::settings_change_signal_t LLHunspellWrapper::s_SettingsChangeSignal;
boost::signals2::connection LLHunspellWrapper::setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb)
{
return s_SettingsChangeSignal.connect(cb);
}
bool LLHunspellWrapper::useSpellCheck()
{
return (LLHunspellWrapper::instanceExists()) || (LLHunspellWrapper::instance().m_pHunspell);
}
void LLHunspellWrapper::setUseSpellCheck(const std::string& strDictionary)
{
if ( ((strDictionary.empty()) && (useSpellCheck())) || (!strDictionary.empty()) )
LLHunspellWrapper::instance().setCurrentDictionary(strDictionary);
}
// ============================================================================