home *** CD-ROM | disk | FTP | other *** search
- unit Richck32;
-
- interface
-
- { Revisions:
- 03/15/96 - Component created
- }
-
- uses
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
- StdCtrls, ComCtrls, 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
- TRichSpell = 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 BaseCheckRich(var TheEditor : TRichEdit;
- StartLine : longint; StartCol : integer;
- EndLine : longint; EndCol : integer);
- procedure SetDialogPosition(var TheEditor : TRichEdit; StartPos : longint);
- 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 CheckRich(TheEditor : TRichEdit); { Main method, check the spelling of a RichEdit control types }
- procedure CheckRichSelection(TheEditor : TRichEdit); { Alternate method to check only selected text }
- 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 FDictionaryMain;
- { Get/Set the name of the main dictionary file }
- property DictionaryUser : string read FDictionaryUser write FDictionaryUser;
- { 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', [TRichSpell]);
- end;
-
-
- constructor TRichSpell.Create(AOwner : TComponent);
- { Standard create method }
- begin
- inherited Create(AOwner); { Make sure the base component to made }
- FSuggestType := stCloseMatch; { Set the default values }
- FAvoidHighlight := true;
- FDictionaryMain := 'acrop.dct';
- FDictionaryUser := 'custom.dct';
- FLeaveDictionaryOpen := TRUE;
- FDictionaryOpen := FALSE;
- UserDictionaryOpen := FALSE;
- FSuggestMax := 10;
- 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 TRichSpell.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 TRichSpell.SetMaximumSuggestions(Max : byte);
- { Set the maximum number of suggestions to return }
- begin
- FSuggestMax := Max; { And store the value }
- end;
-
- procedure TRichSpell.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 TRichSpell.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 TRichSpell.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 TRichSpell.SetDialogPosition(var TheEditor : TRichEdit; StartPos : longint);
- { 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;
- SelectPoint : longint;
- SelectTop : integer;
- SelectBottom : integer;
- begin
- EditorScreen := TheEditor.ClientToScreen(TheEditor.ClientRect.TopLeft);
- if ((EditorScreen.X+TheEditor.Width) < SuggestDlg.Left) or
- (EditorScreen.X > (SuggestDlg.Left+SuggestDlg.Width)) or
- ((EditorScreen.Y+TheEditor.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 }
-
- exit;
- { ***** NOTE *****
- The following code is not executed due to a bug in the RichEdit that
- generates an exception if the EM_POSFROMCHAR message is sent to it.
- This means that the spelling dialog box will not move out of the way
- if it is covering the highlighted text in the RichEdit component.
- }
-
- SelectPoint := SendMessage(TheEditor.Handle, EM_POSFROMCHAR, $FFFF+(StartPos shr 16), 0);
- SelectTop := (SelectPoint SHR 16) + EditorScreen.Y; { Get Position of selection on screen }
- SelectBottom := SelectTop + TheEditor.SelAttributes.Height;
- { 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 TRichSpell.BaseCheckRich(var TheEditor : TRichEdit;
- StartLine : longint; StartCol : integer;
- EndLine : longint; EndCol : integer);
- { The main method for this component. Test the spelling of the text in the passed RichEdit }
- var Done : boolean; { Loop control }
- OldHide : boolean; { Storage for the original state of the HideSelection property }
- Changed : boolean; { Was anything in the control changed? }
- EmptyList : TStringList; { Empty list in case user dictionary need to be made }
- TheResult : integer; { Temporary storage for ShowModal return value }
- Start : integer; { Start of the word }
- WordEnd : integer; { End of the word }
- CCol : integer; { Current column being checked }
- CLine : longint; { Current line being checked }
- OffSet : integer; { Temporary offset to start of line }
- StartPosition : longint; { Starting position of the checked text }
- function StripWord(L : string; var SCol : INTEGER; var EndCol : integer) : string;
- BEGIN
- { Scan for the start of a word }
- WHILE (SCol<= Length(L)) AND (NOT (L[SCol] IN SpellCharSet)) DO
- BEGIN
- Inc(SCol);
- END;
-
- EndCol := SCol; { Assume the end is the same as the start - i.e. one letter word }
- IF SCol > Length(L) THEN { No non-letter left on line, so no word found }
- BEGIN
- StripWord := '';
- Exit;
- END;
- { Scan for the end of the word }
- WHILE (EndCol <= Length(L)) AND (L[EndCol] IN (SpellCharSet + [''''])) DO
- BEGIN
- Inc(EndCol);
- END;
- StripWord := Copy(L,SCol,EndCol-SCol); { Return the word we found }
- END;
- function GetNextWord : STRING;
- var LineLength : integer;
- BEGIN
- GetNextWord := '';
- WITH TheEditor DO
- BEGIN
- LineLength := Length(TheEditor.Lines.Strings[CLine]);
- IF CCol > LineLength THEN
- BEGIN
- Inc(CLine);
- CCol := 1;
- END;
- IF CLine > Lines.Count THEN { Passed the end of the editor get out of here }
- Exit;
- IF (CLine = Lines.Count) AND (CCol >= Length(Lines.Strings[CLine])) THEN { Ditto }
- Exit;
- GetNextWord := StripWord(Lines.Strings[CLine], CCol, WordEnd); { Get the text of the word }
- Start := CCol; { Save where this word started }
- END;
- END;
- begin
- try
- Changed := FALSE; { Nothing has been changed yet. }
- OldHide := TheEditor.HideSelection; { Save the old HideSelection property }
- TheEditor.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
- 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);
- 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;
- with SuggestDlg do { The suggestion dialog is used a lot so make it easily accessible }
- begin
- CCol := StartCol; { Set to beginning of section to spell check }
- CLine := StartLine;
- SuggestDlg.Caption := 'Suggestions: Scanning...'; { Tell the user we're scanning the text }
- TheEditor.SelStart := SendMessage(TheEditor.Handle, EM_LINEINDEX, CLine, 0);
- TheEditor.SelLength := 0; { Move to start of text to check }
- StartPosition := TheEditor.SelStart;
- if FAvoidHighlight then { Calculate a window position if avoiding the highlight }
- SetDialogPosition(TheEditor, TheEditor.SelStart);
- 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 control }
- Application.ProcessMessages; { Give Windows time to process mouse events }
- if StartWord <> '' then
- begin
- 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 }
- OffSet := SendMessage(TheEditor.Handle, EM_LINEINDEX, CLine, 0);
- TheEditor.SelStart := OffSet + Start-1;
- TheEditor.SelLength := (WordEnd-Start);
- Application.ProcessMessages; { Give Windows a little time to update things}
- if ReplaceList.IndexOf(StartWord) = -1 then { In the replacement list? }
- begin { No it isn't in the replace list }
- 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;
- 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(TheEditor, TheEditor.SelStart);
- 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 }
- 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(StartWord)]; { And get the replacement word }
- end;
- case TheResult of { Do what the user told us }
- 100 : Done := TRUE; { Cancel - end the spell checking }
- 101,
- 105 : begin
- { Replace - replace the word with the correction }
- TheEditor.SelText := WordEdit.Text;
- Changed := TRUE;
- { Reset the end of word counter to reflect possible difference in word lengths }
- WordEnd := WordEnd + (Length(WordEdit.Text) - Length(StartWord));
- if CLine = EndLine then { If this is the last line to test reset the ending column }
- EndCol := EndCol + (Length(WordEdit.Text) - Length(StartWord));
- 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 - Dont' do anything }
- { Ignore All occurences - add the questioned word to the ignore list }
- 104 : IgnoreList.Add(Uppercase(StartWord));
- end;
- SuggestDlg.Caption := 'Suggestions: Scanning...'; { Did something Return to scanning... }
- end;
- end;
- CCol := WordEnd+1; { Move to one character after the end of the current word }
- until Done or ((CLine >= EndLine) and (CCol >= EndCol)) or (SuggestDlg.TheResult = 100);
- { Canceled or end of the editor is reached }
- end;
- TheEditor.SelStart := StartPosition; { Move to start of checked text }
- TheEditor.SelLength := 0;
- 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
- 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;
- TheEditor.HideSelection := OldHide; { Restore the HideSelection property of the control }
- end;
- end;
-
- procedure TRichSpell.CheckRich(TheEditor : TRichEdit);
- begin
- { Call the base function to check the entire Editor }
- BaseCheckRich(TheEditor, 0,1, TheEditor.Lines.Count-1, Length(TheEditor.Lines.Strings[TheEditor.Lines.Count-1]));
- end;
-
- procedure TRichSpell.CheckRichSelection(TheEditor : TRichEdit);
- var CheckStart, CheckLength : integer;
- StartLine, StartCol : longint;
- EndLine, EndCol : longint;
- OffSet : longint;
- begin
- with TheEditor 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 (TheEditor.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;
- StartLine := SendMessage(TheEditor.Handle, EM_LINEFROMCHAR, CheckStart, 0);
- OffSet := SendMessage(TheEditor.Handle, EM_LINEINDEX, StartLine, 0);
- StartCol := CheckStart - OffSet+1;
- EndLine := SendMessage(TheEditor.Handle, EM_LINEFROMCHAR, CheckStart+CheckLength, 0);
- OffSet := SendMessage(TheEditor.Handle, EM_LINEINDEX, EndLine, 0);
- EndCol := (CheckStart+CheckLength) - OffSet+1;
- BaseCheckRich(TheEditor, StartLine, StartCol, EndLine, EndCol); { Check the selected region }
- end;
-
-
- end.