home *** CD-ROM | disk | FTP | other *** search
/ Windows 95 Secrets (4th Edition) / Windows95Secrets4thEdition.iso / tools / spellers / spelmate / spellwnd.pa$ / spellwnd.pas
Encoding:
Pascal/Delphi Source File  |  1994-02-16  |  24.0 KB  |  801 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Turbo Pascal for Windows                        }
  5. {       Standard windows unit for ObjectWindows         }
  6. {                                                       }
  7. {       Copyright (c) 1991 Borland International        }
  8. {                                                       }
  9. {=======================================================}
  10. {       Modifications to allows Spell Checker           }
  11. {       SpelMate DLL to be accesses.                    }
  12. {       Copyright (c) 1993 Aciran Software systems      }
  13. {*******************************************************}
  14.  
  15. unit SpellWnds;
  16.  
  17. {$R SpellWNDS.RES}
  18.  
  19. interface
  20.  
  21. uses WinTypes, WinProcs, WinDos, Objects, OWindows, ODialogs,
  22.   OMemory, OStdDlgs, Strings,Win31,ver;
  23.  
  24. const
  25.   cm_spell   = 101;
  26.   MaxWordLen = 20;
  27.  
  28.  
  29. {create function prototypes}
  30. type
  31.   TSpelmateInit = function :integer;
  32.   TSpellCheck = function (AWord:Pchar):BOOL;
  33.   TSuggestModalWord = function(HWindow:HWnd;AWord:PChar):PChar;
  34.   TDisplayAtTop = procedure;
  35.   TSuggestWordList = function(AWord:PChar):PChar;
  36.   TCtl3dRegister = procedure(HInstance:Word);
  37.   TCtl3dUnRegister = procedure(HInstance:Word);
  38.   TCtl3dAutoSubclass = procedure(HInstance:Word);
  39.  
  40.  
  41. {create a new edit type so we can get a handle direct to its text buffer
  42.  for faster scanning}
  43. type
  44.    PNewEdit = ^TNewEdit;
  45.    TNewEdit = object(TEdit)
  46.    function GetHandle:word;
  47. end;
  48.  
  49.  
  50. type
  51.  
  52.   { TSearchRec }
  53.   TSearchRec = record
  54.     SearchText: array[0..80] of Char;
  55.     CaseSensitive: Bool;
  56.     ReplaceText: array[0..80] of Char;
  57.     ReplaceAll: Bool;
  58.     PromptOnReplace: Bool;
  59.     IsReplace: Boolean;
  60.   end;
  61.  
  62.   { TEditWindow  }
  63.   PEditWindow = ^TEditWindow;
  64.   TEditWindow = object(TWindow)
  65.     Handle3D:THandle;
  66.     Editor: PNewEdit;
  67.     SearchRec: TSearchRec;
  68.     constructor Init(AParent: PWindowsObject; ATitle: PChar);
  69.     constructor Load(var S: TStream);
  70.     procedure Store(var S: TStream);
  71.     procedure WMSize(var Msg: TMessage);
  72.       virtual wm_First + wm_Size;
  73.     procedure WMSetFocus(var Msg: TMessage);
  74.       virtual wm_First + wm_SetFocus;
  75.     procedure CMEditFind(var Msg: TMessage);
  76.       virtual cm_First + cm_EditFind;
  77.     procedure CMEditFindNext(var Msg: TMessage);
  78.       virtual cm_First + cm_EditFindNext;
  79.     procedure CMEditReplace(var Msg: TMessage);
  80.       virtual cm_First + cm_EditReplace;
  81.     procedure CMSpell(var Msg: TMessage); {Spell check option added}
  82.       virtual cm_First + cm_Spell;
  83.   private
  84.     procedure DoSearch;
  85.   end;
  86.  
  87.   { TFileWindow }
  88.   PFileWindow = ^TFileWindow;
  89.   TFileWindow = object(TEditWindow)
  90.     FileName: PChar;
  91.     IsNewFile: Boolean;
  92.     constructor Init(AParent: PWindowsObject; ATitle, AFileName: PChar);
  93.     destructor Done; virtual;
  94.     constructor Load(var S: TStream);
  95.     procedure Store(var S: TStream);
  96.     function CanClear: Boolean; virtual;
  97.     function CanClose: Boolean; virtual;
  98.     procedure NewFile;
  99.     procedure Open;
  100.     procedure Read;
  101.     procedure SetFileName(AFileName: PChar);
  102.     procedure ReplaceWith(AFileName: PChar);
  103.     function Save: Boolean;
  104.     function SaveAs: Boolean;
  105.     procedure SetupWindow; virtual;
  106.     procedure Write;
  107.     procedure CMFileNew(var Msg: TMessage);
  108.       virtual cm_First + cm_FileNew;
  109.     procedure CMFileOpen(var Msg: TMessage);
  110.       virtual cm_First + cm_FileOpen;
  111.     procedure CMFileSave(var Msg: TMessage);
  112.       virtual cm_First + cm_FileSave;
  113.     procedure CMFileSaveAs(var Msg: TMessage);
  114.       virtual cm_First + cm_FileSaveAs;
  115.   end;
  116.  
  117. const
  118.   REditWindow: TStreamRec = (
  119.     ObjType: 80;
  120.     VmtLink: Ofs(TypeOf(TEditWindow)^);
  121.     Load:    @TEditWindow.Load;
  122.     Store:   @TEditWindow.Store);
  123.  
  124. const
  125.   RFileWindow: TStreamRec = (
  126.     ObjType: 81;
  127.     VmtLink: Ofs(TypeOf(TFileWindow)^);
  128.     Load:    @TFileWindow.Load;
  129.     Store:   @TFileWindow.Store);
  130.  
  131. procedure RegisterStdWnds;
  132.  
  133. implementation
  134.  
  135. {make instances of the functions from our prototypes}
  136. var
  137. SpelmateInit : TSpelmateInit;
  138. SpellCheck : TSpellCheck;
  139. SuggestModalWord : TSuggestModalWord;
  140. DisplayAtTop : TDisplayAtTop;
  141. SuggestWordList : TSuggestWordList;
  142. Ctl3dRegister : TCtl3dRegister;
  143. Ctl3dUnRegister : TCtl3dRegister;
  144. Ctl3dAutoSubclass : TCtl3dAutoSubclass;
  145.  
  146. { TSearchDialog }
  147.  
  148. const
  149.   sd_Search          = MakeIntResource($7F10);
  150.   sd_Replace         = MakeIntResource($7F11);
  151.   sd_BCSearch        = MakeIntResource($7F12);
  152.   sd_BCReplace       = MakeIntResource($7F13);
  153.   id_SearchText      = 100;
  154.   id_CaseSensitive   = 101;
  155.   id_ReplaceText     = 102;
  156.   id_ReplaceAll      = 103;
  157.   id_PromptOnReplace = 104;
  158.  
  159. type
  160.   PSearchDialog = ^TSearchDialog;
  161.   TSearchDialog = object(TDialog)
  162.     constructor Init(AParent: PWindowsObject; Template: PChar;
  163.       var SearchRec: TSearchRec);
  164.   end;
  165.  
  166. constructor TSearchDialog.Init(AParent: PWindowsObject; Template: PChar;
  167.   var SearchRec: TSearchRec);
  168. var
  169.   C: PWindowsObject;
  170. begin
  171.   TDialog.Init(AParent, Template);
  172.   C := New(PEdit, InitResource(@Self, id_SearchText,
  173.     SizeOf(SearchRec.SearchText)));
  174.   C := New(PCheckBox, InitResource(@Self, id_CaseSensitive));
  175.   if (Template = sd_Replace) or (Template = sd_BCReplace) then
  176.   begin
  177.     C := New(PEdit, InitResource(@Self, id_ReplaceText,
  178.       SizeOf(SearchRec.ReplaceText)));
  179.     C := New(PCheckBox, InitResource(@Self, id_ReplaceAll));
  180.     C := New(PCheckBox, InitResource(@Self, id_PromptOnReplace));
  181.   end;
  182.   TransferBuffer := @SearchRec;
  183. end;
  184.  
  185. function TNewEdit.GetHandle;
  186. begin
  187.   GetHandle := SendMessage(HWindow,EM_GETHANDLE,0,LongInt(0));
  188. end;
  189.  
  190. { TEditWindow }
  191.  
  192. { Constructor for a TEditWindow.  Initializes its data fields using passed
  193.   parameters and default values.  Constructs its child edit control. }
  194. constructor TEditWindow.Init(AParent: PWindowsObject; ATitle: PChar);
  195. begin
  196.   TWindow.Init(AParent, ATitle);
  197.   {NOTE: we use our new edit type here}
  198.   Editor := New(PNewEdit, Init(@Self, 200, nil, 0, 0, 0, 0, 0, True));
  199.   with Editor^.Attr do
  200.     Style := Style or es_NoHideSel;
  201.   FillChar(SearchRec, SizeOf(SearchRec), #0);
  202.   Handle3D :=LoadLibrary('ctl3d.dll');
  203.   if Handle3d >= 32 then
  204.   begin
  205.   @Ctl3dRegister := GetProcAddress(Handle3d,'Ctl3dRegister');
  206.   @Ctl3dUnRegister := GetProcAddress(Handle3d,'Ctl3dUnRegister');
  207.   @Ctl3dAutoSubclass := GetProcAddress(Handle3d,'Ctl3dautoSubclass');
  208.   Ctl3dRegister(HInstance);
  209.   Ctl3dAutoSubclass(HInstance);
  210.   end;
  211. end;
  212.  
  213. { Load a TEditWindow from the given stream }
  214. constructor TEditWindow.Load(var S: TStream);
  215. begin
  216.   TWindow.Load(S);
  217.   GetChildPtr(S, Editor);
  218. end;
  219.  
  220. { Store a TEditWindow to the given stream }
  221. procedure TEditWindow.Store(var S: TStream);
  222. begin
  223.   TWindow.Store(S);
  224.   PutChildPtr(S, Editor);
  225. end;
  226.  
  227. { Responds to an incoming wm_Size message by resizing the child edit
  228.   control according to the size of the TEditWindow's client area. }
  229. procedure TEditWindow.WMSize(var Msg: TMessage);
  230. begin
  231.   TWindow.WMSize(Msg);
  232.   SetWindowPos(Editor^.HWindow, 0, -1, -1, Msg.LParamLo+2, Msg.LParamHi+2,
  233.     swp_NoZOrder);
  234. end;
  235.  
  236. { Responds to an incoming wm_SetFocus message by setting the focus to the
  237.   child edit control. }
  238. procedure TEditWindow.WMSetFocus(var Msg: TMessage);
  239. begin
  240.   SetFocus(Editor^.HWindow);
  241. end;
  242.  
  243. procedure TEditWindow.DoSearch;
  244. var
  245.   S: array[0..80] of Char;
  246.   P: Pointer;
  247.   Rslt: Integer;
  248. begin
  249.   Rslt := 0;
  250.   with SearchRec do
  251.     repeat
  252.       Rslt := Editor^.Search(-1, SearchText, CaseSensitive);
  253.       if Rslt = -1 then
  254.       begin
  255.         if not IsReplace or not ReplaceAll then
  256.         begin
  257.           P := @SearchText;
  258.           WVSPrintF(S, '"%0.60s" not found.', P);
  259.           MessageBox(HWindow, S, 'Find error', mb_OK + mb_IconExclamation);
  260.         end;
  261.       end
  262.       else
  263.         if IsReplace then
  264.           if not PromptOnReplace then Editor^.Insert(ReplaceText)
  265.           else
  266.           begin
  267.             Rslt := MessageBox(HWindow, 'Replace this occurrence?',
  268.               'Search/Replace', mb_YesNoCancel + mb_IconQuestion);
  269.             if Rslt = id_Yes then Editor^.Insert(ReplaceText)
  270.             else if Rslt = id_Cancel then Exit;
  271.           end;
  272.     until (Rslt = -1) or not ReplaceAll or not IsReplace;
  273. end;
  274.  
  275. procedure TEditWindow.CMEditFind(var Msg: TMessage);
  276. var
  277.   Dialog: PChar;
  278. begin
  279.   if BWCCClassNames then
  280.     Dialog := sd_BCSearch
  281.   else
  282.     Dialog := sd_Search;
  283.   if Application^.ExecDialog(New(PSearchDialog, Init(@Self,
  284.     Dialog, SearchRec))) = id_OK then
  285.   begin
  286.     SearchRec.IsReplace := False;
  287.     DoSearch;
  288.   end;
  289. end;
  290.  
  291. procedure TEditWindow.CMEditFindNext(var Msg: TMessage);
  292. begin
  293.   DoSearch;
  294. end;
  295.  
  296. procedure TEditWindow.CMEditReplace(var Msg: TMessage);
  297. var
  298.   Dialog: PChar;
  299. begin
  300.   if BWCCClassNames then
  301.     Dialog := sd_BCReplace
  302.   else
  303.     Dialog := sd_Replace;
  304.   if Application^.ExecDialog(New(PSearchDialog, Init(@Self,
  305.     Dialog, SearchRec))) = id_OK then
  306.   begin
  307.     SearchRec.IsReplace := True;
  308.     DoSearch;
  309.   end;
  310. end;
  311.  
  312. { TFileWindow }
  313.  
  314. { Constructor for a TFileWindow.  Initializes its data fields using
  315.   passed parameters and default values. }
  316. constructor TFileWindow.Init(AParent: PWindowsObject; ATitle,
  317.   AFileName: PChar);
  318. begin
  319.   TEditWindow.Init(AParent, ATitle);
  320.   IsNewFile := True;
  321.   FileName := StrNew(AFileName);
  322. end;
  323.  
  324. { Dispose of the file name }
  325. destructor TFileWindow.Done;
  326. begin
  327.   StrDispose(FileName);
  328. if Handle3d > 32 then
  329. begin
  330.   Ctl3dUnRegister(HInstance);
  331.   FreeLibrary(Handle3D);
  332. end;
  333.   TEditWindow.Done;
  334. end;
  335.  
  336. { Load a TFileWindow from the stream }
  337. constructor TFileWindow.Load(var S: TStream);
  338. begin
  339.   TEditWindow.Load(S);
  340.   FileName := S.StrRead;
  341.   IsNewFile := FileName = nil;
  342. end;
  343.  
  344. { Store a TFileWindow from the stream }
  345. procedure TFileWindow.Store(var S: TStream);
  346. begin
  347.   TEditWindow.Store(S);
  348.   S.StrWrite(FileName);
  349. end;
  350.  
  351. { Performs setup for a TFileWindow, appending 'Untitled' to its caption }
  352. procedure TFileWindow.SetupWindow;
  353. begin
  354.   TEditWindow.SetupWindow;
  355.   SetFileName(FileName);
  356.   if FileName <> nil then Read;
  357. end;
  358.  
  359. { Sets the file name of the window and updates the caption.  Assumes
  360.   that the AFileName parameter and the FileName instance variable were
  361.   allocated by StrNew. }
  362. procedure TFileWindow.SetFileName(AFileName: PChar);
  363. var
  364.   NewCaption: array[0..80] of Char;
  365.   P: array[0..1] of PChar;
  366. begin
  367.   if FileName <> AFileName then
  368.   begin
  369.     StrDispose(FileName);
  370.     FileName := StrNew(AFileName);
  371.   end;
  372.   P[0] := Attr.Title;
  373.   if FileName = nil then P[1] := '(Untitled)'
  374.   else P[1] := AFileName;
  375.   if Attr.Title = nil then SetWindowText(HWindow, P[1])
  376.   else
  377.   begin
  378.     WVSPrintF(NewCaption, '%0.40s - %0.37s', P[0]);
  379.     SetWindowText(HWindow, NewCaption);
  380.   end;
  381. end;
  382.  
  383. { Begins the edit of a new file, after determining that it is Ok to
  384.   clear the TEdit's text. }
  385. procedure TFileWindow.NewFile;
  386. begin
  387.   if CanClear then
  388.   begin
  389.     Editor^.Clear;
  390.     InvalidateRect(Editor^.HWindow, nil, False);
  391.     Editor^.ClearModify;
  392.     IsNewFile := True;
  393.     SetFileName(nil);
  394.   end;
  395. end;
  396.  
  397. { Replaces the current file with the given file. }
  398. procedure TFileWindow.ReplaceWith(AFileName: PChar);
  399. begin
  400.   SetFileName(AFileName);
  401.   Read;
  402.   InvalidateRect(Editor^.HWindow, nil, False);
  403. end;
  404.  
  405. { Brings up a dialog allowing the user to open a file into this
  406.   window.  Save as selecting File|Open from the menus. }
  407. procedure TFileWindow.Open;
  408. var
  409.   TmpName: array[0..fsPathName] of Char;
  410. begin
  411.   if CanClear and (Application^.ExecDialog(New(PFileDialog,
  412.      Init(@Self, PChar(sd_FileOpen), StrCopy(TmpName, '*.*')))) = id_Ok) then
  413.     ReplaceWith(TmpName);
  414. end;
  415.  
  416. { Reads the contents of a previously-specified file into the TEdit
  417.   child control. }
  418. procedure TFileWindow.Read;
  419. const
  420.   BufferSize = 1024;
  421. var
  422.   CharsToRead: LongInt;
  423.   BlockSize: Integer;
  424.   AStream: PDosStream;
  425.   ABuffer: PChar;
  426. begin
  427.   AStream := New(PDosStream, Init(FileName, stOpen));
  428.   ABuffer := MemAlloc(BufferSize + 1);
  429.   CharsToRead := AStream^.GetSize;
  430.   if ABuffer <> nil then
  431.   begin
  432.     Editor^.Clear;
  433.     while CharsToRead > 0 do
  434.     begin
  435.       if CharsToRead > BufferSize then
  436.         BlockSize := BufferSize
  437.       else BlockSize := CharsToRead;
  438.       AStream^.Read(ABuffer^, BlockSize);
  439.       ABuffer[BlockSize] := Char(0);
  440.       Editor^.Insert(ABuffer);
  441.       CharsToRead := CharsToRead - BlockSize;
  442.     end;
  443.     IsNewFile := False;
  444.     Editor^.ClearModify;
  445.     Editor^.SetSelection(0, 0);
  446.     FreeMem(ABuffer, BufferSize + 1);
  447.   end;
  448.   Dispose(AStream, Done);
  449. end;
  450.  
  451. { Saves the contents of the TEdit child control into the file currently
  452.   being editted.  Returns true if the file was saved. }
  453. function TFileWindow.Save: Boolean;
  454. begin
  455.   Save := True;
  456.   if Editor^.IsModified then
  457.     if IsNewFile then Save := SaveAs
  458.     else Write;
  459. end;
  460.  
  461. { Saves the contents of the TEdit child control into a file whose name
  462.   is retrieved from the user, through execution of a "Save" file
  463.   dialog.  Returns true if the file was saved. }
  464. function TFileWindow.SaveAs: Boolean;
  465. var
  466.   TmpName: array[0..fsPathName] of Char;
  467. begin
  468.   SaveAs := False;
  469.   if FileName <> nil then StrCopy(TmpName, FileName)
  470.   else TmpName[0] := #0;
  471.   if Application^.ExecDialog(New(PFileDialog,
  472.       Init(@Self, PChar(sd_FileSave), TmpName))) = id_Ok then
  473.   begin
  474.     SetFileName(TmpName);
  475.     Write;
  476.     SaveAs := True;
  477.   end;
  478. end;
  479.  
  480. { Writes the contents of the TEdit child control to a previously-specified
  481.   file.  If the operation will cause truncation of the text, first confirms
  482.   (through displaying a message box) that it is OK to proceed. }
  483. procedure TFileWindow.Write;
  484. const
  485.   BufferSize = 1024;
  486. var
  487.   CharsToWrite, CharsWritten: LongInt;
  488.   BlockSize: Integer;
  489.   AStream: PDosStream;
  490.   ABuffer: pointer;
  491.   NumLines: Integer;
  492. begin
  493.   NumLines := Editor^.GetNumLines;
  494.   CharsToWrite := Editor^.GetLineIndex(NumLines-1) +
  495.     Editor^.GetLineLength(NumLines-1);
  496.   AStream := New(PDosStream, Init(FileName, stCreate));
  497.   ABuffer := MemAlloc(BufferSize + 1);
  498.   CharsWritten := 0;
  499.   if ABuffer <> nil then
  500.   begin
  501.     while CharsWritten < CharsToWrite do
  502.     begin
  503.       if CharsToWrite - CharsWritten > BufferSize then
  504.         BlockSize := BufferSize
  505.       else BlockSize := CharsToWrite - CharsWritten;
  506.       Editor^.GetSubText(ABuffer, CharsWritten, CharsWritten + BlockSize);
  507.       AStream^.Write(ABuffer^, BlockSize);
  508.       CharsWritten := CharsWritten + BlockSize;
  509.     end;
  510.     IsNewFile := False;
  511.     Editor^.ClearModify;
  512.     FreeMem(ABuffer, BufferSize + 1);
  513.   end;
  514.   Dispose(AStream, Done);
  515. end;
  516.  
  517. { Returns a Boolean value indicating whether or not it is Ok to clear
  518.   the TEdit's text.  Returns True if the text has not been changed, or
  519.   if the user Oks the clearing of the text. }
  520. function TFileWindow.CanClear: Boolean;
  521. var
  522.   S: array[0..fsPathName+27] of Char;
  523.   P: PChar;
  524.   Rslt: Integer;
  525. begin
  526.   CanClear := True;
  527.   if Editor^.IsModified then
  528.   begin
  529.     if FileName = nil then StrCopy(S, 'Untitled file has changed. Save?')
  530.     else
  531.     begin
  532.       P := FileName;
  533.       WVSPrintF(S, 'File "%s" has changed.  Save?', P);
  534.     end;
  535.     Rslt := MessageBox(HWindow, S, 'File Changed', mb_YesNoCancel or
  536.       mb_IconQuestion);
  537.     if Rslt = id_Yes then CanClear := Save
  538.     else CanClear := Rslt <> id_Cancel;
  539.   end;
  540. end;
  541.  
  542. { Returns a Boolean value indicating whether or not it is Ok to close
  543.   the TEdit's text.  Returns the result of a call to Self.CanClear. }
  544. function TFileWindow.CanClose: Boolean;
  545. begin
  546.   CanClose := CanClear;
  547. end;
  548.  
  549. { Responds to an incoming "New" command (with a cm_FileNew command
  550.   identifier) by calling Self.New. }
  551. procedure TFileWindow.CMFileNew(var Msg: TMessage);
  552. begin
  553.   NewFile;
  554. end;
  555.  
  556. { Responds to an incoming "Open" command (with a cm_FileOpen command
  557.   identifier) by calling Self.Open. }
  558. procedure TFileWindow.CMFileOpen(var Msg: TMessage);
  559. begin
  560.   Open;
  561. end;
  562.  
  563. { Responds to an incoming "Save" command (with a cm_FileSave command
  564.   identifier) by calling Self.Save. }
  565. procedure TFileWindow.CMFileSave(var Msg: TMessage);
  566. begin
  567.   Save;
  568. end;
  569.  
  570. { Responds to an incoming "SaveAs" command (with a cm_FileSaveAs command
  571.   identifier) by calling Self.SaveAs. }
  572. procedure TFileWindow.CMFileSaveAs(var Msg: TMessage);
  573. begin
  574.   SaveAs;
  575. end;
  576.  
  577. procedure RegisterStdWnds;
  578. begin
  579.   RegisterType(REditWindow);
  580.   RegisterType(RFileWindow);
  581. end;
  582.  
  583. {Here is our EditWindows Spell method}
  584. procedure TEditWindow.CMSpell;
  585. var
  586. startpos,endpos,i,chs,lines,origin,countoffset,j,k:integer;
  587. chz:array[0..1] of char;
  588. ch:char;
  589. AWord,NewWord : array[0..30] of char;
  590. Handle,EdHandle :THandle;
  591. EdPnt : pointer;
  592. nextcount,result,FirstLine,CurrentLine,NOLinesVisible,
  593.           DistFromMenuBar,NCAHeight,TextY,ScreenHeight : integer;
  594. Dialog : PDlgWindow;
  595. Tm : TTextMetric;
  596. EditDC: HDC;
  597. W,VersionLen :Word;
  598. Points :TPoint;
  599. VerBuf: integer;
  600. VerHandle:LongInt;
  601. Data : PChar;
  602. OurList : PChar;
  603. VersionVal:pointer;
  604. VersionFound:array[0..80] of char;
  605. { Given a text buffer, read it and return the next word }
  606. { We can use out Edit contol like a very long PChar string by
  607.  getting a pointer to the text it contains, then parsing this long string
  608.  and extacting the words one by one}
  609.  
  610. function GetWord(S,F:Pchar): PChar;
  611. var
  612.   C : Char;
  613.   I: Integer;
  614. begin
  615.   I := 0;
  616.   C := #0;
  617.   { find first letter }
  618.   while not (nextcount > Strlen(F)) and not (UpCase(C) in ['A'..'Z','-','''']) do
  619.   begin
  620.     C:= F[nextcount];
  621.     inc(nextcount);
  622.   end;
  623.   { special test in case end of file }
  624.   if (nextcount = Strlen(F)) and (UpCase(C) in ['A'..'Z','-','''']) then
  625.   begin
  626.     if (I < MaxWordLen) then
  627.      begin
  628.       S[I] := C;
  629.       inc(I);
  630.      end;
  631.   end
  632.   else
  633.     { read chars from file, append to S }
  634.     while (UpCase(C) in ['A'..'Z','''','-']) and not (nextcount > Strlen(F)) do
  635.     begin
  636.       if I < MaxWordLen then
  637.       begin
  638.         S[I] := C;
  639.         Inc(I);
  640.       end;
  641.       C:= F[nextcount];
  642.       inc(nextcount);
  643.     end;
  644.   S[I] := #0;
  645.   GetWord := S;
  646. end;
  647.  
  648. begin
  649.   {Check DLL version is OK}
  650.   VerBuf := GetFileVersionInfoSize('Spelmate.dll',VerHandle);
  651.   GetMem(Data,VerBuf+1);
  652.   if (VerBuf = 0) or (GetFileVersionInfo('Spelmate.dll',VerHandle,VerBuf,Data) = false) then
  653.   begin
  654.    MessageBeep(0);
  655.    MessageBox(HWindow,'Spelling Dll Version Information not Available','Application Error',MB_OK or
  656.    MB_ICONSTOP);
  657.    Exit;
  658.   end;
  659.   if VerQueryValue(Data,'StringFileInfo\040904E4\FileVersion',VersionVal,VersionLen) = false then
  660.   begin
  661.    MessageBeep(0);
  662.    MessageBox(HWindow,'Spelling Dll Version not Compatible','Application Error',MB_OK or
  663.    MB_ICONSTOP);
  664.    Exit;
  665.   end;
  666.   if StrComp(VersionVal,'1.3') < 0 then
  667.   begin
  668.    MessageBeep(0);
  669.    StrECopy(StrECopy(VersionFound,'Current Version found is only '),VersionVal);
  670.    MessageBox(HWindow,VersionFound,'Spelmate Version Must be V1.3 or Greater',MB_OK or
  671.    MB_ICONSTOP);
  672.    Exit;
  673.   end;
  674.   {Load Spelling DLL}
  675.   LoadCursor(0,IDC_WAIT);
  676.   Handle := LoadLibrary('Spelmate.Dll');
  677.   LoadCursor(0,IDC_ARROW);
  678.   if Handle < 32 then  {if failed to load/find DLL inform user}
  679.   begin
  680.    MessageBeep(0);
  681.    MessageBox(HWindow,'Unable to Load Spelling Dll','Application Error',MB_OK or
  682.    MB_ICONSTOP);
  683.    Exit;
  684.   end;
  685.   {Set our function addresses up to those in the DLL, should check really
  686.    that they are not nil, as this indicates an error}
  687.   @SpelmateInit := GetProcAddress(Handle,'SpelmateInit');
  688.   @SpellCheck := GetProcAddress(Handle,'Spellcheck');
  689.   @SuggestModalWord := GetProcAddress(Handle,'SuggestModalWord');
  690.   @displayAtTop := GetProcAddress(Handle,'DisplayAtTop');
  691.   @SuggestWordList := GetProcAddress(Handle,'SuggestWordList');
  692.   {This dialog is just to keep the user happy while the main dictionary is
  693.    being loaded as this can take a few seconds}
  694.   Dialog :=New(PDlgWindow,Init(nil,'LoadSpell'));
  695.   Application^.MakeWindow(Dialog);
  696.   {Ask the DLL to init, and get back the status information}
  697.   result := SpelmateInit;
  698.   {remove the dialog now DLL loaded}
  699.   Dialog^.done;
  700.   {if result = -1 then OK}
  701.   if result <> -1 then
  702.   begin
  703.     MessageBox(HWindow,'Cannot Access Spelling Dictionary','Application Error',MB_OK or MB_ICONSTOP);
  704.     Exit;
  705.   { if result not -1 then an error occurred, and so do not spell check}
  706.   { Possible error codes are }
  707.   { 0 not enough memory }
  708.   { 1 Main dictionary not found}
  709.   { 2 Stream access error, main dictionary}
  710.   { 3 Initialisation error, main dictionary}
  711.   { 4 Read error, main dictionary}
  712.   { 5 Corrupt file or wrong file type, error, main dictionary}
  713.   { 6 Stream access error, private dictionary}
  714.   { 7 Initialisation error, private dictionary}
  715.   { 8 Read error, private dictionary}
  716.   { 9 Corrupt file or wrong file type, error, private dictionary}
  717.   end;
  718.   Editor^.GetSelection(startpos,endpos); {set pointers to current cursor position}
  719.   nextcount := startpos;
  720.   origin := startpos;{ remember where started}
  721.   Edhandle := Editor^.GetHandle;{ get a handle to local edit buffer}
  722.   EdPnt := LocalLock(EdHandle); { make a pointer to editor text buffer}
  723.   repeat
  724.   if GetWord(AWord,EdPnt)^ <> #0 then  {while not end of editor text,scan}
  725.   begin
  726.   if AWord[StrLen(AWord)-1] = '''' then
  727.   AWord[Strlen(AWord)-1] := #0;{if word was in quotes,remove end one}
  728.   if not spellcheck(AWord) then {if word not found in main,private or IgnoreAll dictionary}
  729.   begin
  730.     Editor^.SetSelection(nextcount-Strlen(AWord)-1,nextcount-1); {Highlight Word not in dictionary}
  731.  
  732.     W := LoWord(GetVersion);
  733.     if (Lo(W) = 3) and (Hi(W) >= 10) then {check Windows > 3.0 as using 3.1 call}
  734.     begin
  735.       {Find if text in upper half of screen (NOT trivial!)}
  736.       FirstLine := SendMessage(Editor^.HWindow,EM_GETFIRSTVISIBLELINE,0,0);
  737.       CurrentLine := Editor^.GetLineFromPos(-1);  {-1 gets line selected text is on}
  738.       NoLinesVisible := CurrentLine - FirstLine + 1;
  739.  
  740.       {get the height of each line}
  741.       EditDC := GetDC(Editor^.HWindow);
  742.       GetTextMetrics(EditDC,Tm);
  743.       ReleaseDC(Editor^.HWindow,EditDC);
  744.  
  745.       {Now get the distance from the text to the top of the client area}
  746.       {The following is not 100% accurate but is good enough for our purposes}
  747.       DistFromMenuBar := Tm.tmHeight * NoLinesVisible;
  748.  
  749.       {get height of menu bar + title + frame (SM_CYCAPTION does title & frame)}
  750.       NCAHeight := GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CYCAPTION);
  751.  
  752.  
  753.       {calculate relative Y location of text}
  754.       TextY := NCAHeight + DistFromMenuBar;
  755.  
  756.       {convert to absolute screen coordinates}
  757.       Points.X := 0;
  758.       Points.Y := TextY;
  759.       ClienttoScreen(Editor^.HWindow,Points);
  760.       TextY := Points.Y;
  761.  
  762.       {get height of screen}
  763.       ScreenHeight := GetSystemMetrics(SM_CYSCREEN);
  764.  
  765.       {FINALLY! if text in lower half of screen put suggest word dialog at top}
  766.       if (TextY > (ScreenHeight div 2)) then
  767.          DisplayAtTop;
  768.     end;
  769.  
  770.     OurList:=SuggestWordList(AWord);
  771.     StrCopy(NewWord,suggestModalword(Editor^.HWindow,AWord)); {get word user chose during suggestion}
  772.     if NewWord[0] = #0 then {if blank, cancel was selected}
  773.     begin
  774.     Editor^.SetSelection(origin,origin); {return to start position}
  775.     LocalUnlock(EdHandle); {release Editor Memory Block}
  776.     FreeLibrary(Handle); {release Spelmate DLL, or you may wait until your program exits, and do it in your destructor}
  777.     Exit;
  778.     end
  779.   else
  780.     begin
  781.       if StrComp(Aword,NewWord) <> 0 then {if words are not the same,change}
  782.       begin
  783.         j := Strlen(NewWord);
  784.         k := Strlen(AWord);
  785.         countoffset := j - k;
  786.         Editor^.DeleteSubText(nextcount-k-1,nextcount-1); {remove old highlighted word}
  787.         Editor^.Insert(NewWord); {replace with new one}
  788.         nextcount := nextcount + countoffset; {adjust pointer in case new word was larger or smaller}
  789.       end;
  790.     end;
  791.   end;
  792.   end;
  793.   until AWord[0] = #0;
  794. LocalUnlock(EdHandle); {release Editor Memory Block}
  795. FreeLibrary(Handle); {release Spelmate DLL, or you may wait until your program exits, and do it in your destructor}
  796. MessageBox(HWindow,'Spell Checking Complete','All Done!',MB_OK);
  797. Editor^.SetSelection(origin,origin); {return to start position}
  798. end;
  799.  
  800. end.
  801.