| [1693] | 1 | unit skaSpellCheck; | 
|---|
|  | 2 | (* ***************************** BEGIN LICENSE BLOCK ********************** | 
|---|
|  | 3 | * | 
|---|
|  | 4 | * Copyright (C) 2015 | 
|---|
|  | 5 | * Sunil Kumar Arora (digitiger@gmail.com        sunil@healthsevak.com) | 
|---|
|  | 6 | * All Rights Reserved. | 
|---|
|  | 7 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | 
|---|
|  | 8 | * | 
|---|
|  | 9 | * The contents of this file are subject to the Mozilla Public License Version | 
|---|
|  | 10 | * 1.1 (the "License"); you may not use this file except in compliance with | 
|---|
|  | 11 | * the License. You may obtain a copy of the License at | 
|---|
|  | 12 | * http://www.mozilla.org/MPL/ | 
|---|
|  | 13 | * | 
|---|
|  | 14 | * Software distributed under the License is distributed on an "AS IS" basis, | 
|---|
|  | 15 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | 
|---|
|  | 16 | * for the specific language governing rights and limitations under the | 
|---|
|  | 17 | * License. | 
|---|
|  | 18 | * | 
|---|
|  | 19 | * Alternatively, the content of this file maybe used under the terms of either | 
|---|
|  | 20 | * the GNU General Public License Version 2 or later (the "GPL"), or the GNU | 
|---|
|  | 21 | * Lesser General Public License Version 2.1 or later (the "LGPL"), in which | 
|---|
|  | 22 | * case the provisions of the GPL or the LGPL are applicable instead of those | 
|---|
|  | 23 | * above. If you wish to allow use of your version of this file only under the | 
|---|
|  | 24 | * terms of either the GPL or the LGPL, and not to allow others to use your | 
|---|
|  | 25 | * version of this file under the terms of the MPL, indicate your division by | 
|---|
|  | 26 | * deleting the provisions above and replace them with the notice and other | 
|---|
|  | 27 | * provisions required by the GPL or LGPL. If you do not delete the provisions | 
|---|
|  | 28 | * above, a recipient may use your version of this file under the terms of any | 
|---|
|  | 29 | * one of the MPL, the GPL or the LGPL. | 
|---|
|  | 30 | * | 
|---|
|  | 31 | * *********************** END LICENSE BLOCK *********************************) | 
|---|
|  | 32 |  | 
|---|
|  | 33 | interface | 
|---|
|  | 34 |  | 
|---|
|  | 35 | uses | 
|---|
|  | 36 | Windows, Classes, SysUtils, ComCtrls, StdCtrls, Graphics; | 
|---|
|  | 37 |  | 
|---|
|  | 38 | const | 
|---|
|  | 39 | AboutThis = 'A wrapper component developed by Sunil K Arora (digitiger@gmail.com) of HealthSevak using OpenSource HanSpell engine'; | 
|---|
|  | 40 | type | 
|---|
|  | 41 | TSpellState = (ssNotStarted, ssChecking, ssCancelled, ssCompleted); | 
|---|
|  | 42 |  | 
|---|
|  | 43 | TskaHunSpellChecker = class(TComponent) | 
|---|
|  | 44 | private | 
|---|
|  | 45 | FActiveOrLoaded: Boolean; | 
|---|
|  | 46 | FpointerHunLib: Pointer; | 
|---|
|  | 47 | FSourceEdit: TRichEdit; | 
|---|
|  | 48 | FSuggestionList: TListbox; | 
|---|
|  | 49 |  | 
|---|
|  | 50 | FAffixFileName: string; | 
|---|
|  | 51 | FDictFileName: string; | 
|---|
|  | 52 | CurrentWord: String; | 
|---|
|  | 53 | CurrentText: String; | 
|---|
|  | 54 | FoundAt: Integer; | 
|---|
|  | 55 | PosOfFirstCharInCurrentLine: integer; | 
|---|
|  | 56 | CurrentLine: Integer; | 
|---|
|  | 57 | FIgnore: TStringList; | 
|---|
|  | 58 | WaitForUser: Boolean; | 
|---|
|  | 59 | WordLength:integer; | 
|---|
|  | 60 | WordPos: Integer; | 
|---|
|  | 61 | PREditorWndProc:pointer; | 
|---|
|  | 62 | FHighlightColor: TColor; | 
|---|
|  | 63 | FShowCompletion: Boolean; | 
|---|
|  | 64 | FpointerSpellComplete: String; | 
|---|
|  | 65 | FStatus: TSpellState; | 
|---|
|  | 66 | FUndoList: TStringList; | 
|---|
|  | 67 | FCustDict: TStringList; | 
|---|
|  | 68 | FCustom: String; | 
|---|
|  | 69 | FModified: Boolean; | 
|---|
|  | 70 | FHighlightEdit: TEdit; | 
|---|
|  | 71 | FbtnClose: TButton; | 
|---|
|  | 72 | function AddCustomWord(aWord: String; isInternal: Boolean = False): Boolean; | 
|---|
|  | 73 | overload; virtual; | 
|---|
|  | 74 | Function CurrentWordDetail(WithPosition: Boolean= True): String; | 
|---|
|  | 75 | function GetActive: Boolean; | 
|---|
|  | 76 | function GetStatus: TSpellState; | 
|---|
|  | 77 | procedure Initialize; | 
|---|
|  | 78 | procedure SetActive(const Value: Boolean); | 
|---|
|  | 79 | procedure SetAffixFileName(const Value: string); | 
|---|
|  | 80 | procedure SetbtnClose(const Value: TButton); | 
|---|
|  | 81 | procedure SetCustomDict(const Value: String); | 
|---|
|  | 82 | procedure SetDictFileName(const Value: string); | 
|---|
|  | 83 | procedure SetHighLightEdit(const Value: TEdit); | 
|---|
|  | 84 | procedure SetSourceEdit(const Value: TRichEdit); | 
|---|
|  | 85 | Function ShowMisSpelledWord:boolean; | 
|---|
|  | 86 | procedure Loaded; override; | 
|---|
|  | 87 | procedure ReplaceCurrentWordWith(const aNewWord: String); | 
|---|
|  | 88 | function GetAboutThis: String; | 
|---|
|  | 89 | procedure SaveForUndo(const Ignoring: Boolean = False); | 
|---|
|  | 90 | public | 
|---|
|  | 91 | constructor Create(AOwner: TComponent); overload; override; | 
|---|
|  | 92 | constructor Create(AOwner: TComponent; SourceTextRichEdit: TRichedit; | 
|---|
|  | 93 | SuggestList: TListbox); ReIntroduce; overload; | 
|---|
|  | 94 | destructor Destroy; override; | 
|---|
|  | 95 |  | 
|---|
|  | 96 | function AbortSpellCheck(Verbose: Boolean = True):Boolean; | 
|---|
|  | 97 | function AddCustomWord: Boolean; overload; virtual; | 
|---|
|  | 98 | procedure Change; | 
|---|
|  | 99 | procedure ChangeAll; | 
|---|
|  | 100 | procedure CheckSpelling; | 
|---|
|  | 101 | procedure Close; virtual; | 
|---|
|  | 102 | procedure CorrectWithMyWord; | 
|---|
|  | 103 | procedure GetSuggestions(const aMisSpeltWord: string; | 
|---|
|  | 104 | const SuggestionList: TStrings); dynamic; | 
|---|
|  | 105 | procedure IgnoreAll; | 
|---|
|  | 106 | procedure IgnoreOnce; | 
|---|
|  | 107 | function IsMisspelled(const AWord: string): Boolean; dynamic; | 
|---|
|  | 108 | function Open:Boolean; virtual; | 
|---|
|  | 109 | function ReStart: Boolean; virtual; | 
|---|
|  | 110 | function Undo: Boolean; | 
|---|
|  | 111 |  | 
|---|
|  | 112 |  | 
|---|
|  | 113 | property SpellCheckState: TSpellState read GetStatus default ssNotStarted; | 
|---|
|  | 114 | published | 
|---|
|  | 115 | property About: String read GetAboutThis; | 
|---|
|  | 116 | property Active: Boolean read GetActive write SetActive; | 
|---|
|  | 117 | property AffixFileName: string read FAffixFileName write SetAffixFileName; | 
|---|
|  | 118 | property btnClose: TButton read FbtnClose write SetbtnClose; | 
|---|
|  | 119 | property CustDictionaryFile: String read FCustom write SetCustomDict; | 
|---|
|  | 120 | property DictionaryFileName:string read FDictFileName write SetDictFileName; | 
|---|
|  | 121 | property ColorForMisspelled: TColor read FHighlightColor write FHighlightColor default clRed; | 
|---|
|  | 122 | property MisSpeltWord: TEdit read FHighlightEdit write SetHighLightEdit; | 
|---|
|  | 123 | property IsModified: Boolean read FModified; | 
|---|
|  | 124 | property ShowCompletionMessage: Boolean read FShowCompletion write FShowCompletion default True; | 
|---|
|  | 125 | property SourceTextControl: TRichEdit read FSourceEdit write SetSourceEdit; | 
|---|
|  | 126 | property SpellCheckCompletionMessage: String read FpointerSpellComplete write FpointerSpellComplete; | 
|---|
|  | 127 | property SuggestionList: TListbox read FSuggestionList write FSuggestionList; | 
|---|
|  | 128 |  | 
|---|
|  | 129 | end; | 
|---|
|  | 130 |  | 
|---|
|  | 131 | procedure Register; | 
|---|
|  | 132 |  | 
|---|
|  | 133 | Const | 
|---|
|  | 134 | CompletionMessage = 'Spell Check Complete.'; | 
|---|
|  | 135 | CaptionForNewWord = 'New Word Suggestion'; | 
|---|
|  | 136 | ConfirmAbort = 'Really abort?'; | 
|---|
|  | 137 | PromptForNewWord = 'Specify the replacement for current mis-spelt word:'; | 
|---|
|  | 138 | DLLNotLoaded = 'Failed to load SpellCheck Engine DLL.'; | 
|---|
|  | 139 | MisSpeltReplacement = 'The new word specified by you "%s" looks mis-spelt!' | 
|---|
|  | 140 | +' Would you want to still use it?   Click NO button ' | 
|---|
|  | 141 | +'to specify better replacement word.'; | 
|---|
|  | 142 | var | 
|---|
|  | 143 | OldRichEditWndProc: {integer}pointer; | 
|---|
|  | 144 | CurrentMe: TskaHunSpellChecker; | 
|---|
|  | 145 | implementation | 
|---|
|  | 146 | uses messages, Dialogs, RichEdit, SHFolder, Forms, uHunSpellLib; | 
|---|
|  | 147 |  | 
|---|
|  | 148 | procedure Register; | 
|---|
|  | 149 | begin | 
|---|
|  | 150 | RegisterComponentsProc('SkA Utility', [TskaHunSpellChecker]); | 
|---|
|  | 151 | end; | 
|---|
|  | 152 |  | 
|---|
|  | 153 | { TskaHunSpellChecker } | 
|---|
|  | 154 |  | 
|---|
|  | 155 | function TskaHunSpellChecker.AbortSpellCheck(Verbose: Boolean = True): Boolean; | 
|---|
|  | 156 | begin | 
|---|
|  | 157 | Result := (not isModified)  or | 
|---|
|  | 158 | (not Verbose) or (MessageDlg(ConfirmAbort, mtConfirmation, | 
|---|
|  | 159 | [mbYes, mbNo],0, mbNo) = 6); | 
|---|
|  | 160 |  | 
|---|
|  | 161 | if Result then | 
|---|
|  | 162 | FStatus := ssCancelled; | 
|---|
|  | 163 | end; | 
|---|
|  | 164 |  | 
|---|
|  | 165 | function TskaHunSpellChecker.AddCustomWord(aWord: String; isInternal: Boolean = False): Boolean; | 
|---|
|  | 166 | begin | 
|---|
|  | 167 | Result := False; | 
|---|
|  | 168 | if (trim(aWord) = '') or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then | 
|---|
|  | 169 |  | 
|---|
|  | 170 |  | 
|---|
|  | 171 | Result := False; | 
|---|
|  | 172 | if (not Active) then Exit; | 
|---|
|  | 173 | uHunSpellLib.hunspell_put_word(FpointerHunLib, PAnsiChar(AnsiString(aWord))); | 
|---|
|  | 174 | Result := True; | 
|---|
|  | 175 | end; | 
|---|
|  | 176 |  | 
|---|
|  | 177 | procedure TskaHunSpellChecker.ChangeAll; | 
|---|
|  | 178 | begin | 
|---|
|  | 179 | if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then | 
|---|
|  | 180 | exit; | 
|---|
|  | 181 | SaveForUndo; | 
|---|
|  | 182 | SourceTextControl.Text := StringReplace(SourceTextControl.Text, | 
|---|
|  | 183 | CurrentWord, SuggestionList.Items[SuggestionList.ItemIndex], | 
|---|
|  | 184 | [rfReplaceAll,rfIgnoreCase]); | 
|---|
|  | 185 | WaitForUser := False; | 
|---|
|  | 186 | FModified := True; | 
|---|
|  | 187 | SourceTextControl.Invalidate; | 
|---|
|  | 188 |  | 
|---|
|  | 189 | end; | 
|---|
|  | 190 |  | 
|---|
|  | 191 | function TskaHunSpellChecker.AddCustomWord: Boolean; | 
|---|
|  | 192 | begin | 
|---|
|  | 193 | Result := AddCustomWord(CurrentWord, False); | 
|---|
|  | 194 | FCustdict.Add(CurrentWord); | 
|---|
|  | 195 | WaitForUser := False; | 
|---|
|  | 196 | AbortSpellCheck(False); | 
|---|
|  | 197 | Initialize; | 
|---|
|  | 198 | CheckSpelling; | 
|---|
|  | 199 | ShowMisSpelledWord; | 
|---|
|  | 200 | end; | 
|---|
|  | 201 |  | 
|---|
|  | 202 | procedure TskaHunSpellChecker.ReplaceCurrentWordWith(const aNewWord: String); | 
|---|
|  | 203 | var | 
|---|
|  | 204 | full: String; | 
|---|
|  | 205 | prefix: string; | 
|---|
|  | 206 | current: string; | 
|---|
|  | 207 | suffix: string; | 
|---|
|  | 208 | begin | 
|---|
|  | 209 | full := SourceTextControl.Lines[CurrentLine]; | 
|---|
|  | 210 | prefix := copy(CurrentText, 1, WordPos-1); | 
|---|
|  | 211 | Suffix :=  copy(CurrentText, WordPos+WordLength, | 
|---|
|  | 212 | length(CurrentText)); | 
|---|
|  | 213 | SaveForUndo; | 
|---|
|  | 214 | SourceTextControl.Lines[CurrentLine] :=prefix + aNewWord + suffix; | 
|---|
|  | 215 | WaitForUser := False; | 
|---|
|  | 216 | FStatus := ssChecking; | 
|---|
|  | 217 | FModified := True; | 
|---|
|  | 218 | SourceTextControl.Invalidate; | 
|---|
|  | 219 | end; | 
|---|
|  | 220 |  | 
|---|
|  | 221 | function TskaHunSpellChecker.ReStart: Boolean; | 
|---|
|  | 222 | begin | 
|---|
|  | 223 | Close; | 
|---|
|  | 224 | Result := Open; | 
|---|
|  | 225 | Initialize; | 
|---|
|  | 226 | WaitForUser := False; | 
|---|
|  | 227 | SourceTextControl.Invalidate; | 
|---|
|  | 228 | Result := not WaitForUser; | 
|---|
|  | 229 | end; | 
|---|
|  | 230 |  | 
|---|
|  | 231 | procedure TskaHunSpellChecker.Change; | 
|---|
|  | 232 |  | 
|---|
|  | 233 | begin | 
|---|
|  | 234 | if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then | 
|---|
|  | 235 | exit; | 
|---|
|  | 236 | ReplaceCurrentWordWith(SuggestionList.Items[SuggestionList.ItemIndex]); | 
|---|
|  | 237 | end; | 
|---|
|  | 238 |  | 
|---|
|  | 239 | procedure TskaHunSpellChecker.CheckSpelling; | 
|---|
|  | 240 | begin | 
|---|
|  | 241 | if (SpellCheckState = ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then | 
|---|
|  | 242 | exit; | 
|---|
|  | 243 |  | 
|---|
|  | 244 | Initialize; | 
|---|
|  | 245 | FUndoList.Clear; | 
|---|
|  | 246 | WaitForUser := False; | 
|---|
|  | 247 | FStatus := ssChecking; | 
|---|
|  | 248 | SourceTextControl.Invalidate; | 
|---|
|  | 249 | //SourceTextControl.Invalidate; | 
|---|
|  | 250 | end; | 
|---|
|  | 251 |  | 
|---|
|  | 252 | procedure TskaHunSpellChecker.Close; | 
|---|
|  | 253 | begin | 
|---|
|  | 254 | if not Active then Exit; | 
|---|
|  | 255 | uHunSpellLib.hunspell_uninitialize(FpointerHunLib); | 
|---|
|  | 256 | FpointerHunLib := nil; | 
|---|
|  | 257 | end; | 
|---|
|  | 258 |  | 
|---|
|  | 259 |  | 
|---|
|  | 260 | procedure TskaHunSpellChecker.CorrectWithMyWord; | 
|---|
|  | 261 | var | 
|---|
|  | 262 | NewWord: String; | 
|---|
|  | 263 | GotIt: Boolean; | 
|---|
|  | 264 | begin | 
|---|
|  | 265 | if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then | 
|---|
|  | 266 | exit; | 
|---|
|  | 267 |  | 
|---|
|  | 268 | if SuggestionList.Count > 0 then | 
|---|
|  | 269 | NewWord := SuggestionList.Items[0] | 
|---|
|  | 270 | else | 
|---|
|  | 271 | NewWord := CurrentWord; | 
|---|
|  | 272 |  | 
|---|
|  | 273 | GotIt := False; | 
|---|
|  | 274 | while not GotIt do | 
|---|
|  | 275 | begin | 
|---|
|  | 276 | if not InputQuery(CaptionForNewWord, PromptForNewWord, NewWord)  then | 
|---|
|  | 277 | exit; | 
|---|
|  | 278 |  | 
|---|
|  | 279 | GotIt := (not IsMisspelled(NewWord)) | 
|---|
|  | 280 | or (MessageDlg(Format(MisSpeltReplacement,[NewWord]), | 
|---|
|  | 281 | mtWarning, [mbYes, mbNo],0, mbNo) =6) ; | 
|---|
|  | 282 | end; | 
|---|
|  | 283 |  | 
|---|
|  | 284 | if IsMisspelled(NewWord) then | 
|---|
|  | 285 | AddCustomWord(NewWord, True); | 
|---|
|  | 286 |  | 
|---|
|  | 287 | ReplaceCurrentWordWith(NewWord); | 
|---|
|  | 288 | end; | 
|---|
|  | 289 |  | 
|---|
|  | 290 | constructor TskaHunSpellChecker.Create(AOwner: TComponent); | 
|---|
|  | 291 | function GetSpecialFolderPath(folder : integer) : string; | 
|---|
|  | 292 | var | 
|---|
|  | 293 | path: array [0..MAX_PATH] of char; | 
|---|
|  | 294 | begin | 
|---|
|  | 295 | if SUCCEEDED(SHGetFolderPath(0,folder,0,0,@path[0])) then | 
|---|
|  | 296 | Result := path | 
|---|
|  | 297 | else | 
|---|
|  | 298 | Result := ''; | 
|---|
|  | 299 | end; | 
|---|
|  | 300 | begin | 
|---|
|  | 301 | inherited; | 
|---|
|  | 302 |  | 
|---|
|  | 303 | ColorForMisspelled := clRed; | 
|---|
|  | 304 | ShowCompletionMessage := True; | 
|---|
|  | 305 | SpellCheckCompletionMessage := CompletionMessage; | 
|---|
|  | 306 |  | 
|---|
|  | 307 |  | 
|---|
|  | 308 |  | 
|---|
|  | 309 | CurrentMe := Self; | 
|---|
|  | 310 | FIgnore := TStringList.Create; | 
|---|
|  | 311 | FCustDict := TStringList.Create; | 
|---|
|  | 312 |  | 
|---|
|  | 313 | CustDictionaryFile := IncludeTrailingPathDelimiter(GetSpecialFolderPath(CSIDL_PERSONAL)) + 'CustomDictionary.txt'; | 
|---|
|  | 314 | if FileExists(CustDictionaryFile) then | 
|---|
|  | 315 | try | 
|---|
|  | 316 | FCustDict.LoadFromFile(CustDictionaryFile); | 
|---|
|  | 317 | except | 
|---|
|  | 318 | end; | 
|---|
|  | 319 |  | 
|---|
|  | 320 | FUndoList := TStringList.Create; | 
|---|
|  | 321 |  | 
|---|
|  | 322 | FStatus := ssNotStarted; | 
|---|
|  | 323 | WaitForUser := False; | 
|---|
|  | 324 | WordPos := 0; | 
|---|
|  | 325 | end; | 
|---|
|  | 326 |  | 
|---|
|  | 327 | constructor TskaHunSpellChecker.Create(AOwner: TComponent; | 
|---|
|  | 328 | SourceTextRichEdit: TRichedit; SuggestList: TListbox); | 
|---|
|  | 329 | begin | 
|---|
|  | 330 | create(AOwner); | 
|---|
|  | 331 | SourceTextControl := SourceTextRichEdit; | 
|---|
|  | 332 | SuggestionList := SuggestList; | 
|---|
|  | 333 | end; | 
|---|
|  | 334 |  | 
|---|
|  | 335 | function TskaHunSpellChecker.CurrentWordDetail(WithPosition: Boolean): String; | 
|---|
|  | 336 | begin | 
|---|
|  | 337 | Result := '$$' + CurrentWord + '$$'; | 
|---|
|  | 338 | if WithPosition then | 
|---|
|  | 339 | Result := '$$' + IntToStr(FoundAt+1) + Result; | 
|---|
|  | 340 | end; | 
|---|
|  | 341 |  | 
|---|
|  | 342 | destructor TskaHunSpellChecker.Destroy; | 
|---|
|  | 343 | begin | 
|---|
|  | 344 | Close; | 
|---|
|  | 345 | FIgnore.clear; | 
|---|
|  | 346 | FreeAndNil(FIgnore); | 
|---|
|  | 347 | FreeAndNil(FUndoList); | 
|---|
|  | 348 | if not (csDesigning in ComponentState) then | 
|---|
|  | 349 | try | 
|---|
|  | 350 | if FCustDict.Count > 0 then | 
|---|
|  | 351 | try | 
|---|
|  | 352 | FCustDict.SaveToFile(CustDictionaryFile); | 
|---|
|  | 353 | except | 
|---|
|  | 354 | end; | 
|---|
|  | 355 | finally | 
|---|
|  | 356 | FCustDict.Free; | 
|---|
|  | 357 | end; | 
|---|
|  | 358 | inherited; | 
|---|
|  | 359 | end; | 
|---|
|  | 360 |  | 
|---|
|  | 361 | function TskaHunSpellChecker.GetAboutThis: String; | 
|---|
|  | 362 | begin | 
|---|
|  | 363 | Result := AboutThis; | 
|---|
|  | 364 | end; | 
|---|
|  | 365 |  | 
|---|
|  | 366 | function TskaHunSpellChecker.GetActive: Boolean; | 
|---|
|  | 367 | begin | 
|---|
|  | 368 | Result := (FpointerHunLib <> nil); | 
|---|
|  | 369 | end; | 
|---|
|  | 370 |  | 
|---|
|  | 371 | function TskaHunSpellChecker.GetStatus: TSpellState; | 
|---|
|  | 372 | begin | 
|---|
|  | 373 | Result := FStatus; | 
|---|
|  | 374 | end; | 
|---|
|  | 375 |  | 
|---|
|  | 376 | procedure TskaHunSpellChecker.GetSuggestions(const aMisSpeltWord: string; | 
|---|
|  | 377 | const SuggestionList: TStrings); | 
|---|
|  | 378 | var | 
|---|
|  | 379 | i: Integer; | 
|---|
|  | 380 | pMisSpelt: PAnsiChar; | 
|---|
|  | 381 | suggestions: PPAnsiChar; | 
|---|
|  | 382 | Results: PPAnsiChar; | 
|---|
|  | 383 | Count: Integer; | 
|---|
|  | 384 | begin | 
|---|
|  | 385 | if (not Active) or (not Assigned(SuggestionList)) then | 
|---|
|  | 386 | exit; | 
|---|
|  | 387 |  | 
|---|
|  | 388 | pMisSpelt := PAnsiChar(AnsiString(aMisSpeltWord)); | 
|---|
|  | 389 |  | 
|---|
|  | 390 | if not uHunSpellLib.hunspell_spell(FpointerHunLib, pMisSpelt) then | 
|---|
|  | 391 | uHunSpellLib.hunspell_suggest_auto(FpointerHunLib, pMisSpelt, suggestions); | 
|---|
|  | 392 | begin | 
|---|
|  | 393 | Count := uHunSpellLib.hunspell_suggest(FpointerHunLib, pMisSpelt, suggestions); | 
|---|
|  | 394 | Results := suggestions; | 
|---|
|  | 395 | for i := 1 to Count do | 
|---|
|  | 396 | begin | 
|---|
|  | 397 | SuggestionList.Add(Results^); | 
|---|
|  | 398 | Inc(Integer(Results), SizeOf(Pointer)); | 
|---|
|  | 399 | end; | 
|---|
|  | 400 | uHunSpellLib.hunspell_suggest_free(FpointerHunLib, suggestions, Count); | 
|---|
|  | 401 | end; | 
|---|
|  | 402 | end; | 
|---|
|  | 403 |  | 
|---|
|  | 404 | function TskaHunSpellChecker.ShowMisSpelledWord: boolean; | 
|---|
|  | 405 | var | 
|---|
|  | 406 | I , l :integer; | 
|---|
|  | 407 | CharPosion:integer; | 
|---|
|  | 408 | FirstVisibleLine, LastVisibleLine:integer; | 
|---|
|  | 409 |  | 
|---|
|  | 410 | hndl: hwnd; | 
|---|
|  | 411 | dcForHndl: THandle; | 
|---|
|  | 412 | visrect:Trect; | 
|---|
|  | 413 | vispoint:TPoint; | 
|---|
|  | 414 | procedure ShowMisSpelletWord; | 
|---|
|  | 415 | begin | 
|---|
|  | 416 | if Assigned(FHighlightEdit) then | 
|---|
|  | 417 | begin | 
|---|
|  | 418 | FHighlightEdit.Font.Color := ColorForMisspelled; | 
|---|
|  | 419 | FHighlightEdit.Text := CurrentWord; | 
|---|
|  | 420 | FHighlightEdit.Show; | 
|---|
|  | 421 | end ; | 
|---|
|  | 422 |  | 
|---|
|  | 423 | if ((PosOfFirstCharInCurrentLine + FoundAt) < 1) then | 
|---|
|  | 424 | exit; | 
|---|
|  | 425 |  | 
|---|
|  | 426 | SendMessage (SourceTextControl.Handle, EM_POSFROMCHAR, integer(@VisPoint), PosOfFirstCharInCurrentLine + FoundAt-1); | 
|---|
|  | 427 | SetTextColor(dcForHndl, ColorForMisspelled); | 
|---|
|  | 428 | TextOut(dcForHndl,  VisPoint.x,  VisPoint.y,  pchar(CurrentWord), WordLength); | 
|---|
|  | 429 | end; | 
|---|
|  | 430 | begin | 
|---|
|  | 431 | Result := False; | 
|---|
|  | 432 | if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) | 
|---|
|  | 433 | or (not assigned(SuggestionList)) then | 
|---|
|  | 434 | exit; | 
|---|
|  | 435 |  | 
|---|
|  | 436 | hndl:=SourceTextControl.Handle; | 
|---|
|  | 437 |  | 
|---|
|  | 438 | result:= SendMessage (hndl, EM_GETRECT, 0, integer(@visrect))=0; | 
|---|
|  | 439 |  | 
|---|
|  | 440 | dcForHndl := getdc(hndl); | 
|---|
|  | 441 |  | 
|---|
|  | 442 | if result then | 
|---|
|  | 443 | begin | 
|---|
|  | 444 | // VisPoint := visrect.BottomRight; | 
|---|
|  | 445 | vispoint.Y := visrect.Bottom; | 
|---|
|  | 446 | vispoint.X := visrect.Right; | 
|---|
|  | 447 | CharPosion       := SendMessage (hndl, EM_CHARFROMPOS, 0, integer(@VisPoint)); | 
|---|
|  | 448 | LASTVISIBLELINE  := SendMessage (hndl, EM_LINEFROMCHAR, CharPosion, 0); | 
|---|
|  | 449 | FIRSTVISIBLELINE := SendMessage (hndl, EM_GETFIRSTVISIBLELINE, 0, 0); | 
|---|
|  | 450 |  | 
|---|
|  | 451 | SetBkMode (dcForHndl, TRANSPARENT); | 
|---|
|  | 452 | SelectObject(dcForHndl, SourceTextControl.font.Handle); | 
|---|
|  | 453 | i := 0; | 
|---|
|  | 454 |  | 
|---|
|  | 455 | if WaitForUser then | 
|---|
|  | 456 | begin | 
|---|
|  | 457 | ShowMisSpelletWord; | 
|---|
|  | 458 | exit; | 
|---|
|  | 459 | end; | 
|---|
|  | 460 |  | 
|---|
|  | 461 | For l := 0 to SourceTextControl.Lines.Count -1  do | 
|---|
|  | 462 | begin | 
|---|
|  | 463 | {$R-} | 
|---|
|  | 464 | CurrentLine := l; | 
|---|
|  | 465 | if trim(SourceTextControl.Lines[CurrentLine]) = '' then | 
|---|
|  | 466 | continue; | 
|---|
|  | 467 |  | 
|---|
|  | 468 | CurrentText := ' ' + SourceTextControl.Lines[CurrentLine]; | 
|---|
|  | 469 | PosOfFirstCharInCurrentLine := SendMessage (SourceTextControl.Handle, EM_LINEINDEX, CurrentLine, 0); | 
|---|
|  | 470 | i := 0; | 
|---|
|  | 471 |  | 
|---|
|  | 472 | While i <= LENgth(CurrentText) do | 
|---|
|  | 473 | begin | 
|---|
|  | 474 | FoundAt := i -1; | 
|---|
|  | 475 | if Assigned(FHighlightEdit) then | 
|---|
|  | 476 | FHighlightEdit.Hide; | 
|---|
|  | 477 |  | 
|---|
|  | 478 |  | 
|---|
|  | 479 | //SuggestionList.Clear; | 
|---|
|  | 480 | {Any character except these will count as a word delimiter} | 
|---|
|  | 481 | While CurrentText[i] in ['A'..'Z','a'..'z','0'..'9'] do inc(i); | 
|---|
|  | 482 |  | 
|---|
|  | 483 | WordLength        := i- FoundAt -1; | 
|---|
|  | 484 | WordPos           := i-WordLength; | 
|---|
|  | 485 | CurrentWord          := copy(CurrentText, WordPos, WordLength); | 
|---|
|  | 486 | If ((FIgnore.IndexOf(CurrentWordDetail(True))< 0)  //SingelIgnore | 
|---|
|  | 487 | and (FIgnore.IndexOf(CurrentWordDetail(False))< 0) //IgnoreAll | 
|---|
|  | 488 | and (IsMisspelled(CurrentWord))) Then | 
|---|
|  | 489 | begin | 
|---|
|  | 490 | GetSuggestions(CurrentWord, SuggestionList.Items); | 
|---|
|  | 491 | if SuggestionList.Count > 0 then | 
|---|
|  | 492 | SuggestionList.ItemIndex := 0; | 
|---|
|  | 493 | ShowMisSpelletWord; | 
|---|
|  | 494 | if CurrentLine > LastVisibleLine then | 
|---|
|  | 495 | SendMessage(SourceTextControl.Handle, EM_LINESCROLL, 0, (CurrentLine - lastvisibleLine)+5); | 
|---|
|  | 496 | WaitForUser := True; | 
|---|
|  | 497 | exit; | 
|---|
|  | 498 | End | 
|---|
|  | 499 | else | 
|---|
|  | 500 | SuggestionList.Clear; | 
|---|
|  | 501 | inc(i); | 
|---|
|  | 502 | end; | 
|---|
|  | 503 | end; | 
|---|
|  | 504 | if (CurrentLine >= SourceTextControl.Lines.Count-1) and (i >= length(CurrentText) +1) then | 
|---|
|  | 505 | begin | 
|---|
|  | 506 | FStatus := ssCompleted; | 
|---|
|  | 507 | if Assigned(btnClose) then | 
|---|
|  | 508 | btnClose.Click; | 
|---|
|  | 509 | end; | 
|---|
|  | 510 | {$R+} | 
|---|
|  | 511 | end; | 
|---|
|  | 512 | ReleaseDC(SourceTextControl.Handle, dcForHndl); | 
|---|
|  | 513 |  | 
|---|
|  | 514 | End; | 
|---|
|  | 515 |  | 
|---|
|  | 516 | function TskaHunSpellChecker.Undo: Boolean; | 
|---|
|  | 517 | var | 
|---|
|  | 518 | tmpStr: String; | 
|---|
|  | 519 | tmpCount: Integer; | 
|---|
|  | 520 | begin | 
|---|
|  | 521 | if FUndoList.Count > 0 then | 
|---|
|  | 522 | try | 
|---|
|  | 523 | tmpStr := FUndoList.Strings[FUndoList.Count-1]; | 
|---|
|  | 524 | { showmessage(inttostr(AnsiPos('$$',tmpStr)) + #13 + inttostr(length(tmpstr)) + #13 + | 
|---|
|  | 525 | copy(tmpStr,length(tmpStr)-2,2));  } | 
|---|
|  | 526 | if (AnsiPos('$$',tmpStr)=1) and (copy(tmpStr,length(tmpStr)-1,2) = '$$')then | 
|---|
|  | 527 | begin | 
|---|
|  | 528 | tmpCount := strtoInt(StringReplace(tmpStr,'$$','',[rfReplaceAll])); | 
|---|
|  | 529 | while FIgnore.Count > tmpCount do | 
|---|
|  | 530 | FIgnore.Delete(FIgnore.Count -1); | 
|---|
|  | 531 | end | 
|---|
|  | 532 | else | 
|---|
|  | 533 | SourceTextControl.Text := tmpStr; | 
|---|
|  | 534 |  | 
|---|
|  | 535 | Result := True; | 
|---|
|  | 536 | FUndoList.Delete(FUndoList.Count-1); | 
|---|
|  | 537 | ReStart; | 
|---|
|  | 538 | except | 
|---|
|  | 539 | Result := False; | 
|---|
|  | 540 | end; | 
|---|
|  | 541 | end; | 
|---|
|  | 542 |  | 
|---|
|  | 543 | procedure TskaHunSpellChecker.IgnoreAll; | 
|---|
|  | 544 | begin | 
|---|
|  | 545 | if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then | 
|---|
|  | 546 | exit; | 
|---|
|  | 547 | SaveForUndo(True); | 
|---|
|  | 548 | FIgnore.Add(CurrentWordDetail(False)) ; | 
|---|
|  | 549 | WaitForUser := False; | 
|---|
|  | 550 | SourceTextControl.Invalidate; | 
|---|
|  | 551 | end; | 
|---|
|  | 552 |  | 
|---|
|  | 553 | procedure TskaHunSpellChecker.IgnoreOnce; | 
|---|
|  | 554 | begin | 
|---|
|  | 555 | if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then | 
|---|
|  | 556 | exit; | 
|---|
|  | 557 | if trim(CurrentWord) <> '' then | 
|---|
|  | 558 | begin | 
|---|
|  | 559 | SaveForUndo(True); | 
|---|
|  | 560 | FIgnore.Add(CurrentWordDetail(True)) ; | 
|---|
|  | 561 | end; | 
|---|
|  | 562 | WaitForUser := False; | 
|---|
|  | 563 | SourceTextControl.Invalidate; | 
|---|
|  | 564 | end; | 
|---|
|  | 565 |  | 
|---|
|  | 566 | procedure TskaHunSpellChecker.Initialize; | 
|---|
|  | 567 | begin | 
|---|
|  | 568 | CurrentWord := ''; | 
|---|
|  | 569 | WordLength := 0; | 
|---|
|  | 570 | FoundAt := -1; | 
|---|
|  | 571 | CurrentLine := 0; | 
|---|
|  | 572 | WordPos := 0; | 
|---|
|  | 573 | SuggestionList.Clear; | 
|---|
|  | 574 | end; | 
|---|
|  | 575 |  | 
|---|
|  | 576 | function TskaHunSpellChecker.IsMisspelled(const AWord: string): Boolean; | 
|---|
|  | 577 | begin | 
|---|
|  | 578 | if (not Active) then | 
|---|
|  | 579 | Result := True | 
|---|
|  | 580 | else | 
|---|
|  | 581 | Result := not uHunSpellLib.hunspell_spell(FpointerHunLib, PAnsiChar(AnsiString(AWord))); | 
|---|
|  | 582 | end; | 
|---|
|  | 583 |  | 
|---|
|  | 584 | procedure TskaHunSpellChecker.Loaded; | 
|---|
|  | 585 | begin | 
|---|
|  | 586 | inherited; | 
|---|
|  | 587 | SetActive(FActiveOrLoaded); | 
|---|
|  | 588 | end; | 
|---|
|  | 589 |  | 
|---|
|  | 590 | function TskaHunSpellChecker.Open: Boolean; | 
|---|
|  | 591 | var | 
|---|
|  | 592 | CurrentLine: integer; | 
|---|
|  | 593 | begin | 
|---|
|  | 594 | Result := True; | 
|---|
|  | 595 | if Active then Exit; | 
|---|
|  | 596 | Result := False; | 
|---|
|  | 597 | FpointerHunLib := Nil; | 
|---|
|  | 598 | if not uHunSpellLib.LoadLibHunspell('') then | 
|---|
|  | 599 | begin | 
|---|
|  | 600 | MessageDlg(DLLNotLoaded, mtError, [mbOK],0); | 
|---|
|  | 601 | Exit; | 
|---|
|  | 602 | end; | 
|---|
|  | 603 | FpointerHunLib := uHunSpellLib.hunspell_initialize(PAnsiChar(AnsiString(FAffixFileName)), PAnsiChar(AnsiString(FDictFileName))); | 
|---|
|  | 604 | Result := Assigned(FpointerHunLib); | 
|---|
|  | 605 |  | 
|---|
|  | 606 | if (Result) and (assigned(FCustDict)) then | 
|---|
|  | 607 | for CurrentLine := 0 to FCustDict.Count - 1 do | 
|---|
|  | 608 | AddCustomWord(FCustDict[CurrentLine], True); | 
|---|
|  | 609 | end; | 
|---|
|  | 610 |  | 
|---|
|  | 611 | procedure TskaHunSpellChecker.SaveForUndo(const Ignoring: Boolean = False); | 
|---|
|  | 612 | begin | 
|---|
|  | 613 | if Ignoring then | 
|---|
|  | 614 | FUndoList.Add('$$'+ IntToStr(FIgnore.Count)+'$$') | 
|---|
|  | 615 | else | 
|---|
|  | 616 | FUndoList.Add(SourceTextControl.Text); | 
|---|
|  | 617 | end; | 
|---|
|  | 618 |  | 
|---|
|  | 619 | procedure TskaHunSpellChecker.SetActive(const Value: Boolean); | 
|---|
|  | 620 | begin | 
|---|
|  | 621 | if (csDesigning in ComponentState) or (csLoading in ComponentState) then | 
|---|
|  | 622 | FActiveOrLoaded := Value | 
|---|
|  | 623 | else | 
|---|
|  | 624 | if Value then | 
|---|
|  | 625 | FActiveOrLoaded := Open | 
|---|
|  | 626 | else | 
|---|
|  | 627 | Close; | 
|---|
|  | 628 | end; | 
|---|
|  | 629 |  | 
|---|
|  | 630 | procedure TskaHunSpellChecker.SetAffixFileName(const Value: string); | 
|---|
|  | 631 | begin | 
|---|
|  | 632 | Close; | 
|---|
|  | 633 | FAffixFileName := Value; | 
|---|
|  | 634 | end; | 
|---|
|  | 635 |  | 
|---|
|  | 636 | procedure TskaHunSpellChecker.SetbtnClose(const Value: TButton); | 
|---|
|  | 637 | begin | 
|---|
|  | 638 | if btnClose = Value then | 
|---|
|  | 639 | exit; | 
|---|
|  | 640 | FbtnClose := Value; | 
|---|
|  | 641 | FbtnClose.ModalResult := 1; //mrOK | 
|---|
|  | 642 | end; | 
|---|
|  | 643 |  | 
|---|
|  | 644 | procedure TskaHunSpellChecker.SetCustomDict(const Value: String); | 
|---|
|  | 645 | begin | 
|---|
|  | 646 | FCustom := Value; | 
|---|
|  | 647 | if not (csDesigning in componentState) then | 
|---|
|  | 648 | if Active and (FileExists(Value)) then | 
|---|
|  | 649 | FCustDict.LoadFromFile(Value); | 
|---|
|  | 650 | end; | 
|---|
|  | 651 |  | 
|---|
|  | 652 | procedure TskaHunSpellChecker.SetDictFileName(const Value: string); | 
|---|
|  | 653 | begin | 
|---|
|  | 654 | Close; | 
|---|
|  | 655 | FDictFileName := Value; | 
|---|
|  | 656 | end; | 
|---|
|  | 657 |  | 
|---|
|  | 658 | procedure TskaHunSpellChecker.SetHighLightEdit(const Value: TEdit); | 
|---|
|  | 659 | begin | 
|---|
|  | 660 | if FHighlightEdit = Value then | 
|---|
|  | 661 | exit; | 
|---|
|  | 662 |  | 
|---|
|  | 663 | FHighlightEdit := Value; | 
|---|
|  | 664 |  | 
|---|
|  | 665 | if Active then | 
|---|
|  | 666 | FHighlightEdit.Text := CurrentWord; | 
|---|
|  | 667 |  | 
|---|
|  | 668 |  | 
|---|
|  | 669 | end; | 
|---|
|  | 670 |  | 
|---|
|  | 671 | Function RichEditWndProc (handle:HWnd;uMsg,wParam,lParam:longint): longint stdcall; | 
|---|
|  | 672 | begin | 
|---|
|  | 673 | Result := CallWindowProc(OldRichEditWndProc, handle, uMsg, wParam, lParam); | 
|---|
|  | 674 | if (uMsg=WM_PAINT) and assigned(CurrentMe) then CurrentMe.ShowMisSpelledWord; | 
|---|
|  | 675 | End; | 
|---|
|  | 676 |  | 
|---|
|  | 677 | procedure TskaHunSpellChecker.SetSourceEdit(const Value: TRichEdit); | 
|---|
|  | 678 | begin | 
|---|
|  | 679 | if FSourceEdit = Value then | 
|---|
|  | 680 | exit; | 
|---|
|  | 681 |  | 
|---|
|  | 682 | FSourceEdit := Value; | 
|---|
|  | 683 |  | 
|---|
|  | 684 | if csDesigning in ComponentState then | 
|---|
|  | 685 | exit; | 
|---|
|  | 686 |  | 
|---|
|  | 687 | PREditorWndProc:=@RichEditWndProc; | 
|---|
|  | 688 | Value.perform(EM_EXLIMITTEXT, 0, 65535*32); //raise the limit of text which could be inserted into this Richedit | 
|---|
|  | 689 | OldRichEditWndProc := pointer(SetWindowLong(Value.handle, GWL_WNDPROC, longint(@RichEditWndProc))); | 
|---|
|  | 690 |  | 
|---|
|  | 691 | end; | 
|---|
|  | 692 |  | 
|---|
|  | 693 | end. | 
|---|