source: cprs/branches/HealthSevak-CPRS/CPRS-Lib/Hans SpellCheck/skaSpellCheck.pas@ 1722

Last change on this file since 1722 was 1722, checked in by healthsevak, 9 years ago

Did few minor modifications related to restarting spell check etc

File size: 27.1 KB
Line 
1unit 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 *Special Note:
20 * This work has heavily relies upon rather build upon Copyrighted work by
21 * Miha Vrhovnik (http://simail.sf.net, http://xcollect.sf.net) which is
22 * available at http://sourceforge.net/projects/hunspell/
23 *
24 * Alternatively, the content of this file maybe used under the terms of either
25 * the GNU General Public License Version 2 or later (the "GPL"), or the GNU
26 * Lesser General Public License Version 2.1 or later (the "LGPL"), in which
27 * case the provisions of the GPL or the LGPL are applicable instead of those
28 * above. If you wish to allow use of your version of this file only under the
29 * terms of either the GPL or the LGPL, and not to allow others to use your
30 * version of this file under the terms of the MPL, indicate your division by
31 * deleting the provisions above and replace them with the notice and other
32 * provisions required by the GPL or LGPL. If you do not delete the provisions
33 * above, a recipient may use your version of this file under the terms of any
34 * one of the MPL, the GPL or the LGPL.
35 *
36 * *********************** END LICENSE BLOCK *********************************)
37
38interface
39
40uses
41 Windows, Classes, SysUtils, ComCtrls, StdCtrls, Graphics, Forms, Controls;
42
43 const
44 AboutThis = 'A wrapper component developed by Sunil K Arora '
45 + '(digitiger@gmail.com) of HealthSevak using OpenSource HanSpell engine';
46type
47 TSpellState = (ssNoengine, ssInActive, ssReady, ssChecking, ssCancelled,
48 ssCompleted);
49 TStateChangeEvent = procedure (const Sender : TObject;
50 const State : TSpellState) of object;
51
52 TskaHunSpellChecker = class(TComponent)
53 private
54 FActiveOrLoaded: Boolean;
55 FpointerHunLib: Pointer;
56 FSourceEdit: TRichEdit;
57 FSuggestionList: TListbox;
58
59 FAffixFileName: string;
60 FDictFileName: string;
61 CurrentWord: String;
62 CurrentText: String;
63 FoundAt: Integer;
64 PosOfFirstCharInCurrentLine: integer;
65 CurrentLine: Integer;
66 FIgnore: TStringList;
67 WaitForUser: Boolean;
68 WordLength:integer;
69 WordPos: Integer;
70 PREditorWndProc:pointer;
71 FHighlightColor: TColor;
72 FUndoList: TStringList;
73 FCustDict: TStringList;
74 FCustom: String;
75 FModified: Boolean;
76 FHighlightEdit: TEdit;
77 FTxtBeforeManualEdit: String;
78
79 FStatus: TSpellState;
80 FOnStart: TNotifyEvent;
81 FOnAbort : TNotifyEvent;
82 FOnStateChange : TStateChangeEvent;
83 FIgnoreWordWdigits: boolean;
84 FIgnoreCaps: boolean;
85 FOptionsKey: String;
86 function AddCustomWord(aWord: String; isInternal: Boolean = False): Boolean;
87 overload; virtual;
88 Function CurrentWordDetail(WithPosition: Boolean= True): String;
89 function GetActive: Boolean;
90 procedure GetOptions;
91 function GetStatus: TSpellState;
92 procedure Initialize;
93 procedure SetActive(const Value: Boolean);
94 procedure SetAffixFileName(const Value: string);
95 procedure SetCustomDict(const Value: String);
96 procedure SetDictFileName(const Value: string);
97 procedure SetHighLightEdit(const Value: TEdit);
98 procedure SetOptions;
99 procedure SetSourceEdit(const Value: TRichEdit);
100 Function ShowMisSpelledWord:boolean;
101 procedure Loaded; override;
102 procedure ReplaceCurrentWordWith(const aNewWord: String);
103 function GetAboutThis: String;
104 procedure SaveForUndo(const Ignoring: Boolean=False);
105 procedure InformStatusChange;
106 procedure SetIgnoreCaps(const Value: boolean);
107 procedure SetIgnoreWordWdigits(const Value: boolean);
108 public
109 constructor Create(AOwner: TComponent); overload; override;
110 constructor Create(AOwner: TComponent; SourceTextRichEdit: TRichedit;
111 SuggestList: TListbox); ReIntroduce; overload;
112 destructor Destroy; override;
113
114 function AbortSpellCheck(Verbose: Boolean = True):Boolean;
115 function AddCustomWord: Boolean; overload; virtual;
116 procedure Change;
117 procedure ChangeAll;
118 procedure CheckSpelling;
119 procedure Close; virtual;
120 procedure CorrectWithMyWord;
121 procedure GetSuggestions(const aMisSpeltWord: string;
122 const SuggestionList: TStrings); dynamic;
123 procedure IgnoreAll;
124 procedure IgnoreOnce;
125 function IsMisspelled(const AWord: string): Boolean; dynamic;
126 procedure ManualChangeStart;
127 procedure ManualChangeDone;
128 function Open:Boolean; virtual;
129 procedure ReOpen;
130 function ReStart: Boolean; virtual;
131 function Undo: Boolean;
132 property SpellCheckState: TSpellState read GetStatus default ssInActive;
133 published
134 property About: String read GetAboutThis;
135 property Active: Boolean read GetActive write SetActive;
136 property AffixFileName: string read FAffixFileName write SetAffixFileName;
137 property IgnoreAllCaps: boolean read FIgnoreCaps write SetIgnoreCaps default true;
138 property IgnoreWordWithDigits:boolean read FIgnoreWordWdigits write SetIgnoreWordWdigits default true;
139 property CustDictionaryFile: String read FCustom write SetCustomDict;
140 property DictionaryFileName:string read FDictFileName write SetDictFileName;
141 property ColorForMisspelled: TColor read FHighlightColor
142 write FHighlightColor default clRed;
143 property MisSpeltWord: TEdit read FHighlightEdit write SetHighLightEdit;
144 property IsModified: Boolean read FModified;
145 property OnStart : TNotifyEvent read FOnStart write FOnStart;
146 property OnStateChange : TStateChangeEvent read FOnStateChange
147 write FOnStateChange;
148 property OnAbort : TNotifyEvent read FOnAbort write FOnAbort;
149 property SourceTextControl: TRichEdit read FSourceEdit write SetSourceEdit;
150 property SuggestionList:TListbox read FSuggestionList write FSuggestionList;
151
152 end;
153
154 procedure Register;
155
156 Const
157 CompletionMessage = 'Spell Check Complete.';
158 CaptionForNewWord = 'New Word Suggestion';
159 ConfirmAbort = 'Really abort?';
160 ConfirmComplete = 'If you accept last change than SpellCheck is complete.'
161 + #13 + ' To review last change click on "Cancel".';
162 PromptForNewWord = 'Specify the replacement for current mis-spelt word:';
163 DLLNotLoaded = 'Failed to load SpellCheck Engine DLL.';
164 MisSpeltReplacement = 'The new word specified by you "%s" looks mis-spelt!'
165 +' Would you want to still use it? Click NO button '
166 +'to specify better replacement word.';
167
168 var
169 OldRichEditWndProc: {integer}pointer;
170 CurrentMe: TskaHunSpellChecker;
171implementation
172 uses messages, Dialogs, RichEdit, SHFolder, uHunSpellLib, Registry;
173
174procedure Register;
175begin
176 RegisterComponentsProc('SkA Utility', [TskaHunSpellChecker]);
177end;
178
179{ TskaHunSpellChecker }
180
181function TskaHunSpellChecker.AbortSpellCheck(Verbose: Boolean = True): Boolean;
182begin
183 if FStatus <> ssChecking then
184 begin
185 FStatus := ssCancelled;
186 Close;
187 exit;
188 end;
189
190 Result := (not isModified) or
191 (not Verbose) or (MessageDlg(ConfirmAbort, mtConfirmation,
192 [mbYes, mbNo],0, mbNo) = 6);
193
194 if Result then
195 begin
196 if FUndoList.Count > 0 then
197 SourceTextControl.Text := FUndoList[0];
198 FUndoList.Clear;
199 FUndoList.Add(SourceTextControl.Text);
200 FIgnore.Clear;
201 FStatus := ssCancelled;
202 SourceTextControl.Invalidate;
203 if Assigned(OnAbort) then
204 OnAbort(Self);
205 end;
206end;
207
208function TskaHunSpellChecker.AddCustomWord(aWord: String;
209 isInternal: Boolean = False): Boolean;
210begin
211 Result := False;
212 if (not active) or (trim(aWord) = '') or (SpellCheckState <> ssChecking)
213 or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then
214 begin
215 Result := False;
216 exit;
217 end;
218 uHunSpellLib.hunspell_put_word(FpointerHunLib, PAnsiChar(AnsiString(aWord)));
219 Result := True;
220end;
221
222procedure TskaHunSpellChecker.ChangeAll;
223begin
224 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
225 or (not assigned(SuggestionList)) then
226 exit;
227 SaveForUndo;
228 SourceTextControl.Text := StringReplace(SourceTextControl.Text,
229 CurrentWord, SuggestionList.Items[SuggestionList.ItemIndex],
230 [rfReplaceAll,rfIgnoreCase]);
231 WaitForUser := False;
232 FModified := True;
233 SourceTextControl.Invalidate;
234
235end;
236
237function TskaHunSpellChecker.AddCustomWord: Boolean;
238begin
239 Result := AddCustomWord(CurrentWord, False);
240 FCustdict.Add(CurrentWord);
241 WaitForUser := False;
242 SourceTextControl.Invalidate;
243end;
244
245procedure TskaHunSpellChecker.ReOpen;
246begin
247 Close;
248 Open;
249end;
250
251procedure TskaHunSpellChecker.ReplaceCurrentWordWith(const aNewWord: String);
252var
253 full: String;
254 prefix: string;
255 suffix: string;
256begin
257 full := SourceTextControl.Lines[CurrentLine];
258{remember there is one extra space at the start of the line prefixed while
259 populating this variable}
260 prefix := copy(CurrentText, 2, WordPos-2);
261 Suffix := copy(CurrentText, WordPos+WordLength,
262 length(CurrentText));
263 SaveForUndo;
264 FModified := True;
265 SourceTextControl.Lines[CurrentLine] :=prefix + aNewWord + suffix;
266 WaitForUser := False;
267 FStatus := ssChecking;
268 FModified := True;
269 SourceTextControl.Invalidate;
270end;
271
272function TskaHunSpellChecker.ReStart: Boolean;
273begin
274 Close;
275 Result := Open;
276 Initialize;
277 WaitForUser := False;
278 if FStatus <> ssChecking then
279 begin
280 FStatus := ssChecking;
281 InformStatusChange;
282 end;
283 SourceTextControl.Invalidate;
284 Result := not WaitForUser;
285end;
286
287procedure TskaHunSpellChecker.Change;
288
289begin
290 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
291 or (not assigned(SuggestionList)) then
292 exit;
293 ReplaceCurrentWordWith(SuggestionList.Items[SuggestionList.ItemIndex]);
294end;
295
296procedure TskaHunSpellChecker.CheckSpelling;
297begin
298 if (SpellCheckState = ssChecking) or (not assigned(SourceTextControl))
299 or (trim(SourceTextControl.Text)= '') or (not assigned(SuggestionList)) then
300 exit;
301
302 Initialize;
303 FUndoList.Clear;
304 FUndoList.Add(SourceTextControl.Text);
305 FIgnore.Clear;
306 WaitForUser := False;
307 FStatus := ssChecking;
308 if Assigned(OnStart) then
309 OnStart(Self);
310 SourceTextControl.Invalidate;
311end;
312
313procedure TskaHunSpellChecker.Close;
314begin
315 if not Active then Exit;
316 uHunSpellLib.hunspell_uninitialize(FpointerHunLib);
317 FpointerHunLib := nil;
318 FStatus := ssInActive;
319 InformStatusChange;
320end;
321
322
323procedure TskaHunSpellChecker.CorrectWithMyWord;
324var
325 NewWord: String;
326 GotIt: Boolean;
327begin
328 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
329 or (not assigned(SuggestionList)) then
330 exit;
331
332 if SuggestionList.Count > 0 then
333 NewWord := SuggestionList.Items[0]
334 else
335 NewWord := CurrentWord;
336
337 GotIt := False;
338 while not GotIt do
339 begin
340 if not InputQuery(CaptionForNewWord, PromptForNewWord, NewWord) then
341 exit;
342
343 GotIt := (not IsMisspelled(NewWord))
344 or (MessageDlg(Format(MisSpeltReplacement,[NewWord]),
345 mtWarning, [mbYes, mbNo],0, mbNo) =6) ;
346 end;
347
348 if IsMisspelled(NewWord) then
349 AddCustomWord(NewWord, True);
350
351 ReplaceCurrentWordWith(NewWord);
352end;
353
354constructor TskaHunSpellChecker.Create(AOwner: TComponent);
355begin
356 inherited;
357 ColorForMisspelled := clRed;
358
359 CurrentMe := Self;
360 FIgnore := TStringList.Create;
361 FCustDict := TStringList.Create;
362
363 if (trim(CustDictionaryFile)<>'') and (FileExists(CustDictionaryFile)) then
364 try
365 FCustDict.LoadFromFile(CustDictionaryFile);
366 except
367 end;
368
369 FUndoList := TStringList.Create;
370
371 FOptionsKey := '\software\'
372 + ChangeFileExt(ExtractFileName(Application.ExeName),'')
373 + '\skaHunSpellCheckOptions';
374 if csDesigning in componentState then
375 begin
376 IgnoreAllCaps := True;
377 IgnoreWordWithDigits := True;
378 end
379 else
380 GetOptions;
381
382 FStatus := ssInActive;
383 WaitForUser := False;
384 WordPos := 0;
385end;
386
387constructor TskaHunSpellChecker.Create(AOwner: TComponent;
388 SourceTextRichEdit: TRichedit; SuggestList: TListbox);
389begin
390 create(AOwner);
391 SourceTextControl := SourceTextRichEdit;
392 SuggestionList := SuggestList;
393end;
394
395function TskaHunSpellChecker.CurrentWordDetail(WithPosition: Boolean): String;
396begin
397 Result := '$$' + CurrentWord + '$$';
398 if WithPosition then
399 Result :='$$' + IntToStr(CurrentLine) + '$$' + IntToStr(FoundAt+1) + Result;
400end;
401
402destructor TskaHunSpellChecker.Destroy;
403begin
404 Close;
405 FIgnore.clear;
406 FreeAndNil(FIgnore);
407 FreeAndNil(FUndoList);
408 if not (csDesigning in ComponentState) then
409 try
410 if FCustDict.Count > 0 then
411 try
412 FCustDict.SaveToFile(CustDictionaryFile);
413 except
414 end;
415 SetOptions;
416 finally
417 FCustDict.Free;
418 end;
419 inherited;
420end;
421
422function TskaHunSpellChecker.GetAboutThis: String;
423begin
424 Result := AboutThis;
425end;
426
427function TskaHunSpellChecker.GetActive: Boolean;
428begin
429 Result := (FpointerHunLib <> nil);
430end;
431
432procedure TskaHunSpellChecker.GetOptions;
433var
434reg:TRegistry;
435begin
436 reg:=TRegistry.Create;
437 try
438 reg.RootKey := HKEY_CURRENT_USER;
439
440 //first get the dicationary file name
441 Reg.OpenKey(FOptionsKey,True);
442 if reg.ValueExists('DicFileName') then
443 DictionaryFileName:=Reg.readString('DicFileName')
444 else
445 Reg.WriteString('DicFileName',DictionaryFileName);
446
447 //IgnoreAllCaps ?
448 if reg.ValueExists('IgnoreAllCaps') then
449 IgnoreAllCaps:=Reg.readBool('IgnoreAllCaps')
450 else
451 Reg.WriteBool('IgnoreAllCaps',IgnoreAllCaps);
452
453
454 //IgnoreWordsWithDigits ?
455 if reg.ValueExists('IgnoreWordWithDigits') then
456 IgnoreWordWithDigits:=Reg.readBool('IgnoreWordWithDigits')
457 else
458 Reg.WriteBool('IgnoreWordWithDigits',IgnoreWordWithDigits);
459
460 finally
461 Reg.Free;
462 end;
463
464end;
465
466function TskaHunSpellChecker.GetStatus: TSpellState;
467begin
468 Result := FStatus;
469end;
470
471procedure TskaHunSpellChecker.GetSuggestions(const aMisSpeltWord: string;
472 const SuggestionList: TStrings);
473var
474 i: Integer;
475 pMisSpelt: PAnsiChar;
476 suggestions: PPAnsiChar;
477 Results: PPAnsiChar;
478 Count: Integer;
479begin
480 if (not Active) or (not Assigned(SuggestionList)) then
481 exit;
482
483 pMisSpelt := PAnsiChar(AnsiString(aMisSpeltWord));
484
485 if not uHunSpellLib.hunspell_spell(FpointerHunLib, pMisSpelt) then
486 uHunSpellLib.hunspell_suggest_auto(FpointerHunLib, pMisSpelt, suggestions);
487 begin
488 Count :=uHunSpellLib.hunspell_suggest(FpointerHunLib,pMisSpelt,suggestions);
489 Results := suggestions;
490 for i := 1 to Count do
491 begin
492 SuggestionList.Add(Results^);
493 Inc(Integer(Results), SizeOf(Pointer));
494 end;
495 uHunSpellLib.hunspell_suggest_free(FpointerHunLib, suggestions, Count);
496 end;
497end;
498
499function TskaHunSpellChecker.ShowMisSpelledWord: boolean;
500var
501 I , l :integer;
502 CharPosion:integer;
503 FirstVisibleLine, LastVisibleLine:integer;
504
505 hndl: hwnd;
506 dcForHndl: THandle;
507 visrect:Trect;
508 vispoint:TPoint;
509 procedure ShowMisSpelletWord;
510 begin
511 if Assigned(FHighlightEdit) then
512 begin
513 FHighlightEdit.Font.Color := ColorForMisspelled;
514 FHighlightEdit.Text := CurrentWord;
515 FHighlightEdit.Show;
516 end ;
517
518 if ((PosOfFirstCharInCurrentLine + FoundAt) < 1) then
519 exit;
520
521 SendMessage (SourceTextControl.Handle, EM_POSFROMCHAR, integer(@VisPoint),
522 PosOfFirstCharInCurrentLine + FoundAt-1);
523 SetTextColor(dcForHndl, ColorForMisspelled);
524 TextOut(dcForHndl, VisPoint.x, VisPoint.y, pchar(CurrentWord), WordLength);
525 end;
526
527 function WordIsIgnorable: Boolean;
528 var
529 i: Integer;
530 begin
531 Result := False;
532 if IgnoreAllCaps then
533 begin
534 Result := True;
535 for i := 1 to WordLength do
536 begin
537 Result := Result and (ord(CurrentWord[i]) in [65..90]);
538 end;
539 if Result then
540 exit;
541 end;
542
543
544
545 if IgnoreWordWithDigits then
546 begin
547 Result := False;
548 for i := 1 to WordLength do
549 begin
550 Result := Result or (ord(CurrentWord[i]) in [48..57]);
551 if Result then
552 break;
553 end;
554 end;
555 end;
556begin
557 Result := False;
558 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
559 or (not assigned(SuggestionList)) then
560 exit;
561
562 hndl:=SourceTextControl.Handle;
563
564 result:= SendMessage (hndl, EM_GETRECT, 0, integer(@visrect))=0;
565
566 dcForHndl := getdc(hndl);
567
568 if result then
569 begin
570 // VisPoint := visrect.BottomRight;
571 vispoint.Y := visrect.Bottom;
572 vispoint.X := visrect.Right;
573 CharPosion := SendMessage (hndl, EM_CHARFROMPOS, 0, integer(@VisPoint));
574 LASTVISIBLELINE := SendMessage (hndl, EM_LINEFROMCHAR, CharPosion, 0);
575 FIRSTVISIBLELINE := SendMessage (hndl, EM_GETFIRSTVISIBLELINE, 0, 0);
576
577 SetBkMode (dcForHndl, TRANSPARENT);
578 SelectObject(dcForHndl, SourceTextControl.font.Handle);
579 i := 0;
580
581 if WaitForUser then
582 begin
583 ShowMisSpelletWord;
584 exit;
585 end;
586
587 For l := 0 to SourceTextControl.Lines.Count -1 do
588 begin
589 {$R-}
590 CurrentLine := l;
591 if trim(SourceTextControl.Lines[CurrentLine]) = '' then
592 continue;
593
594 CurrentText := ' ' + SourceTextControl.Lines[CurrentLine];
595 PosOfFirstCharInCurrentLine := SendMessage (SourceTextControl.Handle,
596 EM_LINEINDEX, CurrentLine, 0);
597 i := 0;
598
599 While i <= LENgth(CurrentText) do
600 begin
601 FoundAt := i -1;
602 if Assigned(FHighlightEdit) then
603 FHighlightEdit.Hide;
604
605
606 //SuggestionList.Clear;
607 {Any character except these will count as a word delimiter}
608 While CurrentText[i] in ['A'..'Z','a'..'z','0'..'9'] do inc(i);
609
610 WordLength := i- FoundAt -1;
611 WordPos := i-WordLength;
612 CurrentWord := copy(CurrentText, WordPos, WordLength);
613 // if WordIsCorrect then
614 if (((FIgnore.IndexOf(CurrentWordDetail(True))< 0) //SingelIgnore
615 and (FIgnore.IndexOf(CurrentWordDetail(False))< 0) //IgnoreAll
616 and (IsMisspelled(CurrentWord))))
617 and (not WordIsIgnorable) then
618
619 begin
620 GetSuggestions(CurrentWord, SuggestionList.Items);
621 if SuggestionList.Count > 0 then
622 SuggestionList.ItemIndex := 0;
623 ShowMisSpelletWord;
624 if CurrentLine > LastVisibleLine then
625 SendMessage(SourceTextControl.Handle, EM_LINESCROLL, 0,
626 (CurrentLine - lastvisibleLine)+5);
627 WaitForUser := True;
628 exit;
629 End
630 else
631 SuggestionList.Clear;
632 inc(i);
633 end;
634 end;
635 if (CurrentLine >= SourceTextControl.Lines.Count-1)
636 and (i >= length(CurrentText) +1) then
637 begin
638 if (not FModified)
639 or (MessageDlg(ConfirmComplete,mtConfirmation,[mbOK, mbCancel],0)=mrOk)
640 then
641 begin
642 FStatus := ssCompleted;
643 InformStatusChange;
644 end
645 else
646 Undo;
647 end;
648 {$R+}
649 end;
650 ReleaseDC(SourceTextControl.Handle, dcForHndl);
651
652End;
653
654function TskaHunSpellChecker.Undo: Boolean;
655var
656 tmpStr: String;
657 tmpCount: Integer;
658 SrcText: String;
659begin
660 if FUndoList.Count > 1 then
661 try
662 tmpStr := FUndoList.Strings[FUndoList.Count-1];
663 if (AnsiPos('$$',tmpStr)=1) and (copy(tmpStr,length(tmpStr)-1,2) = '$$')then
664 //if last action was ignoring word then just remove it from ignore list
665 begin
666 tmpCount := strtoInt(StringReplace(tmpStr,'$$','',[rfReplaceAll]));
667 while FIgnore.Count > tmpCount do
668 FIgnore.Delete(FIgnore.Count -1);
669 end
670 else
671 SourceTextControl.Text := tmpStr;
672
673 Result := True;
674 FUndoList.Delete(FUndoList.Count-1);
675 ReStart;
676 except
677 Result := False;
678 end;
679end;
680
681procedure TskaHunSpellChecker.IgnoreAll;
682begin
683 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
684 or (not assigned(SuggestionList)) then
685 exit;
686
687 SaveForUndo(True);
688 FIgnore.Add(CurrentWordDetail(False)) ;
689 WaitForUser := False;
690 SourceTextControl.Invalidate;
691end;
692
693procedure TskaHunSpellChecker.IgnoreOnce;
694begin
695 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
696 or (not assigned(SuggestionList)) then
697 exit;
698
699 if trim(CurrentWord) <> '' then
700 begin
701 SaveForUndo(True);
702 FIgnore.Add(CurrentWordDetail(True)) ;
703 end;
704 WaitForUser := False;
705 SourceTextControl.Invalidate;
706end;
707
708procedure TskaHunSpellChecker.InformStatusChange;
709begin
710 if Assigned(OnStateChange) then
711 OnStateChange(Self, FStatus);
712end;
713
714procedure TskaHunSpellChecker.Initialize;
715begin
716 CurrentWord := '';
717 WordLength := 0;
718 FoundAt := -1;
719 CurrentLine := 0;
720 WordPos := 0;
721 SuggestionList.Clear;
722end;
723
724function TskaHunSpellChecker.IsMisspelled(const AWord: string): Boolean;
725begin
726 if (not Active) then
727 Result := True
728 else
729 Result := not uHunSpellLib.hunspell_spell(FpointerHunLib,
730 PAnsiChar(AnsiString(AWord)));
731end;
732
733procedure TskaHunSpellChecker.Loaded;
734begin
735 inherited;
736 SetActive(FActiveOrLoaded);
737end;
738
739procedure TskaHunSpellChecker.ManualChangeDone;
740begin
741 if trim(FTxtBeforeManualEdit) = '' then
742 exit;
743 FUndoList.Add(FTxtBeforeManualEdit);
744 ReStart;
745end;
746
747procedure TskaHunSpellChecker.ManualChangeStart;
748begin
749 FTxtBeforeManualEdit := FSourceEdit.Text;
750end;
751
752function TskaHunSpellChecker.Open: Boolean;
753var
754 CurrentLine: integer;
755 function GetSpecialFolderPath(folder : integer) : string;
756 var
757 path: array [0..MAX_PATH] of char;
758 begin
759 if SUCCEEDED(SHGetFolderPath(0,folder,0,0,@path[0])) then
760 Result := path
761 else
762 Result := '';
763 end;
764begin
765 Result := True;
766 if Active then Exit;
767 Result := False;
768 FpointerHunLib := Nil;
769 if not uHunSpellLib.LoadLibHunspell('') then
770 begin
771 MessageDlg(DLLNotLoaded, mtError, [mbOK],0);
772 Exit;
773 end;
774 FpointerHunLib := uHunSpellLib.hunspell_initialize(
775 PAnsiChar(AnsiString(FAffixFileName)),
776 PAnsiChar(AnsiString(FDictFileName)));
777 Result := Assigned(FpointerHunLib);
778
779 if Result then
780 begin
781 FStatus := ssReady;
782 InformStatusChange;
783 end;
784 if trim(CustDictionaryFile) = '' then
785 CustDictionaryFile := IncludeTrailingPathDelimiter(
786 GetSpecialFolderPath(CSIDL_PERSONAL)) + 'CustomDictionary.txt';
787 if (Result) and (assigned(FCustDict)) then
788 for CurrentLine := 0 to FCustDict.Count - 1 do
789 AddCustomWord(FCustDict[CurrentLine], True);
790end;
791
792procedure TskaHunSpellChecker.SaveForUndo(const Ignoring: Boolean = False);
793begin
794 if Ignoring then
795 FUndoList.Add('$$'+ IntToStr(FIgnore.Count)+'$$')
796 else
797 FUndoList.Add(SourceTextControl.Text);
798end;
799
800procedure TskaHunSpellChecker.SetActive(const Value: Boolean);
801begin
802 if (csDesigning in ComponentState) or (csLoading in ComponentState) then
803 FActiveOrLoaded := Value
804 else
805 if Value then
806 FActiveOrLoaded := Open
807 else
808 Close;
809end;
810
811procedure TskaHunSpellChecker.SetAffixFileName(const Value: string);
812begin
813 Close;
814 FAffixFileName := Value;
815 if (trim(DictionaryFileName) = '') and (trim(value)<>'') then
816 DictionaryFileName := ChangeFileExt(value, '.dic');
817end;
818
819procedure TskaHunSpellChecker.SetCustomDict(const Value: String);
820begin
821 FCustom := Value;
822 if (not (csDesigning in componentState))
823 and (FileExists(Value)) and assigned(FCustDict) then
824 begin
825 FCustDict.Clear;
826 FCustDict.LoadFromFile(Value);
827 end;
828end;
829
830procedure TskaHunSpellChecker.SetDictFileName(const Value: string);
831begin
832 Close;
833 FDictFileName := Value;
834 if (trim(AffixFileName) = '') and (trim(value)<>'') then
835 AffixFileName := ChangeFileExt(value, '.aff');
836end;
837
838procedure TskaHunSpellChecker.SetHighLightEdit(const Value: TEdit);
839begin
840 if FHighlightEdit = Value then
841 exit;
842
843 FHighlightEdit := Value;
844
845 if Active then
846 FHighlightEdit.Text := CurrentWord;
847
848
849end;
850
851procedure TskaHunSpellChecker.SetIgnoreCaps(const Value: boolean);
852begin
853 if (FIgnoreCaps = Value) then
854 exit;
855
856 FIgnoreCaps := Value;
857 if SpellCheckState = ssChecking then
858 ReStart;
859end;
860
861procedure TskaHunSpellChecker.SetIgnoreWordWdigits(const Value: boolean);
862begin
863 if (FIgnoreWordWdigits = Value) then
864 exit;
865
866 FIgnoreWordWdigits := Value;
867 if SpellCheckState = ssChecking then
868 ReStart;
869
870end;
871
872procedure TskaHunSpellChecker.SetOptions;
873var
874reg:TRegistry;
875begin
876 reg:=TRegistry.Create;
877 try
878 reg.RootKey := HKEY_CURRENT_USER;
879
880
881 Reg.OpenKey(FOptionsKey,True);
882
883 //first save the dicationary file name
884 Reg.WriteString('DicFileName',DictionaryFileName);
885
886 //IgnoreAllCaps ?
887 Reg.WriteBool('IgnoreAllCaps',IgnoreAllCaps);
888
889
890 //IgnoreWordsWithDigits ?
891 Reg.WriteBool('IgnoreWordWithDigits',IgnoreWordWithDigits);
892
893 finally
894 Reg.Free;
895 end;
896end;
897
898Function RichEditWndProc(handle:HWnd;uMsg,wParam,lParam:longint):longint stdcall;
899begin
900 Result := CallWindowProc(OldRichEditWndProc, handle, uMsg, wParam, lParam);
901 if (uMsg=WM_PAINT) and assigned(CurrentMe) then
902 CurrentMe.ShowMisSpelledWord;
903End;
904
905procedure TskaHunSpellChecker.SetSourceEdit(const Value: TRichEdit);
906begin
907 if FSourceEdit = Value then
908 exit;
909
910 FSourceEdit := Value;
911
912 if csDesigning in ComponentState then
913 exit;
914
915 PREditorWndProc:=@RichEditWndProc;
916 //raise the limit of text which could be inserted into this Richedit
917 Value.perform(EM_EXLIMITTEXT, 0, 65535*32);
918 OldRichEditWndProc := pointer(SetWindowLong(Value.handle, GWL_WNDPROC,
919 longint(@RichEditWndProc)));
920end;
921
922end.
Note: See TracBrowser for help on using the repository browser.