home *** CD-ROM | disk | FTP | other *** search
- unit Orphchk;
-
- interface
-
- { IMPORTANT: This component REQUIRES that you have Turbo Power's Orpheus
- components installed. It makes use of the Orpheus TOVCCustomEditor so
- you can spell check the following Orpheus editor types with the OrphCheck
- method: TOvcCustomEditor, TOvcEditor, TOvcCustomTextEditor, TOvcTextFileEditor
- and TOvcdbEditor.
- None of Turbo Power's units, components or source is included with this package
- as that would be illegal redistribution of their product. However, if you
- have a need for a large editor (files up to 16 megabytes) to replace TMemo
- I would highly recommend you purchase Turbo Power's Orpheus package. Besides
- the large editors it also provides a large number of useful and powerful
- components for Delphi such as:
-
- Large virtual list boxes, numerous data entry types, array editors,
- Table components, spinners, rotated labels, timers and much more.
-
- Turbo Power Software can be reached at: 1-800-333-4160 }
-
-
- { Revisions:
- 1/24/96 - Added ShowStatus property
- 1/31/96 - Fixed problem with Orphues' SetSelection not showing highlighted text
- - Minor speed up in GetNextWord/StripWord
- 2/8/96 - Made spelling suggestion window always visible
- - ShowStatus removed now that dialog is always visible
- - MaxSuggest is now 255 (was limited to 30)
- - AvoidHighlight property added
- }
-
-
- uses
- SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
- Forms, Dialogs, StdCtrls, SugDialg, OvcBase, OvcData, OvcEdit;
- { ^ Orpheus units needed ^}
-
- type SuggestionType = (stNoSuggest, stCloseMatch, stPhoneme);
-
- type
- TOrphSpell = 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 BaseCheckOrph(var TheEditor : TOvcCustomEditor;
- StartLine : longint; StartCol : integer;
- EndLine : longint; EndCol : integer);
- procedure SetDialogPosition(var TheEditor : TOvcCustomEditor; SelectLine : 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 CheckOrph(TheEditor : TOvcCustomEditor); { Main method, check the spelling of a Orpheus Editor types }
- procedure CheckOrphSelection(TheEditor : TOvcCustomEditor); { 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 BaseASpl;
-
-
- procedure Register; { Standard component registration procedure }
- begin
- RegisterComponents('Samples', [TOrphSpell]);
- end;
-
-
- constructor TOrphSpell.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 TOrphSpell.Free;
- { Standard free method }
- begin
- if FDictionaryOpen then
- BaseASpl.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 TOrphSpell.SetMaximumSuggestions(Max : byte);
- { Set the maximum number of suggestions to return }
- begin
- FSuggestMax := Max; { And store the value }
- end;
-
- procedure TOrphSpell.SetDictionaryMain(Filename : string);
- begin
- if FDictionaryOpen or UserDictionaryOpen then
- begin
- BaseASpl.CloseDictionaries(DictDataPtr); { Close the dictionaries since filename is changing }
- FDictionaryOpen := FALSE; { Mark them as not opened }
- UserDictionaryOpen := FALSE;
- end;
- FDictionaryMain := Filename;
- end;
-
- procedure TOrphSpell.SetDictionaryUser(Filename : string);
- begin
- if FDictionaryOpen or UserDictionaryOpen then
- begin
- BaseASpl.CloseDictionaries(DictDataPtr); { Close the dictionaries since filename is changing }
- FDictionaryOpen := FALSE; { Mark them as not opened }
- UserDictionaryOpen := FALSE;
- end;
- FDictionaryUser := Filename;
- end;
-
- procedure TOrphSpell.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 TOrphSpell.SetDialogPosition(var TheEditor : TOvcCustomEditor; SelectLine : 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;
- SelectBottom : integer;
- Metrics : TTextMetric;
- RowHeight : 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 }
-
- { Get the Row Height }
- TheEditor.Canvas.Font := TheEditor.FixedFont.Font;
- GetTextMetrics(TheEditor.Canvas.Handle, Metrics);
- RowHeight := Metrics.tmHeight+Metrics.tmExternalLeading+1;
-
- (*
- { Use this instead of the above three lines if you are using a
- version of Orpheus earlier than v1.02. However, it is only accurate
- for the default system font. }
- RowHeight := ABS(TheEditor.FixedFont.Font.Height)+3;
- *)
-
- SelectBottom := ((SelectLine-TheEditor.TopLine)+1)*RowHeight+EditorScreen.Y;
- { See if the highlight could actually be covered by the dialog }
- if (SelectBottom < SuggestDlg.Top) or
- ((SelectBottom-RowHeight) > (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 TOrphSpell.BaseCheckOrph(var TheEditor : TOvcCustomEditor;
- StartLine : longint; StartCol : integer;
- EndLine : longint; EndCol : integer);
- { The main method for this component. Test the spelling of the text in the passed memo }
- 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 }
- 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 }
- 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 ['A'..'Z','a'..'z',#138,#140,#159, { Skip any non-letters }
- #192..#214,#216..#223,#240,
- #154,#156,#224..#239,
- #241..#246,#248..#255])) 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 ['A'..'Z','a'..'z',#138,#140,#159, { Only add letters and "'" }
- #192..#214,#216..#223,#240,
- #154,#156,#224..#239,
- #241..#246,#248..#255,'''']) DO
- BEGIN
- Inc(EndCol);
- END;
- StripWord := Copy(L,SCol,EndCol-SCol); { Return the word we found }
- END;
- function GetNextWord : STRING;
- BEGIN
- GetNextWord := '';
- WITH TheEditor DO
- BEGIN
- IF CCol > LineLength[CLine] THEN
- BEGIN
- Inc(CLine);
- CCol := 1;
- END;
- IF CLine > LineCount THEN { Passed the end of the editor get out of here }
- Exit;
- IF (CLine = LineCount) AND (CCol >= LineLength[CLine]) THEN { Ditto }
- Exit;
- GetNextWord := StripWord(Lines[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
- FDictionaryOpen := BaseASpl.OpenDictionary(DictDataPtr, FDictionaryMain); { Open the dictionaries }
- if not FDictionaryOpen then
- begin
- MessageDlg('Could not open dictionary', mtError, [mbOK], -1);
- exit;
- end;
- UserDictID := BaseASpl.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 := BaseASpl.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.SetCaretPosition(1,1); { Move to start of editor }
- 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(TheEditor,1);
- 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 StartWord <> '' then
- begin
- IF not BaseASpl.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 }
- if WordEnd >= TheEditor.VisibleColumns then { Make sure the highlight is visible }
- TheEditor.SetSelection(CLine, Start, CLine, WordEnd, TRUE) { Force a scroll left if needed }
- else
- TheEditor.SetSelection(CLine, Start, CLine, WordEnd, FALSE); { Force a Scroll right if needed }
- 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 := BaseASpl.SuggestCloseMatch(DictDataPtr, StartWord, FSuggestMax);
- stPhoneme : SuggestList.Items := BaseASpl.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,CLine);
- 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.Replace(StartWord, WordEdit.Text, [soReplace, soSelText]);
- 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 : BaseASpl.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.SetSelection(1, 1, 1, 1, TRUE); { Move to the top of the editor }
- 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
- BaseASpl.CloseDictionaries(DictDataPtr); { Close the dictionaries }
- FDictionaryOpen := FALSE; { Mark them as not opened }
- UserDictionaryOpen := FALSE;
- end;
- TheEditor.HideSelection := OldHide; { Restore the HideSelection property of the memo }
- end;
- end;
-
- procedure TOrphSpell.CheckOrph(TheEditor : TOvcCustomEditor);
- begin
- { Call the base function to check the entire Editor }
- BaseCheckOrph(TheEditor, 1,1, TheEditor.LineCount, TheEditor.LineLength[TheEditor.LineCount]);
- end;
-
- procedure TOrphSpell.CheckOrphSelection(TheEditor : TOvcCustomEditor);
- var StartLine, EndLine : longint;
- StartCol, EndCol : integer;
- S : string;
- begin
- { If nothing is selected then just exit since there is nothing to check }
- if not TheEditor.GetSelection(StartLine, StartCol, EndLine, EndCol) then
- exit;
- { Scan backward to make sure we're at the beginning of a word }
- S := TheEditor.Lines[StartLine];
- WHILE (StartCol > 0) AND (S[StartCol] IN ['A'..'Z','a'..'z',#138,#140,#159,
- #192..#214,#216..#223,#240,
- #154,#156,#224..#239,
- #241..#246,#248..#255]) DO
- Dec(StartCol);
- IF StartCol = 0 THEN
- StartCol := 1;
- { Scan forward to make sure we have a whole word at the end of the selection }
- S := TheEditor.Lines[EndLine];
- Dec(EndCol);
- if EndCol < 0 then
- EndCol := 0;
- while (EndCol < Length(S)) and (S[EndCol] in ['A'..'Z','a'..'z',#138,#140,#159,
- #192..#214,#216..#223,#240,
- #154,#156,#224..#239,
- #241..#246,#248..#255]) DO
- Inc(EndCol);
- if EndCol > Length(S) then
- EndCol := Length(S);
- BaseCheckOrph(TheEditor, StartLine,StartCol, EndLine,EndCol);
- end;
-
-
- end.