diff --git a/src/Forms/Main.cs b/src/Forms/Main.cs
index 1f08d13bb..7f26e860d 100644
--- a/src/Forms/Main.cs
+++ b/src/Forms/Main.cs
@@ -2766,8 +2766,56 @@ namespace Nikse.SubtitleEdit.Forms
SpellCheck(true);
}
+ private void SpellCheckViaWord()
+ {
+ WordSpellChecker wordSpellChecker = null;
+ int totalCorrections = 0;
+ try
+ {
+ wordSpellChecker = new WordSpellChecker();
+ }
+ catch
+ {
+ MessageBox.Show(_language.UnableToStartWord);
+ //Configuration.Settings.General.SpellChecker = "hunspell"; ???
+ return;
+ }
+ string version = wordSpellChecker.Version;
+ int index = 1;
+ foreach (Paragraph p in _subtitle.Paragraphs)
+ {
+ int errorsBefore;
+ int errorsAfter;
+ wordSpellChecker.NewDocument();
+ ShowStatus(string.Format(_language.SpellChekingViaWordXLineYOfX, version, index, _subtitle.Paragraphs.Count.ToString()));
+ SubtitleListview1.SelectIndexAndEnsureVisible(index - 1);
+ string newText = wordSpellChecker.CheckSpelling(p.Text, out errorsBefore, out errorsAfter);
+ wordSpellChecker.CloseDocument();
+ if (errorsAfter > 0)
+ {
+ wordSpellChecker.Quit();
+ ShowStatus(string.Format(_language.SpellCheckAbortedXCorrections, totalCorrections));
+ return;
+ }
+ else if (errorsBefore != errorsAfter)
+ {
+ textBoxListViewText.Text = newText;
+ }
+ totalCorrections += (errorsBefore - errorsAfter);
+ index++;
+ }
+ wordSpellChecker.Quit();
+ ShowStatus(string.Format(_language.SpellCheckCompletedXCorrections, totalCorrections));
+ }
+
private void SpellCheck(bool autoDetect)
{
+ if (Configuration.Settings.General.SpellChecker.ToLower().Contains("word"))
+ {
+ SpellCheckViaWord();
+ return;
+ }
+
try
{
string dictionaryFolder = Utilities.DictionaryFolder;
diff --git a/src/Logic/Language.cs b/src/Logic/Language.cs
index 993fba59b..ee905e49e 100644
--- a/src/Logic/Language.cs
+++ b/src/Logic/Language.cs
@@ -662,6 +662,10 @@ namespace Nikse.SubtitleEdit.Logic
NetworkMode = "Networking mode",
UserAndAction = "User/action",
XStartedSessionYAtZ = "{0}: Started session {1} at {2}",
+ SpellChekingViaWordXLineYOfX = "Spell checking using Word {0} - line {1} / {2}",
+ UnableToStartWord = "Unable to start Microsoft Word",
+ SpellCheckAbortedXCorrections = "Spell check aborted. {0} corrections was made.",
+ SpellCheckCompletedXCorrections = "Spell check completed. {0} corrections was made.",
Menu = new LanguageStructure.Main.MainMenu
{
diff --git a/src/Logic/LanguageStructure.cs b/src/Logic/LanguageStructure.cs
index cde668424..a05e12e51 100644
--- a/src/Logic/LanguageStructure.cs
+++ b/src/Logic/LanguageStructure.cs
@@ -590,7 +590,11 @@
public string UserAndAction { get; set; }
public string NetworkMode { get; set; }
public string XStartedSessionYAtZ { get; set; }
-
+ public string SpellChekingViaWordXLineYOfX { get; set; }
+ public string UnableToStartWord { get; set; }
+ public string SpellCheckAbortedXCorrections { get; set; }
+ public string SpellCheckCompletedXCorrections { get; set; }
+
public class MainMenu
{
public class FileMenu
diff --git a/src/Logic/Settings.cs b/src/Logic/Settings.cs
index c3f678931..fb6eac235 100644
--- a/src/Logic/Settings.cs
+++ b/src/Logic/Settings.cs
@@ -237,6 +237,7 @@ namespace Nikse.SubtitleEdit.Logic
public bool AutoContinueOn { get; set; }
public bool SyncListViewWithVideoWhilePlaying { get; set; }
public int AutoBackupSeconds { get; set; }
+ public string SpellChecker { get; set; }
public GeneralSettings()
{
@@ -283,6 +284,7 @@ namespace Nikse.SubtitleEdit.Logic
AutoContinueOn = false;
SyncListViewWithVideoWhilePlaying = false;
AutoBackupSeconds = 0;
+ SpellChecker = "hunspell";
}
}
@@ -626,7 +628,10 @@ namespace Nikse.SubtitleEdit.Logic
settings.General.AutoContinueOn = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("AutoBackupSeconds");
if (subNode != null)
- settings.General.AutoBackupSeconds = Convert.ToInt32(subNode.InnerText);
+ settings.General.AutoBackupSeconds = Convert.ToInt32(subNode.InnerText);
+ subNode = node.SelectSingleNode("SpellChecker");
+ if (subNode != null)
+ settings.General.SpellChecker = subNode.InnerText;
settings.Tools = new Nikse.SubtitleEdit.Logic.ToolsSettings();
node = doc.DocumentElement.SelectSingleNode("Tools");
@@ -929,8 +934,9 @@ namespace Nikse.SubtitleEdit.Logic
textWriter.WriteElementString("DefaultAdjustMilliseconds", settings.General.DefaultAdjustMilliseconds.ToString());
textWriter.WriteElementString("AutoRepeatOn", settings.General.AutoRepeatOn.ToString());
textWriter.WriteElementString("AutoContinueOn", settings.General.AutoContinueOn.ToString());
- textWriter.WriteElementString("SyncListViewWithVideoWhilePlaying", settings.General.SyncListViewWithVideoWhilePlaying.ToString());
- textWriter.WriteElementString("AutoBackupSeconds", settings.General.AutoBackupSeconds.ToString());
+ textWriter.WriteElementString("SyncListViewWithVideoWhilePlaying", settings.General.SyncListViewWithVideoWhilePlaying.ToString());
+ textWriter.WriteElementString("AutoBackupSeconds", settings.General.AutoBackupSeconds.ToString());
+ textWriter.WriteElementString("SpellChecker", settings.General.SpellChecker);
textWriter.WriteEndElement();
textWriter.WriteStartElement("Tools", "");
diff --git a/src/Logic/WordLateBound.cs b/src/Logic/WordLateBound.cs
new file mode 100644
index 000000000..0a9977912
--- /dev/null
+++ b/src/Logic/WordLateBound.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Reflection;
+
+namespace Nikse.SubtitleEdit.Logic
+{
+ ///
+ /// MS Word methods (late bound) for spell checking by Nikse
+ /// Mostly a bunch of hacks...
+ ///
+ internal class WordSpellChecker
+ {
+ private object _wordApplication;
+ private object _wordDocument;
+ private Type _wordApplicationType;
+ private Type _wordDocumentType;
+
+ public WordSpellChecker()
+ {
+ _wordApplicationType = System.Type.GetTypeFromProgID("Word.Application");
+ _wordApplication = Activator.CreateInstance(_wordApplicationType);
+ _wordApplicationType.InvokeMember("Top", BindingFlags.SetProperty, null, _wordApplication, new object[] { -1000 }); // hide window - it's a hack
+ _wordApplicationType.InvokeMember("Visible", BindingFlags.SetProperty, null, _wordApplication, new object[] { true }); // set visible to true - otherwise it will appear in the background
+ }
+
+ public void NewDocument()
+ {
+ _wordDocumentType = System.Type.GetTypeFromProgID("Word.Document");
+ _wordDocument = Activator.CreateInstance(_wordDocumentType);
+ }
+
+ public void CloseDocument()
+ {
+ object saveChanges = false;
+ object p = Missing.Value;
+ _wordDocumentType.InvokeMember("Close", System.Reflection.BindingFlags.InvokeMethod, null, _wordDocument, new object[] { saveChanges, p, p });
+ }
+
+ public string Version
+ {
+ get
+ {
+ object wordVersion = _wordApplicationType.InvokeMember("Version", BindingFlags.GetProperty, null, _wordApplication, null);
+ return wordVersion.ToString();
+ }
+ }
+
+ public void Quit()
+ {
+ object saveChanges = false;
+ object originalFormat = Missing.Value;
+ object routeDocument = Missing.Value;
+ _wordApplicationType.InvokeMember("Quit", System.Reflection.BindingFlags.InvokeMethod, null, _wordApplication, new object[] { saveChanges, originalFormat, routeDocument });
+ try
+ {
+ System.Runtime.InteropServices.Marshal.ReleaseComObject(_wordDocument);
+ System.Runtime.InteropServices.Marshal.ReleaseComObject(_wordApplication);
+ }
+ finally
+ {
+ _wordDocument = null;
+ _wordApplication = null;
+ }
+ }
+
+ public string CheckSpelling(string text, out int errorsBefore, out int errorsAfter)
+ {
+ // insert text
+ object words = _wordDocumentType.InvokeMember("Words", BindingFlags.GetProperty, null, _wordDocument, null);
+ object range = words.GetType().InvokeMember("First", BindingFlags.GetProperty, null, words, null);
+ range.GetType().InvokeMember("InsertBefore", BindingFlags.InvokeMethod, null, range, new Object[] { text });
+
+ // spell check error count
+ object spellingErrors = _wordDocumentType.InvokeMember("SpellingErrors", BindingFlags.GetProperty, null, _wordDocument, null);
+ object spellingErrorsCount = spellingErrors.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, spellingErrors, null);
+ errorsBefore = int.Parse(spellingErrorsCount.ToString());
+ System.Runtime.InteropServices.Marshal.ReleaseComObject(spellingErrors);
+
+ // perform spell check
+ object p = Missing.Value;
+ _wordApplicationType.InvokeMember("Visible", BindingFlags.SetProperty, null, _wordApplication, new object[] { true }); // set visible to true - otherwise it will appear in the background
+ _wordDocumentType.InvokeMember("CheckSpelling", BindingFlags.InvokeMethod, null, _wordDocument, new Object[] { p, p, p, p, p, p, p, p, p, p, p, p }); // 12 parameters
+// _wordApplicationType.InvokeMember("Top", BindingFlags.SetProperty, null, _wordApplication, new object[] { -1000 }); // hide window - it's a hack
+
+ // spell check error count
+ spellingErrors = _wordDocumentType.InvokeMember("SpellingErrors", BindingFlags.GetProperty, null, _wordDocument, null);
+ spellingErrorsCount = spellingErrors.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, spellingErrors, null);
+ errorsAfter = int.Parse(spellingErrorsCount.ToString());
+ System.Runtime.InteropServices.Marshal.ReleaseComObject(spellingErrors);
+
+ // Get spellcheck text
+ object first = 0;
+ object characters = _wordDocumentType.InvokeMember("Characters", BindingFlags.GetProperty, null, _wordDocument, null);
+ object charactersCount = characters.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, characters, null);
+ object last = int.Parse(charactersCount.ToString()) - 1;
+ System.Runtime.InteropServices.Marshal.ReleaseComObject(characters);
+
+ object resultText = range.GetType().InvokeMember("Text", BindingFlags.GetProperty, null, range, null);
+ return resultText.ToString().TrimEnd(); // result needs a trimming at the end
+ }
+
+ }
+}
diff --git a/src/SubtitleEdit.csproj b/src/SubtitleEdit.csproj
index 738da6ae1..32e9d47d2 100644
--- a/src/SubtitleEdit.csproj
+++ b/src/SubtitleEdit.csproj
@@ -561,6 +561,7 @@
+