Ignore:
Timestamp:
May 17, 2015, 7:29:55 AM (10 years ago)
Author:
healthsevak
Message:

Modified the library to make it more generic from Delphi community point of view before sharing with original author/custodian of HunSpell library at sourceforge

File:
1 edited

Legend:

Unmodified
Added
Removed
  • cprs/branches/HealthSevak-CPRS/CPRS-Lib/Hans SpellCheck/skaSpellCheck.pas

    r1712 r1715  
    1717 * License.
    1818 *
     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 *
    1924 * Alternatively, the content of this file maybe used under the terms of either
    2025 * the GNU General Public License Version 2 or later (the "GPL"), or the GNU
     
    3439
    3540uses
    36   Windows, Classes, SysUtils, ComCtrls, StdCtrls, Graphics;
     41  Windows, Classes, SysUtils, ComCtrls, StdCtrls, Graphics, Forms, Controls;
    3742
    3843  const
    39     AboutThis = 'A wrapper component developed by Sunil K Arora (digitiger@gmail.com) of HealthSevak using OpenSource HanSpell engine';
     44    AboutThis = 'A wrapper component developed by Sunil K Arora '
     45      + '(digitiger@gmail.com) of HealthSevak using OpenSource HanSpell engine';
    4046type
    41   TSpellState = (ssNotStarted, ssChecking, ssCancelled, ssCompleted);
     47  TSpellState = (ssNoengine, ssInActive, ssReady, ssChecking, ssCancelled,
     48                                                                  ssCompleted);
     49  TStateChangeEvent = procedure (const Sender : TObject;
     50                                          const State : TSpellState) of object;
    4251
    4352  TskaHunSpellChecker = class(TComponent)
     
    6170    PREditorWndProc:pointer;
    6271    FHighlightColor: TColor;
    63     FShowCompletion: Boolean;
    64     FpointerSpellComplete: String;
    65     FStatus: TSpellState;
    6672    FUndoList: TStringList;
    6773    FCustDict: TStringList;
     
    6975    FModified: Boolean;
    7076    FHighlightEdit: TEdit;
    71     FbtnClose: TButton;
    7277    FTxtBeforeManualEdit: String;
     78
     79    FStatus: TSpellState;
     80    FOnStart: TNotifyEvent;
     81    FOnAbort  : TNotifyEvent;
     82    FOnStateChange  : TStateChangeEvent;
     83
    7384    function AddCustomWord(aWord: String; isInternal: Boolean = False): Boolean;
    7485                                                            overload; virtual;
     
    7990    procedure SetActive(const Value: Boolean);
    8091    procedure SetAffixFileName(const Value: string);
    81     procedure SetbtnClose(const Value: TButton);
    8292    procedure SetCustomDict(const Value: String);
    8393    procedure SetDictFileName(const Value: string);
     
    8999    function GetAboutThis: String;
    90100    procedure SaveForUndo(const Ignoring: Boolean=False);
    91     procedure ShowBtnClose(const ShowIt: Boolean = True);
     101    procedure InformStatusChange;
    92102  public
    93103    constructor Create(AOwner: TComponent); overload; override;
     
    111121    procedure ManualChangeDone;
    112122    function Open:Boolean; virtual;
     123    procedure ReOpen;
    113124    function ReStart: Boolean; virtual;
    114 
    115125    function Undo: Boolean;
    116 
    117 
    118     property SpellCheckState: TSpellState read GetStatus default ssNotStarted;
     126    property SpellCheckState: TSpellState read GetStatus default ssInActive;
    119127  published
    120128    property About: String read GetAboutThis;
    121129    property Active: Boolean read GetActive write SetActive;
    122130    property AffixFileName: string read FAffixFileName write SetAffixFileName;
    123     property btnClose: TButton read FbtnClose write SetbtnClose;
    124131    property CustDictionaryFile: String read FCustom write SetCustomDict;
    125132    property DictionaryFileName:string read FDictFileName write SetDictFileName;
    126     property ColorForMisspelled: TColor read FHighlightColor write FHighlightColor default clRed;
     133    property ColorForMisspelled: TColor read FHighlightColor
     134                                            write FHighlightColor default clRed;
    127135    property MisSpeltWord: TEdit read FHighlightEdit write SetHighLightEdit;
    128136    property IsModified: Boolean read FModified;
    129     property ShowCompletionMessage: Boolean read FShowCompletion write FShowCompletion default True;
     137    property OnStart : TNotifyEvent read FOnStart write FOnStart;
     138    property OnStateChange : TStateChangeEvent read FOnStateChange
     139                                                          write FOnStateChange;
     140    property OnAbort : TNotifyEvent read FOnAbort write FOnAbort;
    130141    property SourceTextControl: TRichEdit read FSourceEdit write SetSourceEdit;
    131     property SpellCheckCompletionMessage: String read FpointerSpellComplete write FpointerSpellComplete;
    132     property SuggestionList: TListbox read FSuggestionList write FSuggestionList;
     142    property SuggestionList:TListbox read FSuggestionList write FSuggestionList;
    133143
    134144  end;
     
    140150    CaptionForNewWord = 'New Word Suggestion';
    141151    ConfirmAbort = 'Really abort?';
     152    ConfirmComplete   = 'If you accept last change than SpellCheck is complete.'
     153                        + #13 + '  To review last change click on "Cancel".';
    142154    PromptForNewWord = 'Specify the replacement for current mis-spelt word:';
    143155    DLLNotLoaded = 'Failed to load SpellCheck Engine DLL.';
     
    145157                          +' Would you want to still use it?   Click NO button '
    146158                          +'to specify better replacement word.';
     159
    147160  var
    148161    OldRichEditWndProc: {integer}pointer;
    149162    CurrentMe: TskaHunSpellChecker;
    150163implementation
    151    uses messages, Dialogs, RichEdit, SHFolder, Forms, uHunSpellLib;
     164   uses messages, Dialogs, RichEdit, SHFolder, uHunSpellLib;
    152165
    153166procedure Register;
     
    171184                                                  [mbYes, mbNo],0, mbNo) = 6);
    172185
    173   if Result then
    174     FStatus := ssCancelled;
    175 
    176   ShowBtnClose;
    177 end;
    178 
    179 function TskaHunSpellChecker.AddCustomWord(aWord: String; isInternal: Boolean = False): Boolean;
     186  if Result and (FUndoList.Count > 0) then
     187  begin
     188       SourceTextControl.Text := FUndoList[0];
     189       FUndoList.Clear;
     190       FUndoList.Add(SourceTextControl.Text);
     191  end;
     192   FIgnore.Clear;
     193   FStatus := ssCancelled;
     194   SourceTextControl.Invalidate;
     195   if Assigned(OnAbort) then
     196     OnAbort(Self);
     197end;
     198
     199function TskaHunSpellChecker.AddCustomWord(aWord: String;
     200                                        isInternal: Boolean = False): Boolean;
    180201begin
    181202  Result := False;
    182   if (trim(aWord) = '') or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then
    183      
    184 
    185   Result := False;
    186   if (not Active) then Exit;
     203  if (not active) or (trim(aWord) = '') or (SpellCheckState <> ssChecking)
     204    or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then
     205  begin
     206    Result := False;
     207    exit;
     208  end;
    187209  uHunSpellLib.hunspell_put_word(FpointerHunLib, PAnsiChar(AnsiString(aWord)));
    188210  Result := True;
     
    191213procedure TskaHunSpellChecker.ChangeAll;
    192214begin
    193   if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then
     215  if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
     216                                         or (not assigned(SuggestionList)) then
    194217     exit;
    195218  SaveForUndo;
     
    214237end;
    215238
     239procedure TskaHunSpellChecker.ReOpen;
     240begin
     241  Close;
     242  Open;
     243end;
     244
    216245procedure TskaHunSpellChecker.ReplaceCurrentWordWith(const aNewWord: String);
    217246var
     
    221250begin
    222251  full := SourceTextControl.Lines[CurrentLine];
    223   prefix := copy(CurrentText, 2, WordPos-2);    //remember there is one extra space at the start of the line prefixed while populating this variable
     252{remember there is one extra space at the start of the line prefixed while
     253 populating this variable}
     254  prefix := copy(CurrentText, 2, WordPos-2);
    224255  Suffix :=  copy(CurrentText, WordPos+WordLength,
    225256                           length(CurrentText));
    226257  SaveForUndo;
     258  FModified := True;
    227259  SourceTextControl.Lines[CurrentLine] :=prefix + aNewWord + suffix;
    228260  WaitForUser := False;
     
    238270  Initialize;
    239271  WaitForUser := False;
    240   FStatus := ssChecking;
     272  if FStatus <> ssChecking then
     273  begin
     274    FStatus := ssChecking;
     275    InformStatusChange;
     276  end;
    241277  SourceTextControl.Invalidate;
    242278  Result := not WaitForUser;
     
    246282
    247283begin
    248   if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then
     284  if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
     285                                          or (not assigned(SuggestionList)) then
    249286     exit;
    250287  ReplaceCurrentWordWith(SuggestionList.Items[SuggestionList.ItemIndex]);
     
    253290procedure TskaHunSpellChecker.CheckSpelling;
    254291begin
    255  if (SpellCheckState = ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then
     292 if (SpellCheckState = ssChecking) or (not assigned(SourceTextControl))
     293    or (trim(SourceTextControl.Text)= '') or (not assigned(SuggestionList)) then
    256294     exit;
    257295
    258296 Initialize;
    259297 FUndoList.Clear;
     298  FUndoList.Add(SourceTextControl.Text);
     299 FIgnore.Clear;
    260300 WaitForUser := False;
    261301 FStatus := ssChecking;
     302 if Assigned(OnStart) then
     303   OnStart(Self);
    262304 SourceTextControl.Invalidate;
    263  //SourceTextControl.Invalidate;
    264  ShowBtnClose(False);
    265305end;
    266306
     
    268308begin
    269309  if not Active then Exit;
    270   uHunSpellLib.hunspell_uninitialize(FpointerHunLib);
     310    uHunSpellLib.hunspell_uninitialize(FpointerHunLib);
    271311  FpointerHunLib := nil;
     312  FStatus := ssInActive;
     313  InformStatusChange;
    272314end;
    273315
     
    278320  GotIt: Boolean;
    279321begin
    280   if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then
     322  if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
     323                                        or (not assigned(SuggestionList)) then
    281324     exit;
    282325
     
    304347
    305348constructor TskaHunSpellChecker.Create(AOwner: TComponent);
    306     function GetSpecialFolderPath(folder : integer) : string;
    307       var
    308         path: array [0..MAX_PATH] of char;
    309     begin
    310       if SUCCEEDED(SHGetFolderPath(0,folder,0,0,@path[0])) then
    311         Result := path
    312       else
    313         Result := '';
    314     end;
    315349begin
    316350  inherited;
    317 
    318351   ColorForMisspelled := clRed;
    319    ShowCompletionMessage := True;
    320    SpellCheckCompletionMessage := CompletionMessage;
    321 
    322 
    323352
    324353   CurrentMe := Self;
     
    326355   FCustDict := TStringList.Create;
    327356
    328    CustDictionaryFile := IncludeTrailingPathDelimiter(GetSpecialFolderPath(CSIDL_PERSONAL)) + 'CustomDictionary.txt';
    329    if FileExists(CustDictionaryFile) then
     357   if (trim(CustDictionaryFile)<>'') and (FileExists(CustDictionaryFile)) then
    330358    try
    331359      FCustDict.LoadFromFile(CustDictionaryFile);
     
    335363   FUndoList := TStringList.Create;
    336364
    337    FStatus := ssNotStarted;
     365   FStatus := ssInActive;
    338366   WaitForUser := False;
    339367   WordPos := 0;
     
    406434    uHunSpellLib.hunspell_suggest_auto(FpointerHunLib, pMisSpelt, suggestions);
    407435  begin
    408     Count := uHunSpellLib.hunspell_suggest(FpointerHunLib, pMisSpelt, suggestions);
     436    Count :=uHunSpellLib.hunspell_suggest(FpointerHunLib,pMisSpelt,suggestions);
    409437    Results := suggestions;
    410438    for i := 1 to Count do
     
    415443    uHunSpellLib.hunspell_suggest_free(FpointerHunLib, suggestions, Count);
    416444  end;
    417 end;
    418 
    419 procedure TskaHunSpellChecker.ShowBtnClose(const ShowIt: Boolean);
    420 begin
    421   if Assigned(btnClose) then
    422   begin
    423    btnClose.Enabled := ShowIt;
    424    btnClose.Visible := ShowIt;
    425    if ShowIt then
    426    begin
    427      btnClose.BringToFront;
    428      btnClose.SetFocus;
    429    end;
    430   end;
    431445end;
    432446
     
    453467        exit;
    454468
    455      SendMessage (SourceTextControl.Handle, EM_POSFROMCHAR, integer(@VisPoint), PosOfFirstCharInCurrentLine + FoundAt-1);
     469     SendMessage (SourceTextControl.Handle, EM_POSFROMCHAR, integer(@VisPoint),
     470                                      PosOfFirstCharInCurrentLine + FoundAt-1);
    456471     SetTextColor(dcForHndl, ColorForMisspelled);
    457      TextOut(dcForHndl,  VisPoint.x,  VisPoint.y, pchar(CurrentWord), WordLength);
     472     TextOut(dcForHndl, VisPoint.x, VisPoint.y, pchar(CurrentWord), WordLength);
    458473   end;
    459474begin
     
    474489    vispoint.Y := visrect.Bottom;
    475490    vispoint.X := visrect.Right;
    476     CharPosion       := SendMessage (hndl, EM_CHARFROMPOS, 0, integer(@VisPoint));
    477     LASTVISIBLELINE  := SendMessage (hndl, EM_LINEFROMCHAR, CharPosion, 0);
     491    CharPosion := SendMessage (hndl, EM_CHARFROMPOS, 0, integer(@VisPoint));
     492    LASTVISIBLELINE := SendMessage (hndl, EM_LINEFROMCHAR, CharPosion, 0);
    478493    FIRSTVISIBLELINE := SendMessage (hndl, EM_GETFIRSTVISIBLELINE, 0, 0);
    479494
     
    496511
    497512      CurrentText := ' ' + SourceTextControl.Lines[CurrentLine];
    498       PosOfFirstCharInCurrentLine := SendMessage (SourceTextControl.Handle, EM_LINEINDEX, CurrentLine, 0);
     513      PosOfFirstCharInCurrentLine := SendMessage (SourceTextControl.Handle,
     514                                                  EM_LINEINDEX, CurrentLine, 0);
    499515      i := 0;
    500516
     
    522538          ShowMisSpelletWord;
    523539          if CurrentLine > LastVisibleLine then
    524             SendMessage(SourceTextControl.Handle, EM_LINESCROLL, 0, (CurrentLine - lastvisibleLine)+5);
     540            SendMessage(SourceTextControl.Handle, EM_LINESCROLL, 0,
     541                                            (CurrentLine - lastvisibleLine)+5);
    525542          WaitForUser := True;
    526543          exit;
     
    531548      end;
    532549    end;
    533     if (CurrentLine >= SourceTextControl.Lines.Count-1) and (i >= length(CurrentText) +1) then
     550    if (CurrentLine >= SourceTextControl.Lines.Count-1)
     551                                          and (i >= length(CurrentText) +1) then
    534552    begin
    535       FStatus := ssCompleted;
    536       if ShowCompletionMessage then
    537         ShowMessage(CompletionMessage);
    538       ShowBtnClose;
     553      if (not FModified)
     554         or (MessageDlg(ConfirmComplete,mtConfirmation,[mbOK, mbCancel],0)=mrOk)
     555         then
     556      begin
     557        FStatus := ssCompleted;
     558        InformStatusChange;
     559      end
     560      else
     561        Undo;
    539562    end;
    540563    {$R+}
     
    548571  tmpStr: String;
    549572  tmpCount: Integer;
    550 begin
    551   if FUndoList.Count > 0 then
     573  SrcText: String;
     574begin
     575  if FUndoList.Count > 1 then
    552576  try
    553577    tmpStr := FUndoList.Strings[FUndoList.Count-1];
    554    { showmessage(inttostr(AnsiPos('$$',tmpStr)) + #13 + inttostr(length(tmpstr)) + #13 +
    555                                         copy(tmpStr,length(tmpStr)-2,2));  }
    556578    if (AnsiPos('$$',tmpStr)=1) and (copy(tmpStr,length(tmpStr)-1,2) = '$$')then
     579    //if last action was ignoring word then just remove it from ignore list
    557580    begin
    558581      tmpCount := strtoInt(StringReplace(tmpStr,'$$','',[rfReplaceAll]));
     
    573596procedure TskaHunSpellChecker.IgnoreAll;
    574597begin
    575   if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then
     598  if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
     599                                        or (not assigned(SuggestionList)) then
    576600     exit;
     601
    577602  SaveForUndo(True);
    578603  FIgnore.Add(CurrentWordDetail(False)) ;
     
    583608procedure TskaHunSpellChecker.IgnoreOnce;
    584609begin
    585   if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl)) or (not assigned(SuggestionList)) then
     610  if (SpellCheckState <> ssChecking) or (not assigned(SourceTextControl))
     611                                          or (not assigned(SuggestionList)) then
    586612     exit;
     613
    587614  if trim(CurrentWord) <> '' then
    588615  begin
     
    592619  WaitForUser := False;
    593620  SourceTextControl.Invalidate;
     621end;
     622
     623procedure TskaHunSpellChecker.InformStatusChange;
     624begin
     625  if Assigned(OnStateChange) then
     626    OnStateChange(Self, FStatus);
    594627end;
    595628
     
    609642    Result := True
    610643  else
    611     Result := not uHunSpellLib.hunspell_spell(FpointerHunLib, PAnsiChar(AnsiString(AWord)));
     644    Result := not uHunSpellLib.hunspell_spell(FpointerHunLib,
     645                                                PAnsiChar(AnsiString(AWord)));
    612646end;
    613647
     
    634668var
    635669  CurrentLine: integer;
     670  function GetSpecialFolderPath(folder : integer) : string;
     671      var
     672        path: array [0..MAX_PATH] of char;
     673    begin
     674      if SUCCEEDED(SHGetFolderPath(0,folder,0,0,@path[0])) then
     675        Result := path
     676      else
     677        Result := '';
     678    end;
    636679begin
    637680  Result := True;
     
    644687     Exit;
    645688  end;
    646   FpointerHunLib := uHunSpellLib.hunspell_initialize(PAnsiChar(AnsiString(FAffixFileName)), PAnsiChar(AnsiString(FDictFileName)));
     689  FpointerHunLib := uHunSpellLib.hunspell_initialize(
     690                                        PAnsiChar(AnsiString(FAffixFileName)),
     691                                        PAnsiChar(AnsiString(FDictFileName)));
    647692  Result := Assigned(FpointerHunLib);
    648693
     694  if Result then
     695  begin
     696    FStatus := ssReady;
     697    InformStatusChange;
     698  end;
     699  if trim(CustDictionaryFile) = '' then
     700    CustDictionaryFile := IncludeTrailingPathDelimiter(
     701                GetSpecialFolderPath(CSIDL_PERSONAL)) + 'CustomDictionary.txt';
    649702  if (Result) and (assigned(FCustDict)) then
    650703     for CurrentLine := 0 to FCustDict.Count - 1 do
     
    675728  Close;
    676729  FAffixFileName := Value;
    677 end;
    678 
    679 procedure TskaHunSpellChecker.SetbtnClose(const Value: TButton);
    680 begin
    681   if btnClose = Value then
    682      exit;
    683   FbtnClose := Value;
    684   FbtnClose.ModalResult := 1; //mrOK
     730  if (trim(DictionaryFileName) = '') and (trim(value)<>'') then
     731    DictionaryFileName := ChangeFileExt(value, '.dic');
    685732end;
    686733
     
    688735begin
    689736  FCustom := Value;
    690   if not (csDesigning in componentState) then
    691     if Active and (FileExists(Value)) then
    692       FCustDict.LoadFromFile(Value);
     737  if (not (csDesigning in componentState))
     738                          and (FileExists(Value)) and assigned(FCustDict) then
     739  begin
     740    FCustDict.Clear;
     741    FCustDict.LoadFromFile(Value);
     742  end;
    693743end;
    694744
     
    697747  Close;
    698748  FDictFileName := Value;
     749  if (trim(AffixFileName) = '') and (trim(value)<>'') then
     750    AffixFileName := ChangeFileExt(value, '.aff');
    699751end;
    700752
     
    712764end;
    713765
    714 Function RichEditWndProc (handle:HWnd;uMsg,wParam,lParam:longint): longint stdcall;
    715 begin
    716       Result := CallWindowProc(OldRichEditWndProc, handle, uMsg, wParam, lParam);
    717       if (uMsg=WM_PAINT) and assigned(CurrentMe) then CurrentMe.ShowMisSpelledWord;
     766Function RichEditWndProc(handle:HWnd;uMsg,wParam,lParam:longint):longint stdcall;
     767begin
     768  Result := CallWindowProc(OldRichEditWndProc, handle, uMsg, wParam, lParam);
     769   if (uMsg=WM_PAINT) and assigned(CurrentMe) then
     770     CurrentMe.ShowMisSpelledWord;
    718771End;
    719772
     
    729782
    730783  PREditorWndProc:=@RichEditWndProc;
    731   Value.perform(EM_EXLIMITTEXT, 0, 65535*32); //raise the limit of text which could be inserted into this Richedit
    732   OldRichEditWndProc := pointer(SetWindowLong(Value.handle, GWL_WNDPROC, longint(@RichEditWndProc)));
    733 
     784  //raise the limit of text which could be inserted into this Richedit
     785  Value.perform(EM_EXLIMITTEXT, 0, 65535*32);
     786  OldRichEditWndProc := pointer(SetWindowLong(Value.handle, GWL_WNDPROC,
     787                                                    longint(@RichEditWndProc)));
    734788end;
    735789
Note: See TracChangeset for help on using the changeset viewer.