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

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

Fixed a small issue related to saving of SpellCheckOptions in Win registry under application name.

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 AbortSpellCheck(False);
243 Initialize;
244 CheckSpelling;
245 ShowMisSpelledWord;
246end;
247
248procedure TskaHunSpellChecker.ReOpen;
249begin
250 Close;
251 Open;
252end;
253
254procedure TskaHunSpellChecker.ReplaceCurrentWordWith(const aNewWord: String);
255var
256 full: String;
257 prefix: string;
258 suffix: string;
259begin
260 full := SourceTextControl.Lines[CurrentLine];
261{remember there is one extra space at the start of the line prefixed while
262 populating this variable}
263 prefix := copy(CurrentText, 2, WordPos-2);
264 Suffix := copy(CurrentText, WordPos+WordLength,
265 length(CurrentText));
266 SaveForUndo;
267 FModified := True;
268 SourceTextControl.Lines[CurrentLine] :=prefix + aNewWord + suffix;
269 WaitForUser := False;
270 FStatus := ssChecking;
271 FModified := True;
272 SourceTextControl.Invalidate;
273end;
274
275function TskaHunSpellChecker.ReStart: Boolean;
276begin
277 Close;
278 Result := Open;
279 Initialize;
280 WaitForUser := False;
281 if FStatus <> ssChecking then
282 begin
283 FStatus := ssChecking;
284 InformStatusChange;
285 end;
286 SourceTextControl.Invalidate;
287 Result := not WaitForUser;
288end;
289
290procedure TskaHunSpellChecker.Change;
291
292begin
293 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
294 or (not assigned(SuggestionList)) then
295 exit;
296 ReplaceCurrentWordWith(SuggestionList.Items[SuggestionList.ItemIndex]);
297end;
298
299procedure TskaHunSpellChecker.CheckSpelling;
300begin
301 if (SpellCheckState = ssChecking) or (not assigned(SourceTextControl))
302 or (trim(SourceTextControl.Text)= '') or (not assigned(SuggestionList)) then
303 exit;
304
305 Initialize;
306 FUndoList.Clear;
307 FUndoList.Add(SourceTextControl.Text);
308 FIgnore.Clear;
309 WaitForUser := False;
310 FStatus := ssChecking;
311 if Assigned(OnStart) then
312 OnStart(Self);
313 SourceTextControl.Invalidate;
314end;
315
316procedure TskaHunSpellChecker.Close;
317begin
318 if not Active then Exit;
319 uHunSpellLib.hunspell_uninitialize(FpointerHunLib);
320 FpointerHunLib := nil;
321 FStatus := ssInActive;
322 InformStatusChange;
323end;
324
325
326procedure TskaHunSpellChecker.CorrectWithMyWord;
327var
328 NewWord: String;
329 GotIt: Boolean;
330begin
331 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
332 or (not assigned(SuggestionList)) then
333 exit;
334
335 if SuggestionList.Count > 0 then
336 NewWord := SuggestionList.Items[0]
337 else
338 NewWord := CurrentWord;
339
340 GotIt := False;
341 while not GotIt do
342 begin
343 if not InputQuery(CaptionForNewWord, PromptForNewWord, NewWord) then
344 exit;
345
346 GotIt := (not IsMisspelled(NewWord))
347 or (MessageDlg(Format(MisSpeltReplacement,[NewWord]),
348 mtWarning, [mbYes, mbNo],0, mbNo) =6) ;
349 end;
350
351 if IsMisspelled(NewWord) then
352 AddCustomWord(NewWord, True);
353
354 ReplaceCurrentWordWith(NewWord);
355end;
356
357constructor TskaHunSpellChecker.Create(AOwner: TComponent);
358begin
359 inherited;
360 ColorForMisspelled := clRed;
361
362 CurrentMe := Self;
363 FIgnore := TStringList.Create;
364 FCustDict := TStringList.Create;
365
366 if (trim(CustDictionaryFile)<>'') and (FileExists(CustDictionaryFile)) then
367 try
368 FCustDict.LoadFromFile(CustDictionaryFile);
369 except
370 end;
371
372 FUndoList := TStringList.Create;
373
374 FOptionsKey := '\software\'
375 + ChangeFileExt(ExtractFileName(Application.ExeName),'')
376 + '\skaHunSpellCheckOptions';
377 if csDesigning in componentState then
378 begin
379 IgnoreAllCaps := True;
380 IgnoreWordWithDigits := True;
381 end
382 else
383 GetOptions;
384
385 FStatus := ssInActive;
386 WaitForUser := False;
387 WordPos := 0;
388end;
389
390constructor TskaHunSpellChecker.Create(AOwner: TComponent;
391 SourceTextRichEdit: TRichedit; SuggestList: TListbox);
392begin
393 create(AOwner);
394 SourceTextControl := SourceTextRichEdit;
395 SuggestionList := SuggestList;
396end;
397
398function TskaHunSpellChecker.CurrentWordDetail(WithPosition: Boolean): String;
399begin
400 Result := '$$' + CurrentWord + '$$';
401 if WithPosition then
402 Result :='$$' + IntToStr(CurrentLine) + '$$' + IntToStr(FoundAt+1) + Result;
403end;
404
405destructor TskaHunSpellChecker.Destroy;
406begin
407 Close;
408 FIgnore.clear;
409 FreeAndNil(FIgnore);
410 FreeAndNil(FUndoList);
411 if not (csDesigning in ComponentState) then
412 try
413 if FCustDict.Count > 0 then
414 try
415 FCustDict.SaveToFile(CustDictionaryFile);
416 except
417 end;
418 SetOptions;
419 finally
420 FCustDict.Free;
421 end;
422 inherited;
423end;
424
425function TskaHunSpellChecker.GetAboutThis: String;
426begin
427 Result := AboutThis;
428end;
429
430function TskaHunSpellChecker.GetActive: Boolean;
431begin
432 Result := (FpointerHunLib <> nil);
433end;
434
435procedure TskaHunSpellChecker.GetOptions;
436var
437reg:TRegistry;
438begin
439 reg:=TRegistry.Create;
440 try
441 reg.RootKey := HKEY_CURRENT_USER;
442
443 //first get the dicationary file name
444 Reg.OpenKey(FOptionsKey,True);
445 if reg.ValueExists('DicFileName') then
446 DictionaryFileName:=Reg.readString('DicFileName')
447 else
448 Reg.WriteString('DicFileName',DictionaryFileName);
449
450 //IgnoreAllCaps ?
451 if reg.ValueExists('IgnoreAllCaps') then
452 IgnoreAllCaps:=Reg.readBool('IgnoreAllCaps')
453 else
454 Reg.WriteBool('IgnoreAllCaps',IgnoreAllCaps);
455
456
457 //IgnoreWordsWithDigits ?
458 if reg.ValueExists('IgnoreWordWithDigits') then
459 IgnoreWordWithDigits:=Reg.readBool('IgnoreWordWithDigits')
460 else
461 Reg.WriteBool('IgnoreWordWithDigits',IgnoreWordWithDigits);
462
463 finally
464 Reg.Free;
465 end;
466
467end;
468
469function TskaHunSpellChecker.GetStatus: TSpellState;
470begin
471 Result := FStatus;
472end;
473
474procedure TskaHunSpellChecker.GetSuggestions(const aMisSpeltWord: string;
475 const SuggestionList: TStrings);
476var
477 i: Integer;
478 pMisSpelt: PAnsiChar;
479 suggestions: PPAnsiChar;
480 Results: PPAnsiChar;
481 Count: Integer;
482begin
483 if (not Active) or (not Assigned(SuggestionList)) then
484 exit;
485
486 pMisSpelt := PAnsiChar(AnsiString(aMisSpeltWord));
487
488 if not uHunSpellLib.hunspell_spell(FpointerHunLib, pMisSpelt) then
489 uHunSpellLib.hunspell_suggest_auto(FpointerHunLib, pMisSpelt, suggestions);
490 begin
491 Count :=uHunSpellLib.hunspell_suggest(FpointerHunLib,pMisSpelt,suggestions);
492 Results := suggestions;
493 for i := 1 to Count do
494 begin
495 SuggestionList.Add(Results^);
496 Inc(Integer(Results), SizeOf(Pointer));
497 end;
498 uHunSpellLib.hunspell_suggest_free(FpointerHunLib, suggestions, Count);
499 end;
500end;
501
502function TskaHunSpellChecker.ShowMisSpelledWord: boolean;
503var
504 I , l :integer;
505 CharPosion:integer;
506 FirstVisibleLine, LastVisibleLine:integer;
507
508 hndl: hwnd;
509 dcForHndl: THandle;
510 visrect:Trect;
511 vispoint:TPoint;
512 procedure ShowMisSpelletWord;
513 begin
514 if Assigned(FHighlightEdit) then
515 begin
516 FHighlightEdit.Font.Color := ColorForMisspelled;
517 FHighlightEdit.Text := CurrentWord;
518 FHighlightEdit.Show;
519 end ;
520
521 if ((PosOfFirstCharInCurrentLine + FoundAt) < 1) then
522 exit;
523
524 SendMessage (SourceTextControl.Handle, EM_POSFROMCHAR, integer(@VisPoint),
525 PosOfFirstCharInCurrentLine + FoundAt-1);
526 SetTextColor(dcForHndl, ColorForMisspelled);
527 TextOut(dcForHndl, VisPoint.x, VisPoint.y, pchar(CurrentWord), WordLength);
528 end;
529
530 function WordIsIgnorable: Boolean;
531 var
532 i: Integer;
533 begin
534 Result := False;
535 if IgnoreAllCaps then
536 begin
537 Result := True;
538 for i := 1 to WordLength do
539 begin
540 Result := Result and (ord(CurrentWord[i]) in [65..90]);
541 end;
542 if Result then
543 exit;
544 end;
545
546
547
548 if IgnoreWordWithDigits then
549 begin
550 Result := False;
551 for i := 1 to WordLength do
552 begin
553 Result := Result or (ord(CurrentWord[i]) in [48..57]);
554 if Result then
555 break;
556 end;
557 end;
558 end;
559begin
560 Result := False;
561 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
562 or (not assigned(SuggestionList)) then
563 exit;
564
565 hndl:=SourceTextControl.Handle;
566
567 result:= SendMessage (hndl, EM_GETRECT, 0, integer(@visrect))=0;
568
569 dcForHndl := getdc(hndl);
570
571 if result then
572 begin
573 // VisPoint := visrect.BottomRight;
574 vispoint.Y := visrect.Bottom;
575 vispoint.X := visrect.Right;
576 CharPosion := SendMessage (hndl, EM_CHARFROMPOS, 0, integer(@VisPoint));
577 LASTVISIBLELINE := SendMessage (hndl, EM_LINEFROMCHAR, CharPosion, 0);
578 FIRSTVISIBLELINE := SendMessage (hndl, EM_GETFIRSTVISIBLELINE, 0, 0);
579
580 SetBkMode (dcForHndl, TRANSPARENT);
581 SelectObject(dcForHndl, SourceTextControl.font.Handle);
582 i := 0;
583
584 if WaitForUser then
585 begin
586 ShowMisSpelletWord;
587 exit;
588 end;
589
590 For l := 0 to SourceTextControl.Lines.Count -1 do
591 begin
592 {$R-}
593 CurrentLine := l;
594 if trim(SourceTextControl.Lines[CurrentLine]) = '' then
595 continue;
596
597 CurrentText := ' ' + SourceTextControl.Lines[CurrentLine];
598 PosOfFirstCharInCurrentLine := SendMessage (SourceTextControl.Handle,
599 EM_LINEINDEX, CurrentLine, 0);
600 i := 0;
601
602 While i <= LENgth(CurrentText) do
603 begin
604 FoundAt := i -1;
605 if Assigned(FHighlightEdit) then
606 FHighlightEdit.Hide;
607
608
609 //SuggestionList.Clear;
610 {Any character except these will count as a word delimiter}
611 While CurrentText[i] in ['A'..'Z','a'..'z','0'..'9'] do inc(i);
612
613 WordLength := i- FoundAt -1;
614 WordPos := i-WordLength;
615 CurrentWord := copy(CurrentText, WordPos, WordLength);
616 // if WordIsCorrect then
617 if (((FIgnore.IndexOf(CurrentWordDetail(True))< 0) //SingelIgnore
618 and (FIgnore.IndexOf(CurrentWordDetail(False))< 0) //IgnoreAll
619 and (IsMisspelled(CurrentWord))))
620 and (not WordIsIgnorable) then
621
622 begin
623 GetSuggestions(CurrentWord, SuggestionList.Items);
624 if SuggestionList.Count > 0 then
625 SuggestionList.ItemIndex := 0;
626 ShowMisSpelletWord;
627 if CurrentLine > LastVisibleLine then
628 SendMessage(SourceTextControl.Handle, EM_LINESCROLL, 0,
629 (CurrentLine - lastvisibleLine)+5);
630 WaitForUser := True;
631 exit;
632 End
633 else
634 SuggestionList.Clear;
635 inc(i);
636 end;
637 end;
638 if (CurrentLine >= SourceTextControl.Lines.Count-1)
639 and (i >= length(CurrentText) +1) then
640 begin
641 if (not FModified)
642 or (MessageDlg(ConfirmComplete,mtConfirmation,[mbOK, mbCancel],0)=mrOk)
643 then
644 begin
645 FStatus := ssCompleted;
646 InformStatusChange;
647 end
648 else
649 Undo;
650 end;
651 {$R+}
652 end;
653 ReleaseDC(SourceTextControl.Handle, dcForHndl);
654
655End;
656
657function TskaHunSpellChecker.Undo: Boolean;
658var
659 tmpStr: String;
660 tmpCount: Integer;
661 SrcText: String;
662begin
663 if FUndoList.Count > 1 then
664 try
665 tmpStr := FUndoList.Strings[FUndoList.Count-1];
666 if (AnsiPos('$$',tmpStr)=1) and (copy(tmpStr,length(tmpStr)-1,2) = '$$')then
667 //if last action was ignoring word then just remove it from ignore list
668 begin
669 tmpCount := strtoInt(StringReplace(tmpStr,'$$','',[rfReplaceAll]));
670 while FIgnore.Count > tmpCount do
671 FIgnore.Delete(FIgnore.Count -1);
672 end
673 else
674 SourceTextControl.Text := tmpStr;
675
676 Result := True;
677 FUndoList.Delete(FUndoList.Count-1);
678 ReStart;
679 except
680 Result := False;
681 end;
682end;
683
684procedure TskaHunSpellChecker.IgnoreAll;
685begin
686 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
687 or (not assigned(SuggestionList)) then
688 exit;
689
690 SaveForUndo(True);
691 FIgnore.Add(CurrentWordDetail(False)) ;
692 WaitForUser := False;
693 SourceTextControl.Invalidate;
694end;
695
696procedure TskaHunSpellChecker.IgnoreOnce;
697begin
698 if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
699 or (not assigned(SuggestionList)) then
700 exit;
701
702 if trim(CurrentWord) <> '' then
703 begin
704 SaveForUndo(True);
705 FIgnore.Add(CurrentWordDetail(True)) ;
706 end;
707 WaitForUser := False;
708 SourceTextControl.Invalidate;
709end;
710
711procedure TskaHunSpellChecker.InformStatusChange;
712begin
713 if Assigned(OnStateChange) then
714 OnStateChange(Self, FStatus);
715end;
716
717procedure TskaHunSpellChecker.Initialize;
718begin
719 CurrentWord := '';
720 WordLength := 0;
721 FoundAt := -1;
722 CurrentLine := 0;
723 WordPos := 0;
724 SuggestionList.Clear;
725end;
726
727function TskaHunSpellChecker.IsMisspelled(const AWord: string): Boolean;
728begin
729 if (not Active) then
730 Result := True
731 else
732 Result := not uHunSpellLib.hunspell_spell(FpointerHunLib,
733 PAnsiChar(AnsiString(AWord)));
734end;
735
736procedure TskaHunSpellChecker.Loaded;
737begin
738 inherited;
739 SetActive(FActiveOrLoaded);
740end;
741
742procedure TskaHunSpellChecker.ManualChangeDone;
743begin
744 if trim(FTxtBeforeManualEdit) = '' then
745 exit;
746 FUndoList.Add(FTxtBeforeManualEdit);
747 ReStart;
748end;
749
750procedure TskaHunSpellChecker.ManualChangeStart;
751begin
752 FTxtBeforeManualEdit := FSourceEdit.Text;
753end;
754
755function TskaHunSpellChecker.Open: Boolean;
756var
757 CurrentLine: integer;
758 function GetSpecialFolderPath(folder : integer) : string;
759 var
760 path: array [0..MAX_PATH] of char;
761 begin
762 if SUCCEEDED(SHGetFolderPath(0,folder,0,0,@path[0])) then
763 Result := path
764 else
765 Result := '';
766 end;
767begin
768 Result := True;
769 if Active then Exit;
770 Result := False;
771 FpointerHunLib := Nil;
772 if not uHunSpellLib.LoadLibHunspell('') then
773 begin
774 MessageDlg(DLLNotLoaded, mtError, [mbOK],0);
775 Exit;
776 end;
777 FpointerHunLib := uHunSpellLib.hunspell_initialize(
778 PAnsiChar(AnsiString(FAffixFileName)),
779 PAnsiChar(AnsiString(FDictFileName)));
780 Result := Assigned(FpointerHunLib);
781
782 if Result then
783 begin
784 FStatus := ssReady;
785 InformStatusChange;
786 end;
787 if trim(CustDictionaryFile) = '' then
788 CustDictionaryFile := IncludeTrailingPathDelimiter(
789 GetSpecialFolderPath(CSIDL_PERSONAL)) + 'CustomDictionary.txt';
790 if (Result) and (assigned(FCustDict)) then
791 for CurrentLine := 0 to FCustDict.Count - 1 do
792 AddCustomWord(FCustDict[CurrentLine], True);
793end;
794
795procedure TskaHunSpellChecker.SaveForUndo(const Ignoring: Boolean = False);
796begin
797 if Ignoring then
798 FUndoList.Add('$$'+ IntToStr(FIgnore.Count)+'$$')
799 else
800 FUndoList.Add(SourceTextControl.Text);
801end;
802
803procedure TskaHunSpellChecker.SetActive(const Value: Boolean);
804begin
805 if (csDesigning in ComponentState) or (csLoading in ComponentState) then
806 FActiveOrLoaded := Value
807 else
808 if Value then
809 FActiveOrLoaded := Open
810 else
811 Close;
812end;
813
814procedure TskaHunSpellChecker.SetAffixFileName(const Value: string);
815begin
816 Close;
817 FAffixFileName := Value;
818 if (trim(DictionaryFileName) = '') and (trim(value)<>'') then
819 DictionaryFileName := ChangeFileExt(value, '.dic');
820end;
821
822procedure TskaHunSpellChecker.SetCustomDict(const Value: String);
823begin
824 FCustom := Value;
825 if (not (csDesigning in componentState))
826 and (FileExists(Value)) and assigned(FCustDict) then
827 begin
828 FCustDict.Clear;
829 FCustDict.LoadFromFile(Value);
830 end;
831end;
832
833procedure TskaHunSpellChecker.SetDictFileName(const Value: string);
834begin
835 Close;
836 FDictFileName := Value;
837 if (trim(AffixFileName) = '') and (trim(value)<>'') then
838 AffixFileName := ChangeFileExt(value, '.aff');
839end;
840
841procedure TskaHunSpellChecker.SetHighLightEdit(const Value: TEdit);
842begin
843 if FHighlightEdit = Value then
844 exit;
845
846 FHighlightEdit := Value;
847
848 if Active then
849 FHighlightEdit.Text := CurrentWord;
850
851
852end;
853
854procedure TskaHunSpellChecker.SetIgnoreCaps(const Value: boolean);
855begin
856 if (FIgnoreCaps = Value) then
857 exit;
858
859 FIgnoreCaps := Value;
860 if SpellCheckState = ssChecking then
861 ReStart;
862end;
863
864procedure TskaHunSpellChecker.SetIgnoreWordWdigits(const Value: boolean);
865begin
866 if (FIgnoreWordWdigits = Value) then
867 exit;
868
869 FIgnoreWordWdigits := Value;
870 if SpellCheckState = ssChecking then
871 ReStart;
872
873end;
874
875procedure TskaHunSpellChecker.SetOptions;
876var
877reg:TRegistry;
878begin
879 reg:=TRegistry.Create;
880 try
881 reg.RootKey := HKEY_CURRENT_USER;
882
883
884 Reg.OpenKey(FOptionsKey,True);
885
886 //first save the dicationary file name
887 Reg.WriteString('DicFileName',DictionaryFileName);
888
889 //IgnoreAllCaps ?
890 Reg.WriteBool('IgnoreAllCaps',IgnoreAllCaps);
891
892
893 //IgnoreWordsWithDigits ?
894 Reg.WriteBool('IgnoreWordWithDigits',IgnoreWordWithDigits);
895
896 finally
897 Reg.Free;
898 end;
899end;
900
901Function RichEditWndProc(handle:HWnd;uMsg,wParam,lParam:longint):longint stdcall;
902begin
903 Result := CallWindowProc(OldRichEditWndProc, handle, uMsg, wParam, lParam);
904 if (uMsg=WM_PAINT) and assigned(CurrentMe) then
905 CurrentMe.ShowMisSpelledWord;
906End;
907
908procedure TskaHunSpellChecker.SetSourceEdit(const Value: TRichEdit);
909begin
910 if FSourceEdit = Value then
911 exit;
912
913 FSourceEdit := Value;
914
915 if csDesigning in ComponentState then
916 exit;
917
918 PREditorWndProc:=@RichEditWndProc;
919 //raise the limit of text which could be inserted into this Richedit
920 Value.perform(EM_EXLIMITTEXT, 0, 65535*32);
921 OldRichEditWndProc := pointer(SetWindowLong(Value.handle, GWL_WNDPROC,
922 longint(@RichEditWndProc)));
923end;
924
925end.
Note: See TracBrowser for help on using the repository browser.