STORM-276 Added the ability to remove (user-installed) dictionaries
parent
45b00642aa
commit
d96d3d5256
|
|
@ -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;
|
||||
|
||||
|
|
@ -141,10 +144,10 @@ 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;
|
||||
|
|
@ -152,7 +155,7 @@ void LLSpellChecker::refreshDictionaryMap()
|
|||
}
|
||||
|
||||
// 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;
|
||||
|
|
@ -245,6 +248,13 @@ void LLSpellChecker::addToDictFile(const std::string& dict_path, const std::stri
|
|||
}
|
||||
}
|
||||
|
||||
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())
|
||||
|
|
@ -263,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
|
||||
{
|
||||
|
|
@ -293,18 +303,18 @@ void LLSpellChecker::setSecondaryDictionaries(dict_list_t dict_list)
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
@ -330,7 +340,7 @@ void LLSpellChecker::initHunspell(const std::string& dict_name)
|
|||
return;
|
||||
}
|
||||
|
||||
mDictName = dict_name;
|
||||
mDictLanguage = dict_language;
|
||||
mDictFile = dict_entry["name"].asString();
|
||||
|
||||
if (dict_entry["has_custom"].asBoolean())
|
||||
|
|
@ -405,6 +415,73 @@ bool LLSpellChecker::getUseSpellCheck()
|
|||
return (LLSpellChecker::instanceExists()) && (LLSpellChecker::instance().mHunspell);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
|
@ -412,12 +489,12 @@ boost::signals2::connection LLSpellChecker::setSettingsChangeCallback(const sett
|
|||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,15 +48,17 @@ 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);
|
||||
|
|
@ -64,9 +66,12 @@ public:
|
|||
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;
|
||||
|
|
@ -76,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;
|
||||
|
|
|
|||
|
|
@ -46,10 +46,24 @@ LLFloaterSpellCheckerSettings::LLFloaterSpellCheckerSettings(const LLSD& key)
|
|||
{
|
||||
}
|
||||
|
||||
void LLFloaterSpellCheckerSettings::draw()
|
||||
{
|
||||
LLFloater::draw();
|
||||
|
||||
std::vector<LLScrollListItem*> sel_items = getChild<LLScrollListCtrl>("spellcheck_available_list")->getAllSelected();
|
||||
bool enable_remove = !sel_items.empty();
|
||||
for (std::vector<LLScrollListItem*>::const_iterator sel_it = sel_items.begin(); sel_it != sel_items.end(); ++sel_it)
|
||||
{
|
||||
enable_remove &= LLSpellChecker::canRemoveDictionary((*sel_it)->getValue().asString());
|
||||
}
|
||||
getChild<LLUICtrl>("spellcheck_remove_btn")->setEnabled(enable_remove);
|
||||
}
|
||||
|
||||
BOOL LLFloaterSpellCheckerSettings::postBuild(void)
|
||||
{
|
||||
gSavedSettings.getControl("SpellCheck")->getSignal()->connect(boost::bind(&LLFloaterSpellCheckerSettings::refreshDictionaries, this, false));
|
||||
LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLFloaterSpellCheckerSettings::onSpellCheckSettingsChange, this));
|
||||
getChild<LLUICtrl>("spellcheck_remove_btn")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onBtnRemove, this));
|
||||
getChild<LLUICtrl>("spellcheck_import_btn")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onBtnImport, this));
|
||||
getChild<LLUICtrl>("spellcheck_main_combo")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::refreshDictionaries, this, false));
|
||||
getChild<LLUICtrl>("spellcheck_moveleft_btn")->setCommitCallback(boost::bind(&LLFloaterSpellCheckerSettings::onBtnMove, this, "spellcheck_active_list", "spellcheck_available_list"));
|
||||
|
|
@ -121,6 +135,15 @@ void LLFloaterSpellCheckerSettings::onOpen(const LLSD& key)
|
|||
refreshDictionaries(true);
|
||||
}
|
||||
|
||||
void LLFloaterSpellCheckerSettings::onBtnRemove()
|
||||
{
|
||||
std::vector<LLScrollListItem*> sel_items = getChild<LLScrollListCtrl>("spellcheck_available_list")->getAllSelected();
|
||||
for (std::vector<LLScrollListItem*>::const_iterator sel_it = sel_items.begin(); sel_it != sel_items.end(); ++sel_it)
|
||||
{
|
||||
LLSpellChecker::instance().removeDictionary((*sel_it)->getValue().asString());
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterSpellCheckerSettings::onSpellCheckSettingsChange()
|
||||
{
|
||||
refreshDictionaries(true);
|
||||
|
|
@ -137,7 +160,7 @@ void LLFloaterSpellCheckerSettings::refreshDictionaries(bool from_settings)
|
|||
std::string dict_cur = dict_combo->getSelectedItemLabel();
|
||||
if ((dict_cur.empty() || from_settings) && (LLSpellChecker::getUseSpellCheck()))
|
||||
{
|
||||
dict_cur = LLSpellChecker::instance().getActiveDictionary();
|
||||
dict_cur = LLSpellChecker::instance().getPrimaryDictionary();
|
||||
}
|
||||
dict_combo->clearRows();
|
||||
dict_combo->setEnabled(enabled);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class LLFloaterSpellCheckerSettings : public LLFloater
|
|||
public:
|
||||
LLFloaterSpellCheckerSettings(const LLSD& key);
|
||||
|
||||
/*virtual*/ void draw();
|
||||
/*virtual*/ BOOL postBuild();
|
||||
/*virtual*/ void onOpen(const LLSD& key);
|
||||
|
||||
|
|
@ -42,6 +43,7 @@ protected:
|
|||
void onBtnImport();
|
||||
void onBtnMove(const std::string& from, const std::string& to);
|
||||
void onBtnOK();
|
||||
void onBtnRemove();
|
||||
void onSpellCheckSettingsChange();
|
||||
void refreshDictionaries(bool from_settings);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
border="true"
|
||||
can_close="true"
|
||||
can_minimize="true"
|
||||
bottom="275"
|
||||
bottom="300"
|
||||
left="300"
|
||||
can_resize="false"
|
||||
height="330"
|
||||
height="355"
|
||||
width="490"
|
||||
name="spellcheck_floater"
|
||||
title="Spell Checker Settings">
|
||||
|
|
@ -53,16 +53,6 @@
|
|||
top_pad="-15"
|
||||
width="175"
|
||||
/>
|
||||
<button
|
||||
follows="left|top"
|
||||
height="23"
|
||||
label="Import"
|
||||
label_selected="Import"
|
||||
layout="topleft"
|
||||
left_pad="5"
|
||||
name="spellcheck_import_btn"
|
||||
top_delta="0"
|
||||
width="75" />
|
||||
<text
|
||||
enabled_control="SpellCheck"
|
||||
follows="top|left"
|
||||
|
|
@ -148,6 +138,25 @@
|
|||
top_pad="-105"
|
||||
width="175"
|
||||
/>
|
||||
<button
|
||||
enabled="false"
|
||||
follows="left|top"
|
||||
height="23"
|
||||
label="Remove"
|
||||
layout="topleft"
|
||||
left="55"
|
||||
name="spellcheck_remove_btn"
|
||||
top_pad="5"
|
||||
width="80" />
|
||||
<button
|
||||
follows="left|top"
|
||||
height="23"
|
||||
label="Import..."
|
||||
layout="topleft"
|
||||
left_pad="15"
|
||||
name="spellcheck_import_btn"
|
||||
top_delta="0"
|
||||
width="80" />
|
||||
<view_border
|
||||
top_pad="10"
|
||||
left="2"
|
||||
|
|
@ -159,7 +168,7 @@
|
|||
mouse_opaque="false"
|
||||
name="divisor4"/>
|
||||
<button
|
||||
top_pad="10"
|
||||
top_pad="8"
|
||||
right="380"
|
||||
height="22"
|
||||
width="90"
|
||||
|
|
|
|||
Loading…
Reference in New Issue