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

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

Cleaned little bit of glitches and added the About dialog and also synced the demo project in line with the updated component properties

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