home *** CD-ROM | disk | FTP | other *** search
- unit Memock32;
-
- interface
-
- { Revisions:
- 01/02/96 - Corrected SyncBuffer. It was not getting the last
- character in the TMemo's buffer.
- 01/07/96 - Improved handling of hyphenated words.
- 01/09/96 - Added Orpheus Editor component.
- 01/11/96 - Added Selection spell checking methods.
- 01/12/96 - Improved the look of the suggestion dialog box.
- 01/16/96 - Renamed TMemoSpellCheck to TMemoSpell.
- 03/15/96 - Converted to 32-Bit component for Delphi 2.0
- }
-
- uses
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
- StdCtrls, ComCtrls, DBCtrls, SugDlg32;
-
- const SpellCharSet : set of char =
- ['A'..'Z','a'..'z',#138,#140,#159,#192..#214,
- #216..#223,#240,#154,#156,#224..#239,#241..#246,#248..#255];
-
-
- type SuggestionType = (stNoSuggest, stCloseMatch, stPhoneme);
-
- type
- TMemoSpell = class(TComponent)
- private
- { Private declarations }
- FSuggestType : SuggestionType; { Holds the default initial suggestion type }
- FDictionaryMain : string; { Holds the name of the main dictionary file }
- FDictionaryUser : string; { Holds the name of the user's custom dictionary file }
- FSuggestMax : byte; { Holds the maximum number of suggestions to return }
- UserDictID : integer; { Holds the ID number ofhte open user dictionary }
- FLeaveDictionaryOpen : boolean; { Should we leave the dictionary files open? }
- FDictionaryOpen : boolean; { Is the dictionary open? }
- FAvoidHighlight : boolean; { Should the dialog avoid the highlighted text? }
- protected
- { Protected declarations }
- DictDataPtr : pointer; { Pointer to internal dictionary data }
- SuggestDlg : TSugDialog; { The dialog box for this component }
- StartWord : string; { Temporary place to store the word being tested }
- IgnoreList : TStringList; { List of words to ignore }
- ReplaceList : TStringList; { Replacement word list }
- AlternateList : TStringList; { Replacement word alternate word list }
- procedure BaseCheckMemo(var TheMemo : TMemo; CheckStart, CheckLength : integer);
- procedure SetDialogPosition(var TheMemo : TMemo);
- public
- { Public declarations }
- UserDictionaryOpen : boolean; { Record if the custom user dictionary was opened ok }
- constructor Create(AOwner : TComponent); override; { Standard create method }
- procedure Free; { Standard free method }
- procedure SetMaximumSuggestions(Max : byte); { Method to set the maximum number of suggestions }
- procedure SetDictionaryMain(Filename : string); { Set a new main dictionary filename }
- procedure SetDictionaryUser(Filename : string); { Set a new user dictionary filename }
- property DictionaryOpen : boolean read FDictionaryOpen;
- published
- { Published declarations }
- procedure CheckMemo(TheMemo : TMemo); { Main method, check the spelling of a TMemo }
- procedure CheckMemoSelection(TheMemo : TMemo); { Alternate method, check the selected text only }
- procedure CheckDBMemo(TheMemo : TDBMemo); { Main method, check the spelling of a TDBMemo }
- procedure CheckDBMemoSelection(TheMemo : TDBMemo); { Alternate method, check the selected text only }
- procedure ClearLists; { Method to clear the ignore/replace lists }
- property SuggestType : SuggestionType read FSuggestType write FSuggestType default stCloseMatch;
- { Get/Set the initial suggestion type }
- property DictionaryMain : string read FDictionaryMain write SetDictionaryMain;
- { Get/Set the name of the main dictionary file }
- property DictionaryUser : string read FDictionaryUser write SetDictionaryUser;
- { Get/Set the name of the user dictionary file }
- property MaxSuggestions : byte read FSuggestMax write SetMaximumSuggestions default 10;
- { Get/Set the maximum number of suggestions }
- property LeaveDictionariesOpen : boolean read FLeaveDictionaryOpen write FLeaveDictionaryOpen default TRUE;
- { Get/Set whether the dictionary should be opened/closed after each call }
- property AvoidHighlight : boolean read FAvoidHighlight write FAvoidHighlight default true;
- { Get/Set whether the highlight should be avoided by the dialog }
- end;
-
-
- procedure Register;
-
- implementation
-
- uses BsASpl32;
-
-
- procedure Register; { Standard component registration procedure }
- begin
- RegisterComponents('Samples', [TMemoSpell]);
- end;
-
-
- constructor TMemoSpell.Create(AOwner : TComponent);
- { Standard create method }
- begin
- inherited Create(AOwner); { Make sure the base component to made }
- FSuggestType := stCloseMatch; { Set the default values }
- FDictionaryMain := 'acrop.dct';
- FDictionaryUser := 'custom.dct';
- FLeaveDictionaryOpen := TRUE;
- FDictionaryOpen := FALSE;
- UserDictionaryOpen := FALSE;
- FSuggestMax := 10;
- FAvoidHighlight := true;
- IgnoreList := TStringList.Create; { Create the list of ignored words }
- IgnoreList.Clear; { And set it to the way it is needed to be }
- IgnoreList.Sorted := TRUE;
- ReplaceList := TStringList.Create; { Create the list of words to replace }
- ReplaceList.Clear; { And set it up }
- ReplaceList.Sorted := FALSE;
- AlternateList := TStringList.Create; { Create the list of words to replace with }
- AlternateList.Clear; { And set it up }
- AlternateList.Sorted := FALSE;
- InitDictionaryData(DictDataPtr); { Create the internal dictionary data }
- SuggestDlg := TSugDialog.Create(Self); { Create the dialog box }
- SuggestDlg.DictDataPtr := DictDataPtr; { And let it know the internal data address }
- end;
-
- procedure TMemoSpell.Free;
- { Standard free method }
- begin
- if FDictionaryOpen then
- BsASpl32.CloseDictionaries(DictDataPtr);
- ReleaseDictionaryData(DictDataPtr);
- IgnoreList.Free; { Get rid of the ignore list }
- ReplaceList.Free; { Get rid of the replacement list }
- AlternateList.Free; { Get rid of the replacement word list }
- SuggestDlg.Free; { Get rid of the suggestion dialog box }
- inherited Free; { and then the base component }
- end;
-
- procedure TMemoSpell.SetMaximumSuggestions(Max : byte);
- { Set the maximum number of suggestions to return }
- begin
- FSuggestMax := Max; { And store the value }
- end;
-
- procedure TMemoSpell.SetDictionaryMain(Filename : string);
- begin
- if FDictionaryOpen or UserDictionaryOpen then
- begin
- BsASpl32.CloseDictionaries(DictDataPtr); { Close the dictionaries since filename is changing }
- FDictionaryOpen := FALSE; { Mark them as not opened }
- UserDictionaryOpen := FALSE;
- end;
- FDictionaryMain := Filename;
- end;
-
- procedure TMemoSpell.SetDictionaryUser(Filename : string);
- begin
- if FDictionaryOpen or UserDictionaryOpen then
- begin
- BsASpl32.CloseDictionaries(DictDataPtr); { Close the dictionaries since filename is changing }
- FDictionaryOpen := FALSE; { Mark them as not opened }
- UserDictionaryOpen := FALSE;
- end;
- FDictionaryUser := Filename;
- end;
-
- procedure TMemoSpell.ClearLists;
- begin
- IgnoreList.Clear; { Clear the ignore list }
- IgnoreList.Sorted := TRUE;
- ReplaceList.Clear; { Clear the list of words to replace }
- ReplaceList.Sorted := FALSE;
- AlternateList.Clear; { Clear the list of words to do the replacing with }
- AlternateList.Sorted := FALSE;
- end;
-
- procedure TMemoSpell.SetDialogPosition(var TheMemo : TMemo);
- { Set the position of the Suggestion Dialog based on the current line.
- If the dialog window and the editor area do not overlap, or there is no
- possibility of the highlight being covered don't move it. }
- var EditorScreen : TPoint;
- SelectBottom : longint;
- SelectTop : longint;
- SelectPoint : longint;
- SelectPos : longint;
- begin
- EditorScreen := TheMemo.ClientToScreen(TheMemo.ClientRect.TopLeft);
- if ((EditorScreen.X+TheMemo.Width) < SuggestDlg.Left) or
- (EditorScreen.X > (SuggestDlg.Left+SuggestDlg.Width)) or
- ((EditorScreen.Y+TheMemo.Height) < SuggestDlg.Top) or
- (EditorScreen.Y > (SuggestDlg.Top+SuggestDlg.Height)) then
- exit; { Not in editor area so exit without bothering to move the dialog }
- { Figure out where the current line really is on the screen }
- SelectPos := SendMessage(TheMemo.Handle, EM_LINEINDEX, $FFFF, 0);
- SelectPoint := SendMessage(TheMemo.Handle, EM_POSFROMCHAR, SelectPos, 0);
- SelectTop := (SelectPoint SHR 16) + EditorScreen.Y; { Get Position of selection on screen }
- SelectBottom := SelectTop + TheMemo.Font.Size + 3;
- { See if the highlight could actually be covered by the dialog }
- if (SelectBottom < SuggestDlg.Top) or
- (SelectTop > (SuggestDlg.Top+SuggestDlg.Height)) then
- exit; { Not near the highlight, exit without moving }
- { It could be covering the highlight, so move to the top or bottom of screen }
- if SelectBottom > (Screen.Height div 2) then
- SuggestDlg.Top := 20
- else
- SuggestDlg.Top := Screen.Height-SuggestDlg.Height-20;
- end;
-
- procedure TMemoSpell.BaseCheckMemo(var TheMemo : TMemo; CheckStart, CheckLength : integer);
- { The main method for this component. Test the spelling of the text in the passed memo }
- type LargeBuffer = array[0..$FFFF] of char; { 64K - the limit on memo's size }
- LargeBufferPtr = ^LargeBuffer;
- var Done : boolean; { Loop control }
- OldHide : boolean; { Storage for the original state of the HideSelection property }
- Changed : boolean; { Was anything in the memo changed? }
- EmptyList : TStringList; { Empty list in case user dictionary need to be made }
- HoldBuffer : LargeBufferPtr; { Buffer to speed up finding words }
- Start : integer; { Start of the word }
- WordEnd : integer; { End of the word }
- CheckLoc : integer; { Location we are currently checking }
- TheResult : integer; { Temporary ShowModal return storage }
- procedure SyncBuffer;
- { Duplicate the memo's text into the temporary buffer }
- begin
- TheMemo.GetTextBuf(HoldBuffer^, TheMemo.GetTextLen+1);
- { No need to worry about the length. TMemo buffers are 32K or smaller }
- end;
- function GetNextWord : string;
- { Get the next word in the memo }
- var CurrentTextLen : integer; { Temporary to hold length of memo's text }
- CurrentPos : integer;
- S : string;
- begin
- { Scan until we find the start of a word. Defined as someting starting with a letter }
- CurrentTextLen := TheMemo.GetTextLen; { Just to speed things up a litte }
- CurrentPos := CheckLoc; { Start at the selection }
- while (CurrentPos < CurrentTextLen) and
- (not (HoldBuffer^[CurrentPos] in SpellCharSet)) do { The english letters and }
- { non-english characters }
- Inc(CurrentPos); { Move to the next character }
- Start := CurrentPos; { Record the actual start of the word }
- { Find the end of the word. The word ends when a non-letter character }
- { or the character "'" is found. }
- S := '';
- while (CurrentPos < CurrentTextLen) and
- (HoldBuffer^[CurrentPos] in (SpellCharSet + [''''])) do
- begin
- S := S + HoldBuffer^[CurrentPos]; { Add it to the current word }
- Inc(CurrentPos); { Move to the next character }
- end;
- WordEnd := CurrentPos; { Save the end of the word }
- GetNextWord := S; { Return the found word }
- end;
- begin
- try
- HoldBuffer := NIL;
- New(HoldBuffer); { Create a temporary buffer to hold a copy of the memo's text }
- Changed := FALSE; { Nothing has been changed yet. }
- OldHide := TheMemo.HideSelection; { Save the old HideSelection property }
- TheMemo.HideSelection := FALSE; { and make sure selections are shown }
- SuggestDlg.MaxSuggest := FSuggestMax; { Set the maximum number of suggestions }
- if not FDictionaryOpen then { Check to see if the dictionary is already open }
- begin
- FDictionaryOpen := BsASpl32.OpenDictionary(DictDataPtr, FDictionaryMain); { Open the dictionaries }
- if not FDictionaryOpen then
- begin
- MessageDlg('Could not open dictionary', mtError, [mbOK], -1);
- exit;
- end;
- UserDictID := BsASpl32.OpenUserDictionary(DictDataPtr, FDictionaryUser); { And record if they actually opened }
- if UserDictID < 0 then { Didn't open so try to make one }
- begin
- EmptyList := TStringList.Create; { Create and clear to make an empty list }
- EmptyList.Clear;
- UserDictID := BsASpl32.BuildUserDictionary(DictDataPtr, FDictionaryUser, EmptyList); { Build dictionary }
- EmptyList.Free; { Free the empty list }
- end;
- UserDictionaryOpen := UserDictID > 0; { Check to see if dictionary was opened/made }
- end;
- SyncBuffer; { Load the text into a easy to access buffer }
- with SuggestDlg do { The suggestion dialog is used a lot so make it easily accessible }
- begin
- TheMemo.SelLength := 0; { Set up no selection and move to the }
- TheMemo.SelStart := 0; { start of the section to check }
- CheckLoc := CheckStart; { Start at the section to spell check }
- SuggestDlg.Caption := 'Suggestions: Scanning...'; { Tell the user we're scanning the text }
- if FAvoidHighlight then { Calculate a window position if avoiding the highlight }
- begin
- SuggestDlg.Top := (Screen.Height div 2) - (SuggestDlg.Height div 2); { Position dialog in center of screen }
- SuggestDlg.Left := (Screen.Width div 2) - (SuggestDlg.Width div 2);
- SetDialogPosition(TheMemo);
- end;
- SuggestDlg.WordEdit.Text := ''; { Clear the fields in the dialog window }
- SuggestDlg.SuggestList.Clear;
- SuggestDlg.TheResult := 0;
- SuggestDlg.DisableButtons; { Disable all but the Cancel button }
- Application.ProcessMessages; { Give Windows time to draw the window }
- Done := FALSE; { Assume we aren't done }
- repeat
- StartWord := GetNextWord; { Get the next word in the memo }
- Application.ProcessMessages; { Give Windows time to process mouse events }
- IF not BsASpl32.GoodWord(DictDataPtr, StartWord) THEN { Is the word in the dictionaries? }
- if IgnoreList.IndexOf(Uppercase(StartWord)) = -1 then { No, is it in the ignore list? }
- begin { Word not found and not ignored }
- TheMemo.SelStart := Start; { Highlight the word }
- TheMemo.SelLength := WordEnd - Start;
- if ReplaceList.IndexOf(Uppercase(StartWord)) = -1 then { In the replacement list? }
- begin
- case FSuggestType of { Build an inital list of suggestions }
- stCloseMatch : SuggestList.Items := BsASpl32.SuggestCloseMatch(DictDataPtr, StartWord, FSuggestMax);
- stPhoneme : SuggestList.Items := BsASpl32.SuggestPhoneme(DictDataPtr, StartWord, FSuggestMax);
- stNoSuggest : SuggestList.Clear;
- end;
- TheMemo.SelText := TheMemo.SelText;
- TheMemo.SelStart := Start; { Highlight the word }
- TheMemo.SelLength := WordEnd - Start;
- SuggestDlg.TheResult := 0; { Clear the Dialog result }
- SuggestDlg.Caption := 'Suggestions'; { Remove "Scanning" from caption }
- if FAvoidHighlight then { Check if the highlight has to be avoided }
- SetDialogPosition(TheMemo);
- if not SuggestDlg.Visible then { If dialog isn't visible, make it so }
- SuggestDlg.Show;
- SuggestDlg.EnableButtons; { Enable all the dialog controls }
- WordEdit.Text := StartWord; { Setup the Suggestion dialog }
- NotWord.Text := StartWord; { Setup the Word we are checking }
- SuggestDlg.ActiveControl := BtnIgnore; { Make the Ignore Button active control }
- Application.ProcessMessages; { Allow Windows to update things }
-
- repeat { Give Windows all the time until }
- Application.ProcessMessages; { one of the buttons are pressed }
- until SuggestDlg.TheResult <> 0;
- SuggestDlg.DisableButtons; { Disable the buttons }
- TheResult := SuggestDlg.TheResult; { Find out what the user did }
- end
- else
- begin
- TheResult := 101; { Fake Replace Button being pressed }
- WordEdit.Text := AlternateList.Strings[ReplaceList.IndexOf(Uppercase(StartWord))]; { And get the replacement word }
- end;
- case TheResult of { Display the suggestion dialog }
- 100 : Done := TRUE; { Cancel - end the spell checking }
- 101,
- 105 : begin { Replace }
- TheMemo.SelText := WordEdit.Text; { Replace - replace the word with the correction }
- Changed := TRUE;
- SyncBuffer; { Resync the temp buffer }
- WordEnd := TheMemo.SelStart + TheMemo.SelLength; { Reset the end of word }
- CheckLength := CheckLength + (Length(WordEdit.Text) - Length(StartWord)); { Adjust ending length }
- if TheResult = 105 then { Replace all occurences }
- begin
- ReplaceList.Add(StartWord);
- AlternateList.Add(WordEdit.Text);
- end;
- end;
- { Add - the questioned word to the user dictionary }
- 102 : BsASpl32.AddWord(DictDataPtr, StartWord, UserDictID);
- 103 : ; { Ignore just this occurence - Don't do anything }
- 104 : IgnoreList.Add(Uppercase(StartWord)); { Ignore All - add the questioned word to the ignore list }
- end;
- end;
- CheckLoc := WordEnd+1; { Move to one character after the end of the current word }
- until Done or (CheckLoc >= (CheckLength+CheckStart)) or (SuggestDlg.TheResult = 100);
- { Canceled or end of the memo is reached }
- end;
- if SuggestDlg.Visible then { Get rid of the dialog, if needed }
- SuggestDlg.Hide;
- if not Changed then { Let the user know something actually happened }
- MessageDlg('No changes made', mtInformation, [mbOK], -1)
- else
- MessageDlg('Checking complete', mtInformation, [mbOK], -1);
- finally
- Dispose(HoldBuffer); { Release the temporary buffer }
- if SuggestDlg.Visible then { Get rid of the dialog, if needed }
- SuggestDlg.Hide;
- if not FLeaveDictionaryOpen then { Check if the dictionaries should be closed }
- begin
- BsASpl32.CloseDictionaries(DictDataPtr); { Close the dictionaries }
- FDictionaryOpen := FALSE; { Mark them as not opened }
- UserDictionaryOpen := FALSE;
- end;
- AlternateList.Clear;
- ReplaceList.Clear;
- TheMemo.HideSelection := OldHide; { Restore the HideSelection property of the memo }
- end;
- end;
-
-
- procedure TMemoSpell.CheckMemo(TheMemo : TMemo);
- begin
- BaseCheckMemo(TheMemo, 0, TheMemo.GetTextLen+1); { Check the whole memo }
- end;
-
- procedure TMemoSpell.CheckMemoSelection(TheMemo : TMemo);
- var CheckStart, CheckLength : integer;
- begin
- with TheMemo do
- begin
- if SelLength = 0 then { Make sure there is something selected }
- exit; { If not then there is nothing to check }
- { Make sure we have a whole word at the start of the selection }
- CheckStart := SelStart; { Get the start of the selection }
- CheckLength := SelLength; { And the length }
- SelLength := 1; { Only look at one character at a time }
- while (CheckStart <> 0) and (TheMemo.SelText[1] in SpellCharSet) do
- begin
- Dec(CheckStart); { Move back another charater }
- Inc(CheckLength); { and expand the length to check }
- if SelStart <> 0 then
- SelStart := SelStart - 1; { then look at the charcter before that }
- SelLength := 1;
- end;
- { Now make sure we have a whole word at the end of the selection }
- SelStart := CheckStart + CheckLength; { Move to the end of the selected text }
- SelLength := 1; { Look at only a single charater }
- while (SelStart < GetTextLen) and (SelText[1] in SpellCharSet) do
- begin
- Inc(CheckLength); { Expand the selection length by one character }
- if SelStart < GetTextLen then { And move to the next if possible }
- SelStart := SelStart + 1;
- SelLength := 1;
- end;
- end;
- BaseCheckMemo(TheMemo, CheckStart, CheckLength); { Check the selected region }
- end;
-
- procedure TMemoSpell.CheckDBMemo(TheMemo : TDBMemo);
- begin
- CheckMemo(TMemo(TheMemo));
- end;
-
- procedure TMemoSpell.CheckDBMemoSelection(TheMemo : TDBMemo);
- begin
- CheckMemoSelection(TMemo(TheMemo));
- end;
-
- end.
-