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

Last change on this file since 1751 was 1725, checked in by healthsevak, 10 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.