diff --git a/aegisub/libaegisub/include/libaegisub/spellchecker.h b/aegisub/libaegisub/include/libaegisub/spellchecker.h index dd4500503..450a39407 100644 --- a/aegisub/libaegisub/include/libaegisub/spellchecker.h +++ b/aegisub/libaegisub/include/libaegisub/spellchecker.h @@ -37,6 +37,11 @@ public: /// @return Whether or not word can be added virtual bool CanAddWord(std::string const& word)=0; + /// Can the word be removed from the current dictionary? + /// @param word Word to check + /// @return Whether or not word can be removed + virtual bool CanRemoveWord(std::string const& word)=0; + /// Check if the given word is spelled correctly /// @param word Word to check /// @return Whether or not the word is valid diff --git a/aegisub/src/dialog_spellchecker.cpp b/aegisub/src/dialog_spellchecker.cpp index 224cacedf..9922300b6 100644 --- a/aegisub/src/dialog_spellchecker.cpp +++ b/aegisub/src/dialog_spellchecker.cpp @@ -80,6 +80,10 @@ DialogSpellChecker::DialogSpellChecker(agi::Context *context) current_word_sizer->Add(new wxStaticText(this, -1, _("Replace with:")), 0, wxALIGN_CENTER_VERTICAL); current_word_sizer->Add(replace_word = new wxTextCtrl(this, -1, ""), wxSizerFlags(1).Expand()); + replace_word->Bind(wxEVT_COMMAND_TEXT_UPDATED, [=](wxCommandEvent&) { + remove_button->Enable(spellchecker->CanRemoveWord(from_wx(replace_word->GetValue()))); + }); + // List of suggested corrections suggest_list = new wxListBox(this, -1, wxDefaultPosition, wxSize(300, 150)); suggest_list->Bind(wxEVT_COMMAND_LISTBOX_SELECTED, &DialogSpellChecker::OnChangeSuggestion, this); @@ -132,19 +136,32 @@ DialogSpellChecker::DialogSpellChecker(agi::Context *context) button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogSpellChecker::OnReplace, this); actions_sizer->Add(button = new wxButton(this, -1, _("Replace &all")), button_flags); - button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogSpellChecker::OnReplaceAll, this); + button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=](wxCommandEvent&) { + auto_replace[from_wx(orig_word->GetValue())] = from_wx(replace_word->GetValue()); + Replace(); + FindNext(); + }); actions_sizer->Add(button = new wxButton(this, -1, _("&Ignore")), button_flags); button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=](wxCommandEvent&) { FindNext(); }); actions_sizer->Add(button = new wxButton(this, -1, _("Ignore a&ll")), button_flags); - button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogSpellChecker::OnIgnoreAll, this); + button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=](wxCommandEvent&) { + auto_ignore.insert(from_wx(orig_word->GetValue())); + FindNext(); + }); actions_sizer->Add(add_button = new wxButton(this, -1, _("Add to &dictionary")), button_flags); - add_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogSpellChecker::OnAdd, this); + add_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=](wxCommandEvent&) { + spellchecker->AddWord(from_wx(orig_word->GetValue())); + FindNext(); + }); actions_sizer->Add(remove_button = new wxButton(this, -1, _("Remove fro&m dictionary")), button_flags); - remove_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogSpellChecker::OnRemove, this); + remove_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=](wxCommandEvent&) { + spellchecker->RemoveWord(from_wx(replace_word->GetValue())); + SetWord(from_wx(orig_word->GetValue())); + }); actions_sizer->Add(new HelpButton(this, "Spell Checker"), button_flags); @@ -166,30 +183,6 @@ void DialogSpellChecker::OnReplace(wxCommandEvent&) { FindNext(); } -void DialogSpellChecker::OnReplaceAll(wxCommandEvent&) { - auto_replace[from_wx(orig_word->GetValue())] = from_wx(replace_word->GetValue()); - - Replace(); - FindNext(); -} - -void DialogSpellChecker::OnIgnoreAll(wxCommandEvent&) { - auto_ignore.insert(from_wx(orig_word->GetValue())); - FindNext(); -} - -void DialogSpellChecker::OnAdd(wxCommandEvent&) { - spellchecker->AddWord(from_wx(orig_word->GetValue())); - FindNext(); -} - -void DialogSpellChecker::OnRemove(wxCommandEvent&) { - // TODO pop-up dialog - - spellchecker->RemoveWord(from_wx(replace_word->GetValue())); - FindNext(); -} - void DialogSpellChecker::OnChangeLanguage(wxCommandEvent&) { wxString code = dictionary_lang_codes[language->GetSelection()]; OPT_SET("Tool/Spell Checker/Language")->SetString(STD_STR(code)); diff --git a/aegisub/src/dialog_spellchecker.h b/aegisub/src/dialog_spellchecker.h index d8e89459f..2e6707771 100644 --- a/aegisub/src/dialog_spellchecker.h +++ b/aegisub/src/dialog_spellchecker.h @@ -85,10 +85,6 @@ class DialogSpellChecker : public wxDialog { void OnChangeSuggestion(wxCommandEvent&); void OnReplace(wxCommandEvent&); - void OnReplaceAll(wxCommandEvent&); - void OnIgnoreAll(wxCommandEvent&); - void OnAdd(wxCommandEvent&); - void OnRemove(wxCommandEvent&); public: DialogSpellChecker(agi::Context *context); diff --git a/aegisub/src/spellchecker_hunspell.cpp b/aegisub/src/spellchecker_hunspell.cpp index 92111a711..b8331cd01 100644 --- a/aegisub/src/spellchecker_hunspell.cpp +++ b/aegisub/src/spellchecker_hunspell.cpp @@ -63,25 +63,19 @@ bool HunspellSpellChecker::CanAddWord(std::string const& word) { } } +bool HunspellSpellChecker::CanRemoveWord(std::string const& word) { + return !!customWords.count(word); +} + void HunspellSpellChecker::AddWord(std::string const& word) { if (!hunspell) return; // Add it to the in-memory dictionary hunspell->add(conv->Convert(word).c_str()); - std::set words; - - try { - ReadUserDictionary(words); - } - catch (agi::FileNotFoundError&) { - LOG_I("dictionary/hunspell/add") << "User dictionary not found; creating it"; - } - // Add the word - words.insert(word); - - WriteUserDictionary(words); + if (customWords.insert(word).second) + WriteUserDictionary(); } void HunspellSpellChecker::RemoveWord(std::string const& word) { @@ -90,26 +84,17 @@ void HunspellSpellChecker::RemoveWord(std::string const& word) { // Remove it from the in-memory dictionary hunspell->remove(conv->Convert(word).c_str()); - std::set words; + auto word_iter = customWords.find(word); + if (word_iter != customWords.end()) { + customWords.erase(word_iter); - try { - ReadUserDictionary(words); - } - catch (agi::FileNotFoundError&) { - LOG_I("dictionary/hunspell/remove") << "User dictionary not found; nothing to remove"; - return; - } - - auto word_iter = words.find(word); - if (word_iter != words.end()) { - words.erase(word_iter); - - WriteUserDictionary(words); + WriteUserDictionary(); } } -void HunspellSpellChecker::ReadUserDictionary(std::set &words) -{ +void HunspellSpellChecker::ReadUserDictionary() { + customWords.clear(); + // Ensure that the path exists wxFileName fn(userDicPath); if (!fn.DirExists()) { @@ -118,21 +103,19 @@ void HunspellSpellChecker::ReadUserDictionary(std::set &words) // Read the old contents of the user's dictionary else { agi::scoped_ptr stream(agi::io::Open(STD_STR(userDicPath))); - remove_copy_if( - ++agi::line_iterator(*stream), - agi::line_iterator(), - inserter(words, words.end()), - [](std::string const& str) { return str.empty(); }); + copy_if( + ++agi::line_iterator(*stream), agi::line_iterator(), + inserter(customWords, customWords.end()), + [](std::string const& str) { return !str.empty(); }); } } -void HunspellSpellChecker::WriteUserDictionary(std::set const& words) -{ +void HunspellSpellChecker::WriteUserDictionary() { // Write the new dictionary { agi::io::Save writer(STD_STR(userDicPath)); - writer.Get() << words.size() << "\n"; - copy(words.begin(), words.end(), std::ostream_iterator(writer.Get(), "\n")); + writer.Get() << customWords.size() << "\n"; + copy(customWords.begin(), customWords.end(), std::ostream_iterator(writer.Get(), "\n")); } // Announce a language change so that any other spellcheckers reload the @@ -252,24 +235,16 @@ void HunspellSpellChecker::OnLanguageChanged() { conv.reset(new agi::charset::IconvWrapper("utf-8", hunspell->get_dic_encoding())); rconv.reset(new agi::charset::IconvWrapper(hunspell->get_dic_encoding(), "utf-8")); - try { - agi::scoped_ptr stream(agi::io::Open(STD_STR(userDicPath))); - agi::line_iterator userDic(*stream); - agi::line_iterator end; - ++userDic; // skip entry count line - for (; userDic != end; ++userDic) { - if (userDic->empty()) continue; - try { - hunspell->add(conv->Convert(*userDic).c_str()); - } - catch (agi::charset::ConvError const&) { - // Normally this shouldn't happen, but some versions of Aegisub - // wrote words in the wrong charset - } + ReadUserDictionary(); + + for (auto const& word : customWords) { + try { + hunspell->add(conv->Convert(word).c_str()); + } + catch (agi::charset::ConvError const&) { + // Normally this shouldn't happen, but some versions of Aegisub + // wrote words in the wrong charset } - } - catch (agi::Exception const&) { - // File doesn't exist or we don't have permission to read it } } diff --git a/aegisub/src/spellchecker_hunspell.h b/aegisub/src/spellchecker_hunspell.h index eb31aa73a..1a96b0266 100644 --- a/aegisub/src/spellchecker_hunspell.h +++ b/aegisub/src/spellchecker_hunspell.h @@ -26,6 +26,8 @@ #include #include +#include + #include namespace agi { namespace charset { class IconvWrapper; } } @@ -46,6 +48,9 @@ class HunspellSpellChecker : public agi::SpellChecker { /// Path to user-local dictionary. wxString userDicPath; + /// Words in the custom user dictionary + std::set customWords; + /// Dictionary language change connection agi::signal::Connection lang_listener; /// Dictionary language change handler @@ -57,9 +62,9 @@ class HunspellSpellChecker : public agi::SpellChecker { void OnPathChanged(); /// Load words from custom dictionary - void ReadUserDictionary(std::set &words); + void ReadUserDictionary(); /// Save words to custom dictionary - void WriteUserDictionary(std::set const& words); + void WriteUserDictionary(); public: HunspellSpellChecker(); @@ -68,6 +73,7 @@ public: void AddWord(std::string const& word); void RemoveWord(std::string const& word); bool CanAddWord(std::string const& word); + bool CanRemoveWord(std::string const& word); bool CheckWord(std::string const& word); std::vector GetSuggestions(std::string const& word); std::vector GetLanguageList();