diff --git a/data/magic.mse-game/magic-words.en_US.dic b/data/magic.mse-game/magic-words.en_US.dic
new file mode 100644
index 00000000..cfb447e6
--- /dev/null
+++ b/data/magic.mse-game/magic-words.en_US.dic
@@ -0,0 +1,34 @@
+33
+mana
+untap/MSDRJZG
+face-down
+nonwhite
+nonblue
+nonblack
+nonred
+nongreen
+non-land
+unblock/USDG
+precombat
+postcombat
+scry
+Plainswalk
+Islandwalk
+Swampwalk
+Mountainwalk
+Forestwalk
+Landwalk
+Desertwalk
+Plainshome
+Islandhome
+Swamphome
+Mountainhome
+Foresthome
+Landhome
+Soulshift
+Ninjitsu
+Bushido
+Lifelink
+Gravestorm
+Fateseal
+Bloodthirst
diff --git a/data/magic.mse-game/script b/data/magic.mse-game/script
index 45e08daa..3bcc099c 100644
--- a/data/magic.mse-game/script
+++ b/data/magic.mse-game/script
@@ -334,8 +334,17 @@ mana_context :=
| [ ]* # keyword argument that is declared as cost
| , # keyword argument that is declared as cost
";
+
# truncates the name of legends
legend_filter := replace@(match:"(, | of | the ).*", replace: "" )
+
+# these are considered a correct 'word' for spellchecking in the text box:
+additional_text_words := match@(match:
+ "(?ix)^(?: # match whole word
+ ]*>.*?]*> # cardnames and stuff
+ | [+-]?[0-9X]+ / [+-]?[0-9X]+ # '3/3', '+X/+X'
+ )$")
+
# the rule text filter
# - adds mana symbols
# - makes text in parentheses italic
@@ -413,7 +422,11 @@ text_filter :=
replace: { _1 + to_upper(_2) }) +
curly_quotes +
# step 9 : spellcheck
- { check_spelling(language:language().spellcheck_code) }
+ { check_spelling(
+ language: language().spellcheck_code,
+ extra_dictionary: "/magic.mse-game/magic-words",
+ extra_match: additional_text_words
+ )}
############################################################## Other boxes
diff --git a/src/gui/symbol/control.cpp b/src/gui/symbol/control.cpp
index 808ad857..c8bb28b9 100644
--- a/src/gui/symbol/control.cpp
+++ b/src/gui/symbol/control.cpp
@@ -193,6 +193,12 @@ void SymbolControl::draw(DC& dc) {
}
dc.SetLogicalFunction(wxCOPY);
}
+ // draw aspect ratio indicators
+ double ar = symbol->aspectRatio();
+ // TODO: limit aspect ratio
+ if (ar > 0) {
+ } else if (ar < 0) {
+ }
// draw editing overlay
if (editor) {
editor->draw(dc);
diff --git a/src/mse.vcproj b/src/mse.vcproj
index 899ba430..46d5bcd7 100644
--- a/src/mse.vcproj
+++ b/src/mse.vcproj
@@ -3948,11 +3948,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/script/functions/spelling.cpp b/src/script/functions/spelling.cpp
index 31c840f9..e57515f9 100644
--- a/src/script/functions/spelling.cpp
+++ b/src/script/functions/spelling.cpp
@@ -15,7 +15,7 @@
// ----------------------------------------------------------------------------- : Functions
-bool spelled_correctly(const String& input, size_t start, size_t end, SpellChecker& checker) {
+inline bool spelled_correctly(const String& input, size_t start, size_t end, SpellChecker** checkers, const ScriptValueP& extra_test, Context& ctx) {
// untag
String word = untag(input.substr(start,end-start));
if (word.empty()) return true;
@@ -33,28 +33,48 @@ bool spelled_correctly(const String& input, size_t start, size_t end, SpellCheck
// symbols are always spelled correctly
return true;
}
- // run through spellchecker
- return checker.spell(word.substr(start_u,end_u));
+ // run through spellchecker(s)
+ word.erase(end_u,String::npos);
+ word.erase(0,start_u);
+ for (SpellChecker** c = checkers ; *c ; ++c) {
+ if ((*c)->spell(word)) {
+ return true;
+ }
+ }
+ // run through additional words regex
+ if (extra_test) {
+ ctx.setVariable(SCRIPT_VAR_input, to_script(input.substr(start2,end2-start2)));
+ if (*extra_test->eval(ctx)) {
+ return true;
+ }
+ }
+ return false;
}
-void check_word(const String& input, String& out, size_t start, size_t end, SpellChecker& checker) {
+void check_word(const String& input, String& out, size_t start, size_t end, SpellChecker** checkers, const ScriptValueP& extra_test, Context& ctx) {
if (start >= end) return;
- bool good = spelled_correctly(input, start, end, checker);
+ bool good = spelled_correctly(input, start, end, checkers, extra_test, ctx);
if (!good) out += _("");
out.append(input, start, end-start);
if (!good) out += _("");
}
SCRIPT_FUNCTION(check_spelling) {
- SCRIPT_PARAM(String,language);
- SCRIPT_PARAM(String,input);
- if (language.empty()) {
- // no language -> spelling checking
- SCRIPT_RETURN(true);
- }
- SpellChecker& checker = SpellChecker::get(language);
+ SCRIPT_PARAM_C(String,language);
+ SCRIPT_PARAM_C(String,input);
+ SCRIPT_OPTIONAL_PARAM_N_(String,_("extra dictionary"),extra_dictionary);
+ SCRIPT_OPTIONAL_PARAM_N_(ScriptValueP,_("extra match"),extra_match);
// remove old spelling error tags
input = remove_tag(input, _(" spelling checking
+ if (language.empty()) {
+ SCRIPT_RETURN(input);
+ }
+ SpellChecker* checkers[3] = {nullptr};
+ checkers[0] = &SpellChecker::get(language);
+ if (!extra_dictionary.empty()) {
+ checkers[1] = &SpellChecker::get(extra_dictionary,language);
+ }
// now walk over the words in the input, and mark misspellings
String result;
size_t word_start = 0, word_end = 0, pos = 0;
@@ -71,7 +91,7 @@ SCRIPT_FUNCTION(check_spelling) {
}
} else if (isSpace(c) || c == EM_DASH || c == EN_DASH) {
// word boundary -> check word
- check_word(input, result, word_start, word_end, checker);
+ check_word(input, result, word_start, word_end, checkers, extra_match, ctx);
// non-word characters
result.append(input, word_end, pos - word_end + 1);
// next
@@ -81,15 +101,15 @@ SCRIPT_FUNCTION(check_spelling) {
}
}
// last word
- check_word(input, result, word_start, word_end, checker);
+ check_word(input, result, word_start, word_end, checkers, extra_match, ctx);
result.append(input, word_end, String::npos);
// done
SCRIPT_RETURN(result);
}
SCRIPT_FUNCTION(check_spelling_word) {
- SCRIPT_PARAM(String,language);
- SCRIPT_PARAM(String,input);
+ SCRIPT_PARAM_C(String,language);
+ SCRIPT_PARAM_C(String,input);
if (language.empty()) {
// no language -> spelling checking
SCRIPT_RETURN(true);
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 9282b294..4ce3bd27 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -77,6 +77,7 @@ void init_script_variables() {
Var(styling);
Var(value);
Var(condition);
+ Var(language);
assert(variables.size() == SCRIPT_VAR_CUSTOM_FIRST);
}
diff --git a/src/script/script.hpp b/src/script/script.hpp
index f1f667bc..74dec9c7 100644
--- a/src/script/script.hpp
+++ b/src/script/script.hpp
@@ -136,6 +136,7 @@ enum Variable
, SCRIPT_VAR_styling
, SCRIPT_VAR_value
, SCRIPT_VAR_condition
+, SCRIPT_VAR_language
, SCRIPT_VAR_CUSTOM_FIRST // other variables start from here
, SCRIPT_VAR_CUSTOM_LOTS = 0xFFFFFF // ensure that sizeof(Variable) is large enough
};
diff --git a/src/util/io/package_manager.cpp b/src/util/io/package_manager.cpp
index 63fb0e35..60683c5f 100644
--- a/src/util/io/package_manager.cpp
+++ b/src/util/io/package_manager.cpp
@@ -120,6 +120,27 @@ InputStreamP PackageManager::openFileFromPackage(Packaged*& package, const Strin
throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
}
+String PackageManager::openFilenameFromPackage(Packaged*& package, const String& name) {
+ if (!name.empty() && name.GetChar(0) == _('/')) {
+ // absolute name; break name
+ size_t start = name.find_first_not_of(_("/\\"), 1); // allow "//package/name" from incorrect scripts
+ size_t pos = name.find_first_of(_("/\\"), start);
+ if (start < pos && pos != String::npos) {
+ // open package
+ PackagedP p = openAny(name.substr(start, pos-start));
+ if (package && !is_substr(name,start,_(":NO-WARN-DEP:"))) {
+ package->requireDependency(p.get());
+ }
+ package = p.get();
+ return p->absoluteFilename() + _("/") + name.substr(pos + 1);
+ }
+ } else if (package) {
+ // relative name
+ return package->absoluteFilename() + _("/") + name;
+ }
+ throw FileNotFoundError(name, _("No package name specified, use '/package/filename'"));
+}
+
String PackageManager::getDictionaryDir(bool l) const {
String dir = (l ? local : global).getDirectory();
if (dir.empty()) return wxEmptyString;
diff --git a/src/util/io/package_manager.hpp b/src/util/io/package_manager.hpp
index f0b72584..f288b3fd 100644
--- a/src/util/io/package_manager.hpp
+++ b/src/util/io/package_manager.hpp
@@ -150,6 +150,12 @@ class PackageManager {
*/
InputStreamP openFileFromPackage(Packaged*& package, const String& name);
+ /// Get a filename to open from a package
+ /** WARNING: this is a bit of a hack, since not all package types support names in this way.
+ * It is needed for third party libraries (i.e. hunspell) that load stuff from files.
+ */
+ String openFilenameFromPackage(Packaged*& package, const String& name);
+
// --------------------------------------------------- : Packages on disk
/// Check if the given dependency is currently installed
diff --git a/src/util/spell_checker.cpp b/src/util/spell_checker.cpp
index 125d0dae..6b8c8e9c 100644
--- a/src/util/spell_checker.cpp
+++ b/src/util/spell_checker.cpp
@@ -35,6 +35,31 @@ SpellChecker& SpellChecker::get(const String& language) {
return *speller;
}
+SpellChecker& SpellChecker::get(const String& filename, const String& language) {
+ SpellCheckerP& speller = spellers[filename + _(".") + language];
+ if (!speller) {
+ Packaged* package = nullptr;
+ String prefix = package_manager.openFilenameFromPackage(package, filename) + _(".");
+ String local_dir = package_manager.getDictionaryDir(true);
+ String global_dir = package_manager.getDictionaryDir(false);
+ String aff_path = language + _(".aff");
+ String dic_path = language + _(".dic");
+ if (wxFileExists(prefix + aff_path) && wxFileExists(prefix + dic_path)) {
+ speller = SpellCheckerP(new SpellChecker((prefix + aff_path).mb_str(),
+ (prefix + dic_path).mb_str()));
+ } else if (wxFileExists(local_dir + aff_path) && wxFileExists(prefix + dic_path)) {
+ speller = SpellCheckerP(new SpellChecker((local_dir + aff_path).mb_str(),
+ (prefix + dic_path).mb_str()));
+ } else if (wxFileExists(global_dir + aff_path) && wxFileExists(prefix + dic_path)) {
+ speller = SpellCheckerP(new SpellChecker((global_dir + aff_path).mb_str(),
+ (prefix + dic_path).mb_str()));
+ } else {
+ throw Error(_("Dictionary '") + filename + _("' not found for language: ") + language);
+ }
+ }
+ return *speller;
+}
+
SpellChecker::SpellChecker(const char* aff_path, const char* dic_path)
: Hunspell(aff_path,dic_path)
, encoding(String(get_dic_encoding(), IF_UNICODE(wxConvLibc, wxSTRING_MAXLEN)))
diff --git a/src/util/spell_checker.hpp b/src/util/spell_checker.hpp
index e2831721..52ffae0b 100644
--- a/src/util/spell_checker.hpp
+++ b/src/util/spell_checker.hpp
@@ -22,6 +22,9 @@ class SpellChecker : public Hunspell, public IntrusivePtrBase {
/// Get a SpellChecker object for the given language.
/** Note: This is not threadsafe yet */
static SpellChecker& get(const String& language);
+ /// Get a SpellChecker object for the given language and filename
+ /** Note: This is not threadsafe yet */
+ static SpellChecker& get(const String& filename, const String& language);
/// Destroy all cached SpellChecker objects
static void destroyAll();