You've already forked lazarus-ccr
jvcllaz: Add TJvSpellChecker incl demo and sample dictionaries
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6237 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
178429
components/jvcllaz/dict/dutch.dic
Normal file
178429
components/jvcllaz/dict/dutch.dic
Normal file
File diff suppressed because it is too large
Load Diff
30651
components/jvcllaz/dict/english.dic
Normal file
30651
components/jvcllaz/dict/english.dic
Normal file
File diff suppressed because it is too large
Load Diff
7
components/jvcllaz/dict/readme.txt
Normal file
7
components/jvcllaz/dict/readme.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Dictionary file format:
|
||||||
|
The words are compressed.
|
||||||
|
Each word is prefixed with a digit letter.
|
||||||
|
The digit gives the number of chars the word shares with the previous uncompressed word.
|
||||||
|
The digit letters range from '0' to '9'. Longer shared stems are not handled.
|
||||||
|
The words are sorted ascending according to their uncompressed text per TStringList.Sort.
|
||||||
|
The lineends are CR only to save space (TStringList.LoadFromFile reads that without problems).
|
@ -0,0 +1,89 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CONFIG>
|
||||||
|
<ProjectOptions>
|
||||||
|
<Version Value="11"/>
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<General>
|
||||||
|
<Flags>
|
||||||
|
<UseDefaultCompilerOptions Value="True"/>
|
||||||
|
</Flags>
|
||||||
|
<SessionStorage Value="InProjectDir"/>
|
||||||
|
<MainUnit Value="0"/>
|
||||||
|
<Title Value="JvSpellCheckerDemo"/>
|
||||||
|
<Scaled Value="True"/>
|
||||||
|
<ResourceType Value="res"/>
|
||||||
|
<UseXPManifest Value="True"/>
|
||||||
|
<XPManifest>
|
||||||
|
<DpiAware Value="True"/>
|
||||||
|
</XPManifest>
|
||||||
|
<Icon Value="0"/>
|
||||||
|
</General>
|
||||||
|
<BuildModes Count="1">
|
||||||
|
<Item1 Name="Default" Default="True"/>
|
||||||
|
</BuildModes>
|
||||||
|
<PublishOptions>
|
||||||
|
<Version Value="2"/>
|
||||||
|
</PublishOptions>
|
||||||
|
<RunParams>
|
||||||
|
<FormatVersion Value="2"/>
|
||||||
|
<Modes Count="0"/>
|
||||||
|
</RunParams>
|
||||||
|
<RequiredPackages Count="2">
|
||||||
|
<Item1>
|
||||||
|
<PackageName Value="JvCmpR"/>
|
||||||
|
</Item1>
|
||||||
|
<Item2>
|
||||||
|
<PackageName Value="LCL"/>
|
||||||
|
</Item2>
|
||||||
|
</RequiredPackages>
|
||||||
|
<Units Count="3">
|
||||||
|
<Unit0>
|
||||||
|
<Filename Value="JvSpellCheckerDemo.lpr"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit0>
|
||||||
|
<Unit1>
|
||||||
|
<Filename Value="JvSpellCheckerForm.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
<HasResources Value="True"/>
|
||||||
|
</Unit1>
|
||||||
|
<Unit2>
|
||||||
|
<Filename Value="MainFrm.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
<ComponentName Value="frmMain"/>
|
||||||
|
<HasResources Value="True"/>
|
||||||
|
<ResourceBaseClass Value="Form"/>
|
||||||
|
</Unit2>
|
||||||
|
</Units>
|
||||||
|
</ProjectOptions>
|
||||||
|
<CompilerOptions>
|
||||||
|
<Version Value="11"/>
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<Target>
|
||||||
|
<Filename Value="..\..\bin\JvSpellCheckerDemo"/>
|
||||||
|
</Target>
|
||||||
|
<SearchPaths>
|
||||||
|
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||||
|
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
|
||||||
|
</SearchPaths>
|
||||||
|
<Linking>
|
||||||
|
<Options>
|
||||||
|
<Win32>
|
||||||
|
<GraphicApplication Value="True"/>
|
||||||
|
</Win32>
|
||||||
|
</Options>
|
||||||
|
</Linking>
|
||||||
|
</CompilerOptions>
|
||||||
|
<Debugging>
|
||||||
|
<Exceptions Count="3">
|
||||||
|
<Item1>
|
||||||
|
<Name Value="EAbort"/>
|
||||||
|
</Item1>
|
||||||
|
<Item2>
|
||||||
|
<Name Value="ECodetoolError"/>
|
||||||
|
</Item2>
|
||||||
|
<Item3>
|
||||||
|
<Name Value="EFOpenError"/>
|
||||||
|
</Item3>
|
||||||
|
</Exceptions>
|
||||||
|
</Debugging>
|
||||||
|
</CONFIG>
|
@ -0,0 +1,16 @@
|
|||||||
|
program JvSpellCheckerDemo;
|
||||||
|
|
||||||
|
uses
|
||||||
|
Forms, Interfaces,
|
||||||
|
MainFrm in 'MainFrm.pas' {frmMain},
|
||||||
|
JvSpellCheckerForm in 'JvSpellCheckerForm.pas' {frmSpellChecker};
|
||||||
|
|
||||||
|
{$R *.res}
|
||||||
|
|
||||||
|
begin
|
||||||
|
Application.Scaled := True;
|
||||||
|
Application.Initialize;
|
||||||
|
Application.CreateForm(TfrmMain, frmMain);
|
||||||
|
Application.CreateForm(TfrmSpellChecker, frmSpellChecker);
|
||||||
|
Application.Run;
|
||||||
|
end.
|
@ -0,0 +1,193 @@
|
|||||||
|
object frmSpellChecker: TfrmSpellChecker
|
||||||
|
Left = 338
|
||||||
|
Top = 177
|
||||||
|
BorderStyle = bsDialog
|
||||||
|
Caption = 'Spell check document'
|
||||||
|
ClientHeight = 334
|
||||||
|
ClientWidth = 384
|
||||||
|
Color = clBtnFace
|
||||||
|
DefaultMonitor = dmDesktop
|
||||||
|
Font.Charset = DEFAULT_CHARSET
|
||||||
|
Font.Color = clWindowText
|
||||||
|
Font.Height = -11
|
||||||
|
Font.Name = 'MS Shell Dlg 2'
|
||||||
|
Font.Style = []
|
||||||
|
FormStyle = fsStayOnTop
|
||||||
|
OldCreateOrder = False
|
||||||
|
Position = poMainFormCenter
|
||||||
|
Scaled = False
|
||||||
|
OnCreate = FormCreate
|
||||||
|
OnShow = FormShow
|
||||||
|
PixelsPerInch = 96
|
||||||
|
TextHeight = 13
|
||||||
|
object Label1: TLabel
|
||||||
|
Left = 16
|
||||||
|
Top = 16
|
||||||
|
Width = 72
|
||||||
|
Height = 13
|
||||||
|
Caption = 'Not in wordlist:'
|
||||||
|
end
|
||||||
|
object Label2: TLabel
|
||||||
|
Left = 16
|
||||||
|
Top = 102
|
||||||
|
Width = 62
|
||||||
|
Height = 13
|
||||||
|
Caption = 'Suggestions:'
|
||||||
|
end
|
||||||
|
object lblNoSuggestions: TLabel
|
||||||
|
Left = 179
|
||||||
|
Top = 101
|
||||||
|
Width = 93
|
||||||
|
Height = 13
|
||||||
|
Caption = '(nothing to display)'
|
||||||
|
Visible = False
|
||||||
|
end
|
||||||
|
object Label3: TLabel
|
||||||
|
Left = 16
|
||||||
|
Top = 56
|
||||||
|
Width = 65
|
||||||
|
Height = 13
|
||||||
|
Caption = 'Replace with:'
|
||||||
|
end
|
||||||
|
object edNewWord: TEdit
|
||||||
|
Left = 16
|
||||||
|
Top = 72
|
||||||
|
Width = 254
|
||||||
|
Height = 21
|
||||||
|
Anchors = [akLeft, akTop, akRight]
|
||||||
|
TabOrder = 1
|
||||||
|
end
|
||||||
|
object lbSuggestions: TListBox
|
||||||
|
Left = 16
|
||||||
|
Top = 120
|
||||||
|
Width = 254
|
||||||
|
Height = 82
|
||||||
|
Anchors = [akLeft, akTop, akRight, akBottom]
|
||||||
|
ItemHeight = 13
|
||||||
|
TabOrder = 2
|
||||||
|
OnClick = lbSuggestionsClick
|
||||||
|
end
|
||||||
|
object btnIgnore: TButton
|
||||||
|
Left = 294
|
||||||
|
Top = 32
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Action = acIgnore
|
||||||
|
Anchors = [akTop, akRight]
|
||||||
|
TabOrder = 3
|
||||||
|
end
|
||||||
|
object btnIgnoreAll: TButton
|
||||||
|
Left = 294
|
||||||
|
Top = 61
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Action = acIgnoreAll
|
||||||
|
Anchors = [akTop, akRight]
|
||||||
|
TabOrder = 4
|
||||||
|
end
|
||||||
|
object btnChange: TButton
|
||||||
|
Left = 294
|
||||||
|
Top = 120
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Action = acChange
|
||||||
|
Anchors = [akTop, akRight]
|
||||||
|
TabOrder = 5
|
||||||
|
end
|
||||||
|
object btnClose: TButton
|
||||||
|
Left = 294
|
||||||
|
Top = 286
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Action = acClose
|
||||||
|
Anchors = [akRight, akBottom]
|
||||||
|
Cancel = True
|
||||||
|
TabOrder = 8
|
||||||
|
end
|
||||||
|
object btnAdd: TButton
|
||||||
|
Left = 294
|
||||||
|
Top = 149
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Action = acAdd
|
||||||
|
Anchors = [akTop, akRight]
|
||||||
|
TabOrder = 6
|
||||||
|
end
|
||||||
|
object GroupBox1: TGroupBox
|
||||||
|
Left = 16
|
||||||
|
Top = 209
|
||||||
|
Width = 257
|
||||||
|
Height = 105
|
||||||
|
Anchors = [akLeft, akRight, akBottom]
|
||||||
|
Caption = ' Ignore: '
|
||||||
|
TabOrder = 7
|
||||||
|
object chkUpperCase: TCheckBox
|
||||||
|
Left = 24
|
||||||
|
Top = 23
|
||||||
|
Width = 207
|
||||||
|
Height = 17
|
||||||
|
Caption = '&UPPERCASE words'
|
||||||
|
TabOrder = 0
|
||||||
|
end
|
||||||
|
object chkNumber: TCheckBox
|
||||||
|
Left = 24
|
||||||
|
Top = 39
|
||||||
|
Width = 207
|
||||||
|
Height = 17
|
||||||
|
Caption = 'Words with &numbers'
|
||||||
|
TabOrder = 1
|
||||||
|
end
|
||||||
|
object chkURL: TCheckBox
|
||||||
|
Left = 24
|
||||||
|
Top = 55
|
||||||
|
Width = 207
|
||||||
|
Height = 17
|
||||||
|
Caption = 'Internet and file &paths'
|
||||||
|
TabOrder = 2
|
||||||
|
end
|
||||||
|
object chkHTML: TCheckBox
|
||||||
|
Left = 24
|
||||||
|
Top = 71
|
||||||
|
Width = 207
|
||||||
|
Height = 17
|
||||||
|
Caption = 'HT&ML code'
|
||||||
|
TabOrder = 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
object edBadWord: TEdit
|
||||||
|
Left = 16
|
||||||
|
Top = 32
|
||||||
|
Width = 254
|
||||||
|
Height = 21
|
||||||
|
Anchors = [akLeft, akTop, akRight]
|
||||||
|
Color = clBtnFace
|
||||||
|
ReadOnly = True
|
||||||
|
TabOrder = 0
|
||||||
|
end
|
||||||
|
object alSpell: TActionList
|
||||||
|
OnUpdate = alSpellUpdate
|
||||||
|
Left = 296
|
||||||
|
Top = 224
|
||||||
|
object acIgnore: TAction
|
||||||
|
Caption = '&Ignore'
|
||||||
|
OnExecute = acIgnoreExecute
|
||||||
|
end
|
||||||
|
object acIgnoreAll: TAction
|
||||||
|
Caption = 'Ignore &All'
|
||||||
|
OnExecute = acIgnoreAllExecute
|
||||||
|
end
|
||||||
|
object acChange: TAction
|
||||||
|
Caption = 'C&hange'
|
||||||
|
OnExecute = acChangeExecute
|
||||||
|
end
|
||||||
|
object acAdd: TAction
|
||||||
|
Caption = 'A&dd'
|
||||||
|
OnExecute = acAddExecute
|
||||||
|
end
|
||||||
|
object acClose: TAction
|
||||||
|
Caption = '&Close'
|
||||||
|
ShortCut = 27
|
||||||
|
OnExecute = acCloseExecute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,329 @@
|
|||||||
|
{******************************************************************
|
||||||
|
|
||||||
|
JEDI-VCL Demo
|
||||||
|
|
||||||
|
Copyright (C) 2002 Project JEDI
|
||||||
|
|
||||||
|
Original author:
|
||||||
|
|
||||||
|
Contributor(s):
|
||||||
|
|
||||||
|
You may retrieve the latest version of this file at the JEDI-JVCL
|
||||||
|
home page, located at http://jvcl.delphi-jedi.org
|
||||||
|
|
||||||
|
The contents of this file are used with permission, subject to
|
||||||
|
the Mozilla Public License Version 1.1 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may
|
||||||
|
obtain a copy of the License at
|
||||||
|
http://www.mozilla.org/MPL/MPL-1_1Final.html
|
||||||
|
|
||||||
|
Software distributed under the License is distributed on an
|
||||||
|
"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||||
|
implied. See the License for the specific language governing
|
||||||
|
rights and limitations under the License.
|
||||||
|
|
||||||
|
******************************************************************}
|
||||||
|
|
||||||
|
unit JvSpellCheckerForm;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
//{$I jvcl.inc}
|
||||||
|
|
||||||
|
uses
|
||||||
|
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
|
||||||
|
Dialogs, StdCtrls, JvSpellChecker, ActnList;
|
||||||
|
|
||||||
|
type
|
||||||
|
{ This is an example form with code that shows how to implement a spell check form
|
||||||
|
that can be displayed to the end user. The TJvSpellChecker is created dynamically
|
||||||
|
so you don't need to install it to run the demo: just make sure the JvSpellChecker and
|
||||||
|
JvSpellIntf units are somewhere in your path.
|
||||||
|
|
||||||
|
The main tasks of this form is to:
|
||||||
|
* Scan for the next misspelled word (GetNextWord)
|
||||||
|
* Display the misspelled word along with suggested replacements
|
||||||
|
* Call an event handler to highlight the text in the original control
|
||||||
|
* Call an event handler when the user wants to replace the word
|
||||||
|
* Add a word to the user dictionary (btnAdd)
|
||||||
|
* Add a word to the ignore list (btnIgnoreAll)
|
||||||
|
|
||||||
|
This form doesn't implement everything needed for a professional looking form (i.e only
|
||||||
|
enable buttons as needed) but it can serve as a base for a more complete implementation.
|
||||||
|
}
|
||||||
|
|
||||||
|
TJvReplaceTextEvent = procedure(Sender: TObject; StartIndex, ALength: integer; const NewText: string) of object;
|
||||||
|
TJvSelectTextEvent = procedure(Sender: TObject; StartIndex, ALength: integer) of object;
|
||||||
|
TfrmSpellChecker = class(TForm)
|
||||||
|
Label1: TLabel;
|
||||||
|
edNewWord: TEdit;
|
||||||
|
Label2: TLabel;
|
||||||
|
lbSuggestions: TListBox;
|
||||||
|
btnIgnore: TButton;
|
||||||
|
btnIgnoreAll: TButton;
|
||||||
|
btnChange: TButton;
|
||||||
|
btnClose: TButton;
|
||||||
|
btnAdd: TButton;
|
||||||
|
GroupBox1: TGroupBox;
|
||||||
|
chkUpperCase: TCheckBox;
|
||||||
|
chkNumber: TCheckBox;
|
||||||
|
chkURL: TCheckBox;
|
||||||
|
chkHTML: TCheckBox;
|
||||||
|
lblNoSuggestions: TLabel;
|
||||||
|
Label3: TLabel;
|
||||||
|
edBadWord: TEdit;
|
||||||
|
alSpell: TActionList;
|
||||||
|
acIgnore: TAction;
|
||||||
|
acIgnoreAll: TAction;
|
||||||
|
acChange: TAction;
|
||||||
|
acAdd: TAction;
|
||||||
|
acClose: TAction;
|
||||||
|
procedure FormCreate(Sender: TObject);
|
||||||
|
procedure FormShow(Sender: TObject);
|
||||||
|
procedure acIgnoreExecute(Sender: TObject);
|
||||||
|
procedure acIgnoreAllExecute(Sender: TObject);
|
||||||
|
procedure acChangeExecute(Sender: TObject);
|
||||||
|
procedure acAddExecute(Sender: TObject);
|
||||||
|
procedure acCloseExecute(Sender: TObject);
|
||||||
|
procedure alSpellUpdate(AAction: TBasicAction; var Handled: Boolean);
|
||||||
|
procedure lbSuggestionsClick(Sender: TObject);
|
||||||
|
private
|
||||||
|
{ Private declarations }
|
||||||
|
FStartIndex, FLength: integer;
|
||||||
|
ASpellChecker: TJvSpellChecker;
|
||||||
|
FOnReplaceText: TJvReplaceTextEvent;
|
||||||
|
FOnSelectText: TJvSelectTextEvent;
|
||||||
|
procedure CloseAndReport(ReportSuccess: boolean);
|
||||||
|
function GetNextWord: boolean;
|
||||||
|
procedure DoReplaceText(Sender: TObject; StartIndex, ALength: integer; const NewText: string);
|
||||||
|
procedure DoSelectText(Sender: TObject; StartIndex, ALength: integer);
|
||||||
|
function GetSpellText: string;
|
||||||
|
procedure SetSpellText(const Value: string);
|
||||||
|
procedure DoCanIgnore(Sender: TObject; const Value: string; var CanIgnore: boolean);
|
||||||
|
procedure CheckSuggestions;
|
||||||
|
public
|
||||||
|
{ Public declarations }
|
||||||
|
property SpellText: string read GetSpellText write SetSpellText;
|
||||||
|
property OnReplaceText: TJvReplaceTextEvent read FOnReplaceText write FOnReplaceText;
|
||||||
|
property OnSelectText: TJvSelectTextEvent read FOnSelectText write FOnSelectText;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
frmSpellChecker: TfrmSpellChecker;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{$R *.lfm}
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.FormCreate(Sender: TObject);
|
||||||
|
var S:string;
|
||||||
|
begin
|
||||||
|
ASpellChecker := TJvSpellChecker.Create(self);
|
||||||
|
// Dictionaries are plain text files, one word per row, preferably sorted.
|
||||||
|
// If you don't load a dictionary, all words are misspelled and you won't get any suggestions
|
||||||
|
S := ExtractFilePath(Application.ExeName) + 'english.dic';
|
||||||
|
if not FileExists(S) then
|
||||||
|
S := '..\Dict\english.dic';
|
||||||
|
if FileExists(S) then
|
||||||
|
ASpellChecker.Dictionary := S
|
||||||
|
else
|
||||||
|
ShowMessage('Dictionary file not found: make sure you have an english.dic file in the exe folder!');
|
||||||
|
// ASpellChecker.UserDictionary.LoadFromFile(Application.ExeName + 'custom.dic'); // you need to create this
|
||||||
|
// set up a custom ignore filter:
|
||||||
|
ASpellChecker.OnCanIgnore := @DoCanIgnore;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.DoReplaceText(Sender: TObject; StartIndex, ALength: integer; const NewText: string);
|
||||||
|
begin
|
||||||
|
// this events calls back to the main form where the content of the rich edit is updated
|
||||||
|
if Assigned(FOnReplaceText) then
|
||||||
|
FOnReplaceText(self, StartIndex, ALength, NewText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TfrmSpellChecker.GetSpellText: string;
|
||||||
|
begin
|
||||||
|
Result := ASpellChecker.Text;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.SetSpellText(const Value: string);
|
||||||
|
begin
|
||||||
|
ASpellChecker.Text := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.CheckSuggestions;
|
||||||
|
begin
|
||||||
|
if lbSuggestions.Items.Count = 0 then
|
||||||
|
begin
|
||||||
|
lblNoSuggestions.Parent := lbSuggestions;
|
||||||
|
lblNoSuggestions.Top := 4;
|
||||||
|
lblNoSuggestions.Left := (lbSuggestions.ClientWidth - lblNoSuggestions.Width) div 2;
|
||||||
|
lblNoSuggestions.Visible := true;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
lblNoSuggestions.Visible := false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TfrmSpellChecker.GetNextWord: boolean;
|
||||||
|
begin
|
||||||
|
// scan for the next misspelled word. Returns false if no more misspelled words are found
|
||||||
|
Result := false;
|
||||||
|
while ASpellChecker.SpellChecker.Next(FStartIndex, FLength) do
|
||||||
|
begin
|
||||||
|
edBadWord.Text := '';
|
||||||
|
edNewWord.Text := '';
|
||||||
|
Result := FLength > 0;
|
||||||
|
if Result then
|
||||||
|
begin
|
||||||
|
edBadWord.Text := Copy(ASpellChecker.Text, FStartIndex, FLength);
|
||||||
|
lbSuggestions.Items := ASpellChecker.SpellChecker.Suggestions;
|
||||||
|
if lbSuggestions.Items.Count > 0 then
|
||||||
|
begin
|
||||||
|
edNewWord.Text := lbSuggestions.Items[0];
|
||||||
|
lbSuggestions.ItemIndex := 0;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
edNewWord.Text := edBadWord.Text;
|
||||||
|
edNewWord.SetFocus;
|
||||||
|
end;
|
||||||
|
CheckSuggestions;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.FormShow(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if GetNextWord then
|
||||||
|
DoSelectText(self, FStartIndex, FLength)
|
||||||
|
else
|
||||||
|
CloseAndReport(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.DoSelectText(Sender: TObject; StartIndex,
|
||||||
|
ALength: integer);
|
||||||
|
begin
|
||||||
|
// this events calls back to the main form where the selection in the rich edit is updated
|
||||||
|
if Assigned(FOnSelectText) then FOnSelectText(self, StartIndex, ALength);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.DoCanIgnore(Sender: TObject; const Value: string;
|
||||||
|
var CanIgnore: boolean);
|
||||||
|
var
|
||||||
|
i: integer;
|
||||||
|
begin
|
||||||
|
// custom event to manage some of the options in the dialog
|
||||||
|
|
||||||
|
// always ignore words shorter than four letter
|
||||||
|
if Length(Value) < 4 then
|
||||||
|
begin
|
||||||
|
CanIgnore := true;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// make some additional checks on the current word to determine if we need to spellcheck it
|
||||||
|
if chkUpperCase.Checked and (AnsiUpperCase(Value) = Value) then // ignore all UPPERCASE words
|
||||||
|
begin
|
||||||
|
CanIgnore := true;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if chkNumber.Checked then // ignore words that contains numbers
|
||||||
|
for i := 1 to Length(Value) do
|
||||||
|
if {$IFDEF RTL200_UP}CharInSet(Value[i], ['0'..'9', '#', '%']){$ELSE}(Value[i] in ['0'..'9', '#', '%']){$ENDIF RTL200_UP} then
|
||||||
|
begin
|
||||||
|
CanIgnore := true;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
if chkURL.Checked then // ignore URL's and file paths (this code is in no way 100% effective...)
|
||||||
|
for i := 1 to Length(Value) do
|
||||||
|
if {$IFDEF RTL200_UP}CharInSet(Value[i], [':', '/', '\']){$ELSE}(Value[i] in [':', '/', '\']){$ENDIF RTL200_UP} then
|
||||||
|
begin
|
||||||
|
CanIgnore := true;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
if chkHTML.Checked then // ignore HTML tags (this code is in no way 100% effective...)
|
||||||
|
CanIgnore := (Length(Value) < 2) or ((Value[1] = '<') or (Value[Length(Value)] = '>')) or
|
||||||
|
((Value[1] = '&') and (Value[Length(Value)] = ';'));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.CloseAndReport(ReportSuccess: boolean);
|
||||||
|
var
|
||||||
|
S: string;
|
||||||
|
begin
|
||||||
|
if ReportSuccess then
|
||||||
|
S := 'Spell check completed!'
|
||||||
|
else
|
||||||
|
S := 'There is nothing to spell check';
|
||||||
|
ShowMessage(S);
|
||||||
|
// delay since we might have been called from the OnShow event (can't close in OnShow)
|
||||||
|
PostMessage(Handle, WM_CLOSE, 0, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.acIgnoreExecute(Sender: TObject);
|
||||||
|
begin
|
||||||
|
// ignore = skip to next word but don't remember the word we just saw
|
||||||
|
if GetNextWord then
|
||||||
|
DoSelectText(self, FStartIndex, FLength)
|
||||||
|
else
|
||||||
|
CloseAndReport(true)
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.acIgnoreAllExecute(Sender: TObject);
|
||||||
|
begin
|
||||||
|
// ignore all = add to ignore list so it will be skipped in the future as well
|
||||||
|
ASpellChecker.SpellChecker.Ignores.Add(AnsiLowerCase(edBadWord.Text));
|
||||||
|
if GetNextWord then
|
||||||
|
DoSelectText(self, FStartIndex, FLength)
|
||||||
|
else
|
||||||
|
CloseAndReport(true);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.acChangeExecute(Sender: TObject);
|
||||||
|
begin
|
||||||
|
// replace the current selection with the word in the edit
|
||||||
|
DoReplaceText(self, FStartIndex, FLength, edNewWord.Text);
|
||||||
|
if GetNextWord then
|
||||||
|
DoSelectText(self, FStartIndex, FLength)
|
||||||
|
else
|
||||||
|
CloseAndReport(true)
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.acAddExecute(Sender: TObject);
|
||||||
|
begin
|
||||||
|
// Add the misspelled word to the user dictionary. To persist, you must add code to call
|
||||||
|
// UserDictionary.SaveToFile() at close down as well as UserDictionary.LoadFromFile() at start up.
|
||||||
|
ASpellChecker.SpellChecker.UserDictionary.Add(edBadWord.Text);
|
||||||
|
edNewWord.Text := edBadWord.Text;
|
||||||
|
// change the word as well
|
||||||
|
if not acChange.Execute then
|
||||||
|
begin
|
||||||
|
// move on
|
||||||
|
if GetNextWord then
|
||||||
|
DoSelectText(self, FStartIndex, FLength)
|
||||||
|
else
|
||||||
|
CloseAndReport(true);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.acCloseExecute(Sender: TObject);
|
||||||
|
begin
|
||||||
|
Close;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.alSpellUpdate(AAction: TBasicAction;
|
||||||
|
var Handled: Boolean);
|
||||||
|
begin
|
||||||
|
acIgnore.Enabled := edBadWord.Text <> '';
|
||||||
|
acIgnoreAll.Enabled := acIgnore.Enabled;
|
||||||
|
acChange.Enabled := not AnsiSameText(edBadWord.Text, edNewWord.Text);
|
||||||
|
acAdd.Enabled := (edBadWord.Text <> '') and (ASpellChecker.UserDictionary.IndexOf(edBadWord.Text) < 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmSpellChecker.lbSuggestionsClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
with lbSuggestions do
|
||||||
|
if ItemIndex > -1 then
|
||||||
|
edNewWord.Text := Items[ItemIndex];
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
93
components/jvcllaz/examples/JvSpellChecker/MainFrm.lfm
Normal file
93
components/jvcllaz/examples/JvSpellChecker/MainFrm.lfm
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
object frmMain: TfrmMain
|
||||||
|
Left = 353
|
||||||
|
Height = 398
|
||||||
|
Top = 156
|
||||||
|
Width = 535
|
||||||
|
Caption = 'JvSpellChecker Demo'
|
||||||
|
ClientHeight = 378
|
||||||
|
ClientWidth = 535
|
||||||
|
Color = clBtnFace
|
||||||
|
Constraints.MinHeight = 200
|
||||||
|
Constraints.MinWidth = 320
|
||||||
|
DefaultMonitor = dmDesktop
|
||||||
|
Font.Color = clWindowText
|
||||||
|
Font.Height = -11
|
||||||
|
Font.Name = 'MS Shell Dlg 2'
|
||||||
|
Menu = mmMain
|
||||||
|
Position = poScreenCenter
|
||||||
|
LCLVersion = '1.9.0.0'
|
||||||
|
Scaled = False
|
||||||
|
object reText: TMemo
|
||||||
|
Left = 0
|
||||||
|
Height = 355
|
||||||
|
Top = 0
|
||||||
|
Width = 535
|
||||||
|
Align = alClient
|
||||||
|
Font.Color = clWindowText
|
||||||
|
Font.Height = -13
|
||||||
|
Font.Name = 'MS Shell Dlg 2'
|
||||||
|
HideSelection = False
|
||||||
|
ParentFont = False
|
||||||
|
ScrollBars = ssBoth
|
||||||
|
TabOrder = 0
|
||||||
|
WantTabs = True
|
||||||
|
WordWrap = False
|
||||||
|
end
|
||||||
|
object StatusBar1: TStatusBar
|
||||||
|
Left = 0
|
||||||
|
Height = 23
|
||||||
|
Top = 355
|
||||||
|
Width = 535
|
||||||
|
Panels = <
|
||||||
|
item
|
||||||
|
Width = 50
|
||||||
|
end>
|
||||||
|
SimplePanel = False
|
||||||
|
end
|
||||||
|
object mmMain: TMainMenu
|
||||||
|
left = 120
|
||||||
|
top = 64
|
||||||
|
object File1: TMenuItem
|
||||||
|
Caption = 'File'
|
||||||
|
object Open1: TMenuItem
|
||||||
|
Caption = 'Open...'
|
||||||
|
ShortCut = 16463
|
||||||
|
OnClick = Open1Click
|
||||||
|
end
|
||||||
|
object N1: TMenuItem
|
||||||
|
Caption = '-'
|
||||||
|
end
|
||||||
|
object Close1: TMenuItem
|
||||||
|
Caption = 'Close'
|
||||||
|
ShortCut = 32883
|
||||||
|
OnClick = Close1Click
|
||||||
|
end
|
||||||
|
end
|
||||||
|
object Edit1: TMenuItem
|
||||||
|
Caption = 'Edit'
|
||||||
|
object SpellCheck1: TMenuItem
|
||||||
|
Caption = 'Spell Check...'
|
||||||
|
ShortCut = 118
|
||||||
|
OnClick = SpellCheck1Click
|
||||||
|
end
|
||||||
|
object N2: TMenuItem
|
||||||
|
Caption = '-'
|
||||||
|
end
|
||||||
|
object Saveasimage1: TMenuItem
|
||||||
|
Caption = 'Save as image...'
|
||||||
|
OnClick = Saveasimage1Click
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
object OpenDialog1: TOpenDialog
|
||||||
|
Filter = 'Text files|*.txt|RTF files|*.rtf|All files|*.*'
|
||||||
|
left = 168
|
||||||
|
top = 160
|
||||||
|
end
|
||||||
|
object SaveDialog1: TSaveDialog
|
||||||
|
FileName = 'new.bmp'
|
||||||
|
Options = [ofOverwritePrompt, ofHideReadOnly, ofEnableSizing]
|
||||||
|
left = 272
|
||||||
|
top = 192
|
||||||
|
end
|
||||||
|
end
|
142
components/jvcllaz/examples/JvSpellChecker/MainFrm.pas
Normal file
142
components/jvcllaz/examples/JvSpellChecker/MainFrm.pas
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
{******************************************************************
|
||||||
|
|
||||||
|
JEDI-VCL Demo
|
||||||
|
|
||||||
|
Copyright (C) 2002 Project JEDI
|
||||||
|
|
||||||
|
Original author:
|
||||||
|
|
||||||
|
Contributor(s):
|
||||||
|
|
||||||
|
You may retrieve the latest version of this file at the JEDI-JVCL
|
||||||
|
home page, located at http://jvcl.delphi-jedi.org
|
||||||
|
|
||||||
|
The contents of this file are used with permission, subject to
|
||||||
|
the Mozilla Public License Version 1.1 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may
|
||||||
|
obtain a copy of the License at
|
||||||
|
http://www.mozilla.org/MPL/MPL-1_1Final.html
|
||||||
|
|
||||||
|
Software distributed under the License is distributed on an
|
||||||
|
"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||||
|
implied. See the License for the specific language governing
|
||||||
|
rights and limitations under the License.
|
||||||
|
|
||||||
|
******************************************************************}
|
||||||
|
|
||||||
|
unit MainFrm;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
|
||||||
|
Dialogs, StdCtrls, Menus, ComCtrls, JvComponent;
|
||||||
|
|
||||||
|
type
|
||||||
|
TfrmMain = class(TForm)
|
||||||
|
reText: TMemo;
|
||||||
|
mmMain: TMainMenu;
|
||||||
|
File1: TMenuItem;
|
||||||
|
Edit1: TMenuItem;
|
||||||
|
SpellCheck1: TMenuItem;
|
||||||
|
Open1: TMenuItem;
|
||||||
|
N1: TMenuItem;
|
||||||
|
Close1: TMenuItem;
|
||||||
|
StatusBar1: TStatusBar;
|
||||||
|
OpenDialog1: TOpenDialog;
|
||||||
|
N2: TMenuItem;
|
||||||
|
Saveasimage1: TMenuItem;
|
||||||
|
SaveDialog1: TSaveDialog;
|
||||||
|
procedure Open1Click(Sender: TObject);
|
||||||
|
procedure Close1Click(Sender: TObject);
|
||||||
|
procedure SpellCheck1Click(Sender: TObject);
|
||||||
|
procedure Saveasimage1Click(Sender: TObject);
|
||||||
|
private
|
||||||
|
{ Private declarations }
|
||||||
|
// FOffset is used to adjust the insert point in the rich edit when the replaced word
|
||||||
|
// has a different length than the original word
|
||||||
|
FOffset: integer;
|
||||||
|
// called when a word should be replaced
|
||||||
|
procedure DoReplaceText(Sender: TObject; StartIndex, ALength: integer; const NewText: string);
|
||||||
|
// called when a word needs to be highlighted. NB: set HideSelection := false or you
|
||||||
|
// won't see the selection in the edit!
|
||||||
|
procedure DoSelectText(Sender: TObject; StartIndex, ALength: integer);
|
||||||
|
public
|
||||||
|
{ Public declarations }
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
frmMain: TfrmMain;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
uses
|
||||||
|
JvSpellCheckerForm;
|
||||||
|
|
||||||
|
{$R *.lfm}
|
||||||
|
|
||||||
|
procedure TfrmMain.Open1Click(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if OpenDialog1.Execute then
|
||||||
|
begin
|
||||||
|
reText.Lines.LoadFromFile(OpenDialog1.Filename);
|
||||||
|
StatusBar1.Panels[0].Text := ' ' + OpenDialog1.Filename;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmMain.Close1Click(Sender: TObject);
|
||||||
|
begin
|
||||||
|
Close;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmMain.SpellCheck1Click(Sender: TObject);
|
||||||
|
begin
|
||||||
|
// set up the spell-checker form
|
||||||
|
if not frmSpellChecker.Visible then
|
||||||
|
begin
|
||||||
|
// StartIndex is 1-based, SelStart is 0-based, so we set intial FOffset to 1
|
||||||
|
FOffset := 1;
|
||||||
|
// the original text to spell check
|
||||||
|
frmSpellChecker.SpellText := reText.Lines.Text;
|
||||||
|
// event handler for when a word is to be replaced
|
||||||
|
frmSpellChecker.OnReplaceText := @DoReplaceText;
|
||||||
|
// event handler for when a word needs to be selected
|
||||||
|
frmSpellChecker.OnSelectText := @DoSelectText;
|
||||||
|
frmSpellChecker.Show; // ShowModal also works
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmMain.DoSelectText(Sender: TObject; StartIndex, ALength: integer);
|
||||||
|
begin
|
||||||
|
// just select the text in the rich edit so the user can see were he is
|
||||||
|
reText.SelStart := StartIndex - FOffset;
|
||||||
|
reText.SelLength := ALength;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmMain.DoReplaceText(Sender: TObject; StartIndex,
|
||||||
|
ALength: integer; const NewText: string);
|
||||||
|
begin
|
||||||
|
reText.SelStart := StartIndex - FOffset;
|
||||||
|
reText.SelLength := ALength;
|
||||||
|
// replace the selected text
|
||||||
|
reText.SelText := NewText;
|
||||||
|
// adjust offset for next round
|
||||||
|
Inc(FOffset, ALength - Length(NewText));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TfrmMain.Saveasimage1Click(Sender: TObject);
|
||||||
|
var Picture:TPicture;
|
||||||
|
begin
|
||||||
|
if SaveDialog1.Execute then
|
||||||
|
begin
|
||||||
|
Picture := TPicture.Create;
|
||||||
|
try
|
||||||
|
// reText.SaveToImage(Picture);
|
||||||
|
// Picture.SaveToFile(SaveDialog1.Filename);
|
||||||
|
finally
|
||||||
|
Picture.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
end.
|
@ -4,9 +4,6 @@
|
|||||||
<Version Value="11"/>
|
<Version Value="11"/>
|
||||||
<PathDelim Value="\"/>
|
<PathDelim Value="\"/>
|
||||||
<General>
|
<General>
|
||||||
<Flags>
|
|
||||||
<UseDefaultCompilerOptions Value="True"/>
|
|
||||||
</Flags>
|
|
||||||
<SessionStorage Value="InProjectDir"/>
|
<SessionStorage Value="InProjectDir"/>
|
||||||
<MainUnit Value="0"/>
|
<MainUnit Value="0"/>
|
||||||
<Title Value="SimpleTLTest1"/>
|
<Title Value="SimpleTLTest1"/>
|
||||||
@ -64,7 +61,7 @@
|
|||||||
<Version Value="11"/>
|
<Version Value="11"/>
|
||||||
<PathDelim Value="\"/>
|
<PathDelim Value="\"/>
|
||||||
<Target>
|
<Target>
|
||||||
<Filename Value="SimpleTLTest1"/>
|
<Filename Value="..\..\bin\SimpleTLTest1"/>
|
||||||
</Target>
|
</Target>
|
||||||
<SearchPaths>
|
<SearchPaths>
|
||||||
<IncludeFiles Value="$(ProjOutDir)"/>
|
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<CONFIG>
|
<CONFIG>
|
||||||
<ProjectGroup FileVersion="1">
|
<ProjectGroup FileVersion="1">
|
||||||
<Targets Count="16">
|
<Targets Count="19">
|
||||||
<Target0 FileName="JvCoreLazR.lpk"/>
|
<Target0 FileName="JvCoreLazR.lpk"/>
|
||||||
<Target1 FileName="JvCoreLazD.lpk"/>
|
<Target1 FileName="JvCoreLazD.lpk"/>
|
||||||
<Target2 FileName="JvCtrlsLazR.lpk"/>
|
<Target2 FileName="JvCtrlsLazR.lpk"/>
|
||||||
@ -18,6 +18,9 @@
|
|||||||
<Target13 FileName="JvXPCtrlsLazD.lpk"/>
|
<Target13 FileName="JvXPCtrlsLazD.lpk"/>
|
||||||
<Target14 FileName="jvruntimedesignlazr.lpk"/>
|
<Target14 FileName="jvruntimedesignlazr.lpk"/>
|
||||||
<Target15 FileName="jvruntimedesignlazd.lpk"/>
|
<Target15 FileName="jvruntimedesignlazd.lpk"/>
|
||||||
|
<Target16 FileName="jvcustomlazr.lpk"/>
|
||||||
|
<Target17 FileName="jvcustomlazd.lpk"/>
|
||||||
|
<Target18 FileName="jvcmpr.lpk"/>
|
||||||
</Targets>
|
</Targets>
|
||||||
</ProjectGroup>
|
</ProjectGroup>
|
||||||
</CONFIG>
|
</CONFIG>
|
||||||
|
49
components/jvcllaz/packages/jvcmpr.lpk
Normal file
49
components/jvcllaz/packages/jvcmpr.lpk
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CONFIG>
|
||||||
|
<Package Version="4">
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<Name Value="JvCmpR"/>
|
||||||
|
<Author Value="Various authors - see header of each unit for original author."/>
|
||||||
|
<CompilerOptions>
|
||||||
|
<Version Value="11"/>
|
||||||
|
<PathDelim Value="\"/>
|
||||||
|
<SearchPaths>
|
||||||
|
<IncludeFiles Value="..\run\JvCmp"/>
|
||||||
|
<OtherUnitFiles Value="..\run\JvCmp"/>
|
||||||
|
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
|
||||||
|
</SearchPaths>
|
||||||
|
</CompilerOptions>
|
||||||
|
<Description Value="Custom controls of the JVCL library (https://sourceforge.net/projects/jvcl/) (runtime code):
|
||||||
|
- Spellchecker component"/>
|
||||||
|
<License Value="The JVCL is released in accordance with the MPL 1.1 license. To get your own copy or read it, go to http://www.mozilla.org/MPL/MPL-1.1.html. "/>
|
||||||
|
<Version Major="1" Release="4"/>
|
||||||
|
<Files Count="3">
|
||||||
|
<Item1>
|
||||||
|
<Filename Value="..\run\JvCmp\JvSpellChecker.pas"/>
|
||||||
|
<UnitName Value="JvSpellChecker"/>
|
||||||
|
</Item1>
|
||||||
|
<Item2>
|
||||||
|
<Filename Value="..\run\JvCmp\JvSpellerForm.pas"/>
|
||||||
|
<UnitName Value="JvSpellerForm"/>
|
||||||
|
</Item2>
|
||||||
|
<Item3>
|
||||||
|
<Filename Value="..\run\JvCmp\JvSpellIntf.pas"/>
|
||||||
|
<UnitName Value="JvSpellIntf"/>
|
||||||
|
</Item3>
|
||||||
|
</Files>
|
||||||
|
<RequiredPkgs Count="2">
|
||||||
|
<Item1>
|
||||||
|
<PackageName Value="JvCoreLazR"/>
|
||||||
|
</Item1>
|
||||||
|
<Item2>
|
||||||
|
<PackageName Value="FCL"/>
|
||||||
|
</Item2>
|
||||||
|
</RequiredPkgs>
|
||||||
|
<UsageOptions>
|
||||||
|
<UnitPath Value="$(PkgOutDir)"/>
|
||||||
|
</UsageOptions>
|
||||||
|
<PublishOptions>
|
||||||
|
<Version Value="2"/>
|
||||||
|
</PublishOptions>
|
||||||
|
</Package>
|
||||||
|
</CONFIG>
|
633
components/jvcllaz/run/JvCmp/JvSpellChecker.pas
Normal file
633
components/jvcllaz/run/JvCmp/JvSpellChecker.pas
Normal file
@ -0,0 +1,633 @@
|
|||||||
|
{-----------------------------------------------------------------------------
|
||||||
|
The contents of this file are subject to the Mozilla Public License
|
||||||
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
||||||
|
|
||||||
|
Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
||||||
|
the specific language governing rights and limitations under the License.
|
||||||
|
|
||||||
|
The Original Code is: JvSpellChecker.PAS, released on 2003-08-19.
|
||||||
|
|
||||||
|
The Initial Developer of the Original Code is Peter Th�rnqvist [peter3 at sourceforge dot net]
|
||||||
|
Portions created by Peter Th�rnqvist are Copyright (C) 2003 Peter Th�rnqvist.
|
||||||
|
All Rights Reserved.
|
||||||
|
|
||||||
|
Contributor(s):
|
||||||
|
|
||||||
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
||||||
|
located at http://jvcl.delphi-jedi.org
|
||||||
|
|
||||||
|
Known Issues:
|
||||||
|
- Items in the UserDictionary are not added to the internal WordTable/SoundexTable when
|
||||||
|
you add a new item (i.e call UserDictionary.Add). This is mostly for performance.
|
||||||
|
UserDictionary entries are loaded into the dictionary table in BuildTables, so to get
|
||||||
|
them added make sure UserDictionary is filled before setting the Dictionary property.
|
||||||
|
-----------------------------------------------------------------------------}
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
unit JvSpellChecker;
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
//{$I jvcl.inc}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
SysUtils, Classes,
|
||||||
|
Windows, Controls, Messages,
|
||||||
|
JvSpellIntf; //, JvComponentBase;
|
||||||
|
|
||||||
|
type
|
||||||
|
TJvSpellChecker = class(TComponent) //TJvComponent)
|
||||||
|
private
|
||||||
|
FSpellChecker: IJvSpellChecker;
|
||||||
|
procedure SetText(const Value: string);
|
||||||
|
function GetText: string;
|
||||||
|
function GetDictionary: TFileName;
|
||||||
|
function GetUserDictionary: TStrings;
|
||||||
|
procedure SetDictionary(const Value: TFileName);
|
||||||
|
procedure SetUserDictionary(const Value: TStrings);
|
||||||
|
function GetSpellChecker: IJvSpellChecker;
|
||||||
|
function GetDelimiters: TSysCharSet;
|
||||||
|
procedure SetDelimiters(const Value: TSysCharSet);
|
||||||
|
function GetIgnores: TStrings;
|
||||||
|
procedure SetIgnores(const Value: TStrings);
|
||||||
|
function GetCanIgnore: TJvSpellCheckIgnoreEvent;
|
||||||
|
procedure SetCanIgnore(const Value: TJvSpellCheckIgnoreEvent);
|
||||||
|
public
|
||||||
|
// reference to the actual spell check implementation
|
||||||
|
property SpellChecker: IJvSpellChecker read GetSpellChecker;
|
||||||
|
property Delimiters: TSysCharSet read GetDelimiters write SetDelimiters;
|
||||||
|
published
|
||||||
|
// Surface interface properties to make it a bit easier to work with this component
|
||||||
|
property Text: string read GetText write SetText;
|
||||||
|
property Dictionary: TFileName read GetDictionary write SetDictionary;
|
||||||
|
property UserDictionary: TStrings read GetUserDictionary write SetUserDictionary;
|
||||||
|
property Ignores: TStrings read GetIgnores write SetIgnores;
|
||||||
|
property OnCanIgnore: TJvSpellCheckIgnoreEvent read GetCanIgnore write SetCanIgnore;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{$IFDEF UNITVERSIONING}
|
||||||
|
const
|
||||||
|
UnitVersioning: TUnitVersionInfo = (
|
||||||
|
RCSfile: '$URL$';
|
||||||
|
Revision: '$Revision$';
|
||||||
|
Date: '$Date$';
|
||||||
|
LogPath: 'JVCL\run'
|
||||||
|
);
|
||||||
|
{$ENDIF UNITVERSIONING}
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
// JclStrings, // StrAddRef, StrDecRef
|
||||||
|
JvTypes, JvResources;
|
||||||
|
|
||||||
|
// NOTE: hash table and soundex lookup code originally from Julian Bucknall's
|
||||||
|
// "Algorithms Alfresco" column in The Delphi Magazine, Issue 52, December 1999
|
||||||
|
// Used with permission
|
||||||
|
const
|
||||||
|
WordTableSize = 10007; {a prime}
|
||||||
|
SoundexTableSize = 26 * 7 * 7 * 7; {the exact number of Soundexes}
|
||||||
|
cDelimiters: TSysCharSet = [#0..#32, '.', ',', '<', '>', '=', '!', '?', ':', ';', '"', '''', '(', ')', '[', ']', '{', '}', '+', '|'];
|
||||||
|
|
||||||
|
type
|
||||||
|
TSoundex = string[4];
|
||||||
|
// default implementation of the IJvSpellChecker interface. To provide a new implementation,
|
||||||
|
// assign a function to the CreateSpellChecker function variable in JvSpellIntf that returns an
|
||||||
|
// instance of your implementation. For more info, see InternalSpellChecker in this unit.
|
||||||
|
TJvDefaultSpellChecker = class(TInterfacedObject, IJvSpellChecker)
|
||||||
|
private
|
||||||
|
FText: string;
|
||||||
|
FCurrentWord: string;
|
||||||
|
FPosition: Integer;
|
||||||
|
FDictionary: string;
|
||||||
|
FSuggestions: TStringList;
|
||||||
|
FUserDictionary: TStringList;
|
||||||
|
FIgnores: TStringList;
|
||||||
|
FWordTable: TList;
|
||||||
|
FSoundexTable: TList;
|
||||||
|
FDelimiters: TSysCharSet;
|
||||||
|
FOnCanIgnore: TJvSpellCheckIgnoreEvent;
|
||||||
|
{ IJvSpellChecker }
|
||||||
|
procedure SetDictionary(const Value: string);
|
||||||
|
function GetDictionary: string;
|
||||||
|
function GetUserDictionary: TStrings;
|
||||||
|
procedure SetUserDictionary(const Value: TStrings);
|
||||||
|
function GetSuggestions: TStrings;
|
||||||
|
function GetText: string;
|
||||||
|
procedure SetText(const Value: string);
|
||||||
|
function GetIgnores: TStrings;
|
||||||
|
procedure SetIgnores(const Value: TStrings);
|
||||||
|
function GetDelimiters: TSysCharSet;
|
||||||
|
procedure SetDelimiters(const Value: TSysCharSet);
|
||||||
|
function GetCanIgnore: TJvSpellCheckIgnoreEvent;
|
||||||
|
procedure SetCanIgnore(const Value: TJvSpellCheckIgnoreEvent);
|
||||||
|
protected
|
||||||
|
procedure BuildTables; virtual;
|
||||||
|
procedure ClearTables; virtual;
|
||||||
|
function GetCurrentWord: string; virtual;
|
||||||
|
procedure GetWordSuggestions(const Value: string; AStrings: TStrings); virtual;
|
||||||
|
procedure AddSoundex(ASoundex: TSoundex; const Value: string); virtual;
|
||||||
|
procedure AddWord(const Value: string); virtual;
|
||||||
|
function WordExists(const Value: string): Boolean; virtual;
|
||||||
|
function CanIgnore(const Value: string): Boolean; virtual;
|
||||||
|
{ IJvSpellChecker }
|
||||||
|
function Next(out StartIndex, WordLength: Integer): WordBool; virtual;
|
||||||
|
procedure Seek(Position: Integer); virtual;
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
property Delimiters: TSysCharSet read GetDelimiters write SetDelimiters;
|
||||||
|
property Suggestions: TStrings read GetSuggestions;
|
||||||
|
property Dictionary: string read GetDictionary write SetDictionary;
|
||||||
|
property UserDictionary: TStrings read GetUserDictionary write SetUserDictionary;
|
||||||
|
property Text: string read GetText write SetText;
|
||||||
|
property Ignores: TStrings read GetIgnores write SetIgnores;
|
||||||
|
property OnCanIgnore: TJvSpellCheckIgnoreEvent read GetCanIgnore write SetCanIgnore;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function InternalCreateSpellChecker: IJvSpellChecker;
|
||||||
|
begin
|
||||||
|
// create our implementation of the spell checker interface
|
||||||
|
Result := TJvDefaultSpellChecker.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function Soundex(const Value: string): TSoundex;
|
||||||
|
const
|
||||||
|
Encode: array ['A'..'Z'] of AnsiChar =
|
||||||
|
('0', '1', '2', '3', '0', '1', '2', '/', '0', '2', '2',
|
||||||
|
'4', '5', '5', '0', '1', '2', '6', '2', '3', '0', '1',
|
||||||
|
'/', '2', '0', '2');
|
||||||
|
var
|
||||||
|
Ch: Char;
|
||||||
|
Code, OldCode: AnsiChar;
|
||||||
|
SxInx: Integer;
|
||||||
|
I: Integer;
|
||||||
|
UpperValue: string;
|
||||||
|
begin
|
||||||
|
Result := 'A000';
|
||||||
|
if Value = '' then
|
||||||
|
Exit;
|
||||||
|
// raise Exception.Create('Soundex: input string is empty');
|
||||||
|
UpperValue := AnsiUpperCase(Value);
|
||||||
|
|
||||||
|
Ch := UpperValue[1];
|
||||||
|
if (Ch < 'A') or (Ch > 'Z') then
|
||||||
|
Ch := 'A';
|
||||||
|
// raise Exception.Create('Soundex: unknown character in input string');
|
||||||
|
Result[1] := AnsiChar(Ch);
|
||||||
|
Code := Encode[Ch];
|
||||||
|
OldCode := Code;
|
||||||
|
SxInx := 2;
|
||||||
|
for I := 2 to Length(UpperValue) do
|
||||||
|
begin
|
||||||
|
if (Code <> '/') then
|
||||||
|
OldCode := Code;
|
||||||
|
Ch := UpperValue[I];
|
||||||
|
if not ('A' <= Ch) and (Ch <= 'Z') then
|
||||||
|
Code := '0'
|
||||||
|
else
|
||||||
|
Code := Encode[Ch];
|
||||||
|
if (Code <> OldCode) and (Code > '0') then
|
||||||
|
begin
|
||||||
|
Result[SxInx] := Code;
|
||||||
|
Inc(SxInx);
|
||||||
|
if SxInx > 4 then
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ELFHash(const S: string): Integer;
|
||||||
|
var
|
||||||
|
G, I: Integer;
|
||||||
|
begin
|
||||||
|
Result := 0;
|
||||||
|
for I := 1 to Length(S) do
|
||||||
|
begin
|
||||||
|
Result := (Result shl 4) + Ord(S[I]);
|
||||||
|
G := Result and Longint($F0000000);
|
||||||
|
if G <> 0 then
|
||||||
|
Result := Result xor (G shr 24);
|
||||||
|
Result := Result and (not G);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function SoundexHash(const S: TSoundex): Integer;
|
||||||
|
begin
|
||||||
|
Result :=
|
||||||
|
((Ord(S[1]) - Ord('A')) * 343) +
|
||||||
|
((Ord(S[2]) - Ord('0')) * 49) +
|
||||||
|
((Ord(S[3]) - Ord('0')) * 7) +
|
||||||
|
(Ord(S[4]) - Ord('0'));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function GetNextWord(var S: PChar; out Word: string; Delimiters: TSysCharSet): Boolean;
|
||||||
|
var
|
||||||
|
Start: PChar;
|
||||||
|
begin
|
||||||
|
Word := '';
|
||||||
|
Result := S = nil;
|
||||||
|
if Result then
|
||||||
|
Exit;
|
||||||
|
Start := nil;
|
||||||
|
while True do
|
||||||
|
begin
|
||||||
|
if S^ = #0 then
|
||||||
|
begin
|
||||||
|
Word := Start;
|
||||||
|
Result := Start <> nil;
|
||||||
|
Exit;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if ((S^ <= #255) and (AnsiChar(S^) in Delimiters)) then
|
||||||
|
begin
|
||||||
|
if Start <> nil then
|
||||||
|
begin
|
||||||
|
SetString(Word, Start, S - Start);
|
||||||
|
Exit;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
while ((S^ <= #255) and (AnsiChar(S^) in Delimiters)) and (S^ <> #0) do
|
||||||
|
Inc(S);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
if Start = nil then
|
||||||
|
Start := S;
|
||||||
|
Inc(S);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//=== { TJvDefaultSpellChecker } =============================================
|
||||||
|
|
||||||
|
constructor TJvDefaultSpellChecker.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FDelimiters := cDelimiters;
|
||||||
|
FSuggestions := TStringList.Create;
|
||||||
|
FUserDictionary := TStringList.Create;
|
||||||
|
FUserDictionary.Sorted := True;
|
||||||
|
FIgnores := TStringList.Create;
|
||||||
|
FIgnores.Sorted := True;
|
||||||
|
|
||||||
|
FWordTable := TList.Create;
|
||||||
|
FWordTable.Count := WordTableSize;
|
||||||
|
FSoundexTable := TList.Create;
|
||||||
|
FSoundexTable.Count := SoundexTableSize;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TJvDefaultSpellChecker.Destroy;
|
||||||
|
begin
|
||||||
|
ClearTables;
|
||||||
|
FreeAndNil(FSuggestions);
|
||||||
|
FreeAndNil(FUserDictionary);
|
||||||
|
FreeAndNil(FWordTable);
|
||||||
|
FreeAndNil(FSoundexTable);
|
||||||
|
FreeAndNil(FIgnores);
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.AddSoundex(ASoundex: TSoundex; const Value: string);
|
||||||
|
var
|
||||||
|
Hash: Integer;
|
||||||
|
begin
|
||||||
|
Hash := SoundexHash(ASoundex) mod SoundexTableSize;
|
||||||
|
if FSoundexTable[Hash] = nil then
|
||||||
|
FSoundexTable[Hash] := TStringList.Create;
|
||||||
|
TStringList(FSoundexTable[Hash]).Add(Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.AddWord(const Value: string);
|
||||||
|
var
|
||||||
|
Hash: Integer;
|
||||||
|
begin
|
||||||
|
Hash := ELFHash(Value) mod WordTableSize;
|
||||||
|
if FWordTable[Hash] = nil then
|
||||||
|
FWordTable[Hash] := TStringList.Create;
|
||||||
|
TStringList(FWordTable[Hash]).Add(Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.BuildTables;
|
||||||
|
var
|
||||||
|
AFile: TextFile;
|
||||||
|
Value: string;
|
||||||
|
LastValue: string;
|
||||||
|
SoundexVal: TSoundex;
|
||||||
|
I: Integer;
|
||||||
|
N: Integer;
|
||||||
|
begin
|
||||||
|
ClearTables;
|
||||||
|
if FileExists(Dictionary) then
|
||||||
|
begin
|
||||||
|
System.Assign(AFile, Dictionary);
|
||||||
|
System.Reset(AFile);
|
||||||
|
try
|
||||||
|
repeat
|
||||||
|
ReadLn(AFile, Value);
|
||||||
|
if Value <> '' then
|
||||||
|
begin
|
||||||
|
// (rom) simple compession for dictionary
|
||||||
|
N := Ord(Value[1]) - Ord('0');
|
||||||
|
Value := Copy(Value, 2, Length(Value) - 1);
|
||||||
|
if N > 0 then
|
||||||
|
Value := Copy(LastValue, 1, N) + Value;
|
||||||
|
LastValue := Value;
|
||||||
|
|
||||||
|
Value := AnsiLowerCase(Value);
|
||||||
|
AddWord(Value);
|
||||||
|
SoundexVal := Soundex(Value);
|
||||||
|
AddSoundex(SoundexVal, Value);
|
||||||
|
end;
|
||||||
|
until Eof(AFile);
|
||||||
|
finally
|
||||||
|
System.Close(AFile);
|
||||||
|
end;
|
||||||
|
for I := 0 to UserDictionary.Count - 1 do
|
||||||
|
if UserDictionary[I] <> '' then
|
||||||
|
begin
|
||||||
|
Value := AnsiLowerCase(UserDictionary[I]);
|
||||||
|
AddWord(Value);
|
||||||
|
SoundexVal := Soundex(Value);
|
||||||
|
AddSoundex(SoundexVal, Value);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.ClearTables;
|
||||||
|
var
|
||||||
|
I: Integer;
|
||||||
|
begin
|
||||||
|
if FSoundexTable <> nil then
|
||||||
|
for I := 0 to FSoundexTable.Count - 1 do
|
||||||
|
begin
|
||||||
|
TObject(FSoundexTable[I]).Free;
|
||||||
|
FSoundexTable[I] := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FWordTable <> nil then
|
||||||
|
for I := 0 to FWordTable.Count - 1 do
|
||||||
|
begin
|
||||||
|
TObject(FWordTable[I]).Free;
|
||||||
|
FWordTable[I] := nil;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.GetSuggestions: TStrings;
|
||||||
|
begin
|
||||||
|
Result := FSuggestions;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.GetText: string;
|
||||||
|
begin
|
||||||
|
Result := FText;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.GetWordSuggestions(const Value: string; AStrings: TStrings);
|
||||||
|
var
|
||||||
|
SoundexVal: TSoundex;
|
||||||
|
Hash: Integer;
|
||||||
|
begin
|
||||||
|
if AStrings <> nil then
|
||||||
|
begin
|
||||||
|
AStrings.BeginUpdate;
|
||||||
|
try
|
||||||
|
AStrings.Clear;
|
||||||
|
SoundexVal := Soundex(Value);
|
||||||
|
Hash := SoundexHash(SoundexVal) mod SoundexTableSize;
|
||||||
|
if FSoundexTable[Hash] <> nil then
|
||||||
|
AStrings.AddStrings(TStringList(FSoundexTable[Hash]));
|
||||||
|
finally
|
||||||
|
AStrings.EndUpdate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.Next(out StartIndex, WordLength: Integer): WordBool;
|
||||||
|
var
|
||||||
|
S: PChar;
|
||||||
|
begin
|
||||||
|
StartIndex := 0;
|
||||||
|
WordLength := 0;
|
||||||
|
Result := False;
|
||||||
|
if FPosition <= 0 then
|
||||||
|
FPosition := 1;
|
||||||
|
if FPosition >= Length(FText) then
|
||||||
|
Exit;
|
||||||
|
S := PChar(Text) + FPosition - 1;
|
||||||
|
if (S = nil) or (S^ = #0) or (Trim(S) = '') then
|
||||||
|
Exit;
|
||||||
|
while True do
|
||||||
|
begin
|
||||||
|
FCurrentWord := '';
|
||||||
|
GetNextWord(S, FCurrentWord, Delimiters);
|
||||||
|
WordLength := Length(FCurrentWord);
|
||||||
|
StartIndex := S - PChar(Text) - WordLength + 1;
|
||||||
|
FPosition := StartIndex + WordLength;
|
||||||
|
if (FCurrentWord <> '') and not CanIgnore(FCurrentWord) then
|
||||||
|
begin
|
||||||
|
FSuggestions.Clear;
|
||||||
|
Result := not WordExists(FCurrentWord);
|
||||||
|
if Result then
|
||||||
|
begin
|
||||||
|
GetWordSuggestions(FCurrentWord, FSuggestions);
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if (S = nil) or (S^ = #0) or (Trim(S) = '') then
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.Seek(Position: Integer);
|
||||||
|
begin
|
||||||
|
FPosition := Position;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.SetText(const Value: string);
|
||||||
|
begin
|
||||||
|
FText := Value;
|
||||||
|
FPosition := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.WordExists(const Value: string): Boolean;
|
||||||
|
var
|
||||||
|
I: Integer;
|
||||||
|
Hash: Integer;
|
||||||
|
List: TStringList;
|
||||||
|
FWord: string;
|
||||||
|
begin
|
||||||
|
FWord := AnsiLowerCase(Value);
|
||||||
|
Hash := ELFHash(FWord) mod WordTableSize;
|
||||||
|
if FWordTable[Hash] <> nil then
|
||||||
|
begin
|
||||||
|
List := TStringList(FWordTable[Hash]);
|
||||||
|
for I := 0 to List.Count - 1 do
|
||||||
|
if AnsiSameText(PChar(List[I]), FWord) then
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
// ignore or user word?
|
||||||
|
Result := (UserDictionary.IndexOf(FWord) > -1) or (Ignores.IndexOf(FWord) > -1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.GetDictionary: string;
|
||||||
|
begin
|
||||||
|
Result := FDictionary;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.GetUserDictionary: TStrings;
|
||||||
|
begin
|
||||||
|
Result := FUserDictionary;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.SetDictionary(const Value: string);
|
||||||
|
begin
|
||||||
|
if FDictionary <> Value then
|
||||||
|
begin
|
||||||
|
FDictionary := Value;
|
||||||
|
BuildTables;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.SetUserDictionary(const Value: TStrings);
|
||||||
|
begin
|
||||||
|
FUserDictionary.Assign(Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.GetIgnores: TStrings;
|
||||||
|
begin
|
||||||
|
Result := FIgnores;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.SetIgnores(const Value: TStrings);
|
||||||
|
begin
|
||||||
|
FIgnores.Assign(Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.GetDelimiters: TSysCharSet;
|
||||||
|
begin
|
||||||
|
Result := FDelimiters;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.SetDelimiters(const Value: TSysCharSet);
|
||||||
|
begin
|
||||||
|
FDelimiters := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.CanIgnore(const Value: string): Boolean;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
if Assigned(FOnCanIgnore) then
|
||||||
|
FOnCanIgnore(Self, Value, Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.GetCanIgnore: TJvSpellCheckIgnoreEvent;
|
||||||
|
begin
|
||||||
|
Result := FOnCanIgnore;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvDefaultSpellChecker.SetCanIgnore(const Value: TJvSpellCheckIgnoreEvent);
|
||||||
|
begin
|
||||||
|
FOnCanIgnore := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvDefaultSpellChecker.GetCurrentWord: string;
|
||||||
|
begin
|
||||||
|
Result := FCurrentWord;
|
||||||
|
end;
|
||||||
|
|
||||||
|
//=== { TJvSpellChecker } ====================================================
|
||||||
|
|
||||||
|
function TJvSpellChecker.GetCanIgnore: TJvSpellCheckIgnoreEvent;
|
||||||
|
begin
|
||||||
|
Result := SpellChecker.OnCanIgnore;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvSpellChecker.GetDelimiters: TSysCharSet;
|
||||||
|
begin
|
||||||
|
Result := SpellChecker.Delimiters;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvSpellChecker.GetDictionary: TFileName;
|
||||||
|
begin
|
||||||
|
Result := SpellChecker.Dictionary;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvSpellChecker.GetIgnores: TStrings;
|
||||||
|
begin
|
||||||
|
Result := SpellChecker.Ignores;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvSpellChecker.GetSpellChecker: IJvSpellChecker;
|
||||||
|
begin
|
||||||
|
if FSpellChecker = nil then
|
||||||
|
begin
|
||||||
|
if Assigned(CreateSpellChecker) then
|
||||||
|
FSpellChecker := CreateSpellChecker()
|
||||||
|
else
|
||||||
|
FSpellChecker := InternalCreateSpellChecker;
|
||||||
|
end;
|
||||||
|
Result := FSpellChecker;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvSpellChecker.GetText: string;
|
||||||
|
begin
|
||||||
|
Result := SpellChecker.GetText;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvSpellChecker.GetUserDictionary: TStrings;
|
||||||
|
begin
|
||||||
|
Result := SpellChecker.UserDictionary;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpellChecker.SetCanIgnore(const Value: TJvSpellCheckIgnoreEvent);
|
||||||
|
begin
|
||||||
|
SpellChecker.OnCanIgnore := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpellChecker.SetDelimiters(const Value: TSysCharSet);
|
||||||
|
begin
|
||||||
|
SpellChecker.Delimiters := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpellChecker.SetDictionary(const Value: TFileName);
|
||||||
|
begin
|
||||||
|
SpellChecker.Dictionary := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpellChecker.SetIgnores(const Value: TStrings);
|
||||||
|
begin
|
||||||
|
SpellChecker.Ignores := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpellChecker.SetText(const Value: string);
|
||||||
|
begin
|
||||||
|
SpellChecker.SetText(Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpellChecker.SetUserDictionary(const Value: TStrings);
|
||||||
|
begin
|
||||||
|
SpellChecker.UserDictionary := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{$IFDEF UNITVERSIONING}
|
||||||
|
initialization
|
||||||
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
||||||
|
{$ENDIF UNITVERSIONING}
|
||||||
|
|
||||||
|
{$IFDEF UNITVERSIONING}
|
||||||
|
finalization
|
||||||
|
UnregisterUnitVersion(HInstance);
|
||||||
|
{$ENDIF UNITVERSIONING}
|
||||||
|
|
||||||
|
end.
|
124
components/jvcllaz/run/JvCmp/JvSpellIntf.pas
Normal file
124
components/jvcllaz/run/JvCmp/JvSpellIntf.pas
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
{-----------------------------------------------------------------------------
|
||||||
|
The contents of this file are subject to the Mozilla Public License
|
||||||
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
||||||
|
|
||||||
|
Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
||||||
|
the specific language governing rights and limitations under the License.
|
||||||
|
|
||||||
|
The Original Code is: JvSpellIntf.PAS, released on 2003-08-19.
|
||||||
|
|
||||||
|
The Initial Developer of the Original Code is Peter Thörnqvist [peter3 at sourceforge dot net]
|
||||||
|
Portions created by Peter Thörnqvist are Copyright (C) 2003 Peter Thörnqvist.
|
||||||
|
All Rights Reserved.
|
||||||
|
|
||||||
|
Contributor(s):
|
||||||
|
|
||||||
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
||||||
|
located at http://jvcl.delphi-jedi.org
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Interface declarations for classes that want to implement a spell
|
||||||
|
checker compatible with the TJvSpellChecker component.
|
||||||
|
|
||||||
|
Known Issues:
|
||||||
|
-----------------------------------------------------------------------------}
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
unit JvSpellIntf;
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
//{$I jvcl.inc}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
SysUtils, Classes;
|
||||||
|
|
||||||
|
type
|
||||||
|
TJvSpellCheckIgnoreEvent = procedure(Sender: TObject; const Value: string;
|
||||||
|
var CanIgnore: Boolean) of object;
|
||||||
|
|
||||||
|
IJvSpellChecker = interface
|
||||||
|
['{819CE37A-E3C1-4F54-B9E1-1CFAA8AFB887}']
|
||||||
|
// GetCurrentWord returns the currently found misspelled or missing word
|
||||||
|
function GetCurrentWord: string;
|
||||||
|
// Seek moves the internal text pointer to the position in Text given by Position.
|
||||||
|
// If Position >= Length(Text), calls to Next always returns false.
|
||||||
|
// Since positioning is 1-based, Seek(0) = Seek(1).
|
||||||
|
procedure Seek(Position: Integer);
|
||||||
|
// Next returns true if a misspelled word was found. If a misspelled word
|
||||||
|
// was found, StartIndex is set to the start of the word in Text and WordLength
|
||||||
|
// is set to the length of the word. Note that StartIndex is 1-based, i.e the first
|
||||||
|
// position in Text is 1. If Next returns false, it means that no more misspelled words
|
||||||
|
// can be found (i.e either when at end of Text or everything from the current point and
|
||||||
|
// onward is correctly spelled)
|
||||||
|
function Next(out StartIndex, WordLength: Integer): WordBool;
|
||||||
|
// The Text to spell check. When Text is changed, the internal position is reset
|
||||||
|
// to the start of Text (no need to call Seek)
|
||||||
|
function GetText: string;
|
||||||
|
procedure SetText(const Value: string);
|
||||||
|
property Text: string read GetText write SetText;
|
||||||
|
// Delimiters specifies the characters that are used to break strings into words.
|
||||||
|
function GetDelimiters: TSysCharSet;
|
||||||
|
procedure SetDelimiters(const Value: TSysCharSet);
|
||||||
|
property Delimiters: TSysCharSet read GetDelimiters write SetDelimiters;
|
||||||
|
|
||||||
|
// Adds the content of a dictionary to the internal list of words that are scanned for matches.
|
||||||
|
procedure SetDictionary(const Value: string);
|
||||||
|
function GetDictionary: string;
|
||||||
|
property Dictionary: string read GetDictionary write SetDictionary;
|
||||||
|
// "User" dictionary. This is a list of words, sorted.
|
||||||
|
// Manage the user dictionary by using the methods of TStrings.
|
||||||
|
// The main difference between a dictionary and a user dictionary is that you cannot change
|
||||||
|
// the content of the main dictionary from the interface. In addition, the UserDictionary is presumed to
|
||||||
|
// contain a list of words, one per line, sorted whereas the dictionary can be in any format (determined
|
||||||
|
// by the actual implementation).
|
||||||
|
function GetUserDictionary: TStrings;
|
||||||
|
procedure SetUserDictionary(const Value: TStrings);
|
||||||
|
property UserDictionary: TStrings read GetUserDictionary write SetUserDictionary;
|
||||||
|
// Ignores are used for words that should be ignored in the current session.
|
||||||
|
// To make an ignore persistent, you should call UserDictionary.Add
|
||||||
|
// and then save/load from file as needed.
|
||||||
|
function GetIgnores: TStrings;
|
||||||
|
procedure SetIgnores(const Value: TStrings);
|
||||||
|
property Ignores: TStrings read GetIgnores write SetIgnores;
|
||||||
|
// Suggestion returns the suggested replacements for a misspelled word. How the
|
||||||
|
// implementation determines valid and/or useful replacement words is defined
|
||||||
|
// by the implementation.
|
||||||
|
function GetSuggestions: TStrings;
|
||||||
|
property Suggestions: TStrings read GetSuggestions;
|
||||||
|
// Assign a handler to this event when you need to set up ignores for words
|
||||||
|
// that can't be captured using the ignore list and/or the user dictionary.
|
||||||
|
function GetCanIgnore: TJvSpellCheckIgnoreEvent;
|
||||||
|
procedure SetCanIgnore(const Value: TJvSpellCheckIgnoreEvent);
|
||||||
|
property OnCanIgnore: TJvSpellCheckIgnoreEvent read GetCanIgnore write SetCanIgnore;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
CreateSpellChecker: function: IJvSpellChecker = nil;
|
||||||
|
|
||||||
|
{$IFDEF UNITVERSIONING}
|
||||||
|
const
|
||||||
|
UnitVersioning: TUnitVersionInfo = (
|
||||||
|
RCSfile: '$URL$';
|
||||||
|
Revision: '$Revision$';
|
||||||
|
Date: '$Date$';
|
||||||
|
LogPath: 'JVCL\run'
|
||||||
|
);
|
||||||
|
{$ENDIF UNITVERSIONING}
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
|
||||||
|
{$IFDEF UNITVERSIONING}
|
||||||
|
initialization
|
||||||
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
||||||
|
|
||||||
|
finalization
|
||||||
|
UnregisterUnitVersion(HInstance);
|
||||||
|
{$ENDIF UNITVERSIONING}
|
||||||
|
|
||||||
|
end.
|
114
components/jvcllaz/run/JvCmp/JvSpellerForm.lfm
Normal file
114
components/jvcllaz/run/JvCmp/JvSpellerForm.lfm
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
object JvSpellerForm: TJvSpellerForm
|
||||||
|
Left = 306
|
||||||
|
Top = 251
|
||||||
|
BorderStyle = bsDialog
|
||||||
|
Caption = 'Spelling checker'
|
||||||
|
ClientHeight = 153
|
||||||
|
ClientWidth = 371
|
||||||
|
Color = clBtnFace
|
||||||
|
Font.Charset = DEFAULT_CHARSET
|
||||||
|
Font.Color = clWindowText
|
||||||
|
Font.Height = -11
|
||||||
|
Font.Name = 'MS Sans Serif'
|
||||||
|
Font.Style = []
|
||||||
|
OldCreateOrder = False
|
||||||
|
PixelsPerInch = 96
|
||||||
|
TextHeight = 13
|
||||||
|
object LblContext: TLabel
|
||||||
|
Left = 0
|
||||||
|
Top = 33
|
||||||
|
Width = 371
|
||||||
|
Height = 86
|
||||||
|
Hint = 'look ahead box'
|
||||||
|
Align = alClient
|
||||||
|
Caption = 'LblContext'
|
||||||
|
ParentShowHint = False
|
||||||
|
ShowHint = True
|
||||||
|
WordWrap = True
|
||||||
|
end
|
||||||
|
object TextPanel: TPanel
|
||||||
|
Left = 0
|
||||||
|
Top = 0
|
||||||
|
Width = 371
|
||||||
|
Height = 33
|
||||||
|
Align = alTop
|
||||||
|
BevelInner = bvRaised
|
||||||
|
BevelOuter = bvLowered
|
||||||
|
TabOrder = 0
|
||||||
|
object TxtSpell: TEdit
|
||||||
|
Left = 7
|
||||||
|
Top = 7
|
||||||
|
Width = 293
|
||||||
|
Height = 21
|
||||||
|
TabOrder = 0
|
||||||
|
Text = 'TxtSpell'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
object ButtonPanel: TPanel
|
||||||
|
Left = 0
|
||||||
|
Top = 119
|
||||||
|
Width = 371
|
||||||
|
Height = 34
|
||||||
|
Align = alBottom
|
||||||
|
BevelInner = bvRaised
|
||||||
|
BevelOuter = bvLowered
|
||||||
|
TabOrder = 1
|
||||||
|
object BtnSkip: TButton
|
||||||
|
Left = 13
|
||||||
|
Top = 7
|
||||||
|
Width = 61
|
||||||
|
Height = 20
|
||||||
|
Hint = 'Skip this word'
|
||||||
|
Caption = '&Skip'
|
||||||
|
ParentShowHint = False
|
||||||
|
ShowHint = True
|
||||||
|
TabOrder = 0
|
||||||
|
end
|
||||||
|
object BtnChange: TButton
|
||||||
|
Left = 228
|
||||||
|
Top = 7
|
||||||
|
Width = 60
|
||||||
|
Height = 20
|
||||||
|
Hint = 'Change to corrected word'
|
||||||
|
Caption = '&Change'
|
||||||
|
ParentShowHint = False
|
||||||
|
ShowHint = True
|
||||||
|
TabOrder = 3
|
||||||
|
end
|
||||||
|
object BtnCancel: TButton
|
||||||
|
Left = 299
|
||||||
|
Top = 7
|
||||||
|
Width = 61
|
||||||
|
Height = 20
|
||||||
|
Hint = 'Abort all changes'
|
||||||
|
Caption = 'Cancel'
|
||||||
|
ModalResult = 2
|
||||||
|
ParentShowHint = False
|
||||||
|
ShowHint = True
|
||||||
|
TabOrder = 4
|
||||||
|
end
|
||||||
|
object BtnAdd: TButton
|
||||||
|
Left = 156
|
||||||
|
Top = 7
|
||||||
|
Width = 61
|
||||||
|
Height = 20
|
||||||
|
Hint = 'Add to user Dictionary'
|
||||||
|
Caption = '&Add'
|
||||||
|
ParentShowHint = False
|
||||||
|
ShowHint = True
|
||||||
|
TabOrder = 2
|
||||||
|
end
|
||||||
|
object BtnSkipAll: TButton
|
||||||
|
Left = 85
|
||||||
|
Top = 7
|
||||||
|
Width = 60
|
||||||
|
Height = 20
|
||||||
|
Hint = 'Skip all, update and finish'
|
||||||
|
Caption = 'S&kip All'
|
||||||
|
ModalResult = 1
|
||||||
|
ParentShowHint = False
|
||||||
|
ShowHint = True
|
||||||
|
TabOrder = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
433
components/jvcllaz/run/JvCmp/JvSpellerForm.pas
Normal file
433
components/jvcllaz/run/JvCmp/JvSpellerForm.pas
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
{-----------------------------------------------------------------------------
|
||||||
|
The contents of this file are subject to the Mozilla Public License
|
||||||
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
||||||
|
|
||||||
|
Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
||||||
|
the specific language governing rights and limitations under the License.
|
||||||
|
|
||||||
|
The Original Code is: JvSpellerForm.PAS, released on 2002-06-15.
|
||||||
|
|
||||||
|
The Initial Developer of the Original Code is Jan Verhoeven [jan1 dott verhoeven att wxs dott nl]
|
||||||
|
Portions created by Jan Verhoeven are Copyright (C) 2002 Jan Verhoeven.
|
||||||
|
All Rights Reserved.
|
||||||
|
|
||||||
|
Contributor(s): Robert Love [rlove att slcdug dott org].
|
||||||
|
|
||||||
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
||||||
|
located at http://jvcl.delphi-jedi.org
|
||||||
|
|
||||||
|
Known Issues:
|
||||||
|
-----------------------------------------------------------------------------}
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
unit JvSpellerForm;
|
||||||
|
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
//{$I jvcl.inc}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
SysUtils, Classes, Windows, Messages, Graphics, Controls, Forms, Dialogs,
|
||||||
|
StdCtrls, ExtCtrls,
|
||||||
|
JvComponent;
|
||||||
|
|
||||||
|
type
|
||||||
|
TJvSpeller = class;
|
||||||
|
|
||||||
|
TJvSpellerForm = class(TForm) //TJvForm)
|
||||||
|
TextPanel: TPanel;
|
||||||
|
LblContext: TLabel;
|
||||||
|
TxtSpell: TEdit;
|
||||||
|
ButtonPanel: TPanel;
|
||||||
|
BtnSkip: TButton;
|
||||||
|
BtnChange: TButton;
|
||||||
|
BtnCancel: TButton;
|
||||||
|
BtnAdd: TButton;
|
||||||
|
BtnSkipAll: TButton;
|
||||||
|
private
|
||||||
|
FSpeller: TJvSpeller;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TJvDicIndexArray = array [1..26] of Integer;
|
||||||
|
|
||||||
|
TJvSpeller = class(TComponent)
|
||||||
|
private
|
||||||
|
FSourceText: string;
|
||||||
|
FDict: string;
|
||||||
|
FUserDic: string;
|
||||||
|
FUserDicChanged: Boolean;
|
||||||
|
FDicIndex: TJvDicIndexArray;
|
||||||
|
FUserDicIndex: TJvDicIndexArray;
|
||||||
|
FSpellerDialog: TJvSpellerForm;
|
||||||
|
FWordBegin: Integer;
|
||||||
|
FWordEnd: Integer;
|
||||||
|
FDictionary: TFileName;
|
||||||
|
FUserDictionary: TFileName;
|
||||||
|
function WordBegin: Boolean;
|
||||||
|
function WordEnd: Boolean;
|
||||||
|
function ParseWord: string;
|
||||||
|
procedure SpellNext;
|
||||||
|
procedure Skip(Sender: TObject);
|
||||||
|
procedure Add(Sender: TObject);
|
||||||
|
procedure Change(Sender: TObject);
|
||||||
|
procedure IndexDictionary;
|
||||||
|
procedure IndexUserDictionary;
|
||||||
|
procedure SetDictionary(const Value: TFileName);
|
||||||
|
procedure SetUserDictionary(const Value: TFileName);
|
||||||
|
procedure CreateSpellerDialog(const SpellWord: string);
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
procedure LoadDictionary(const AFile: string);
|
||||||
|
procedure LoadUserDictionary(const AFile: string);
|
||||||
|
procedure Spell(var SourceText: string);
|
||||||
|
published
|
||||||
|
property Dictionary: TFileName read FDictionary write SetDictionary;
|
||||||
|
property UserDictionary: TFileName read FUserDictionary write SetUserDictionary;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{$IFDEF UNITVERSIONING}
|
||||||
|
const
|
||||||
|
UnitVersioning: TUnitVersionInfo = (
|
||||||
|
RCSfile: '$URL$';
|
||||||
|
Revision: '$Revision$';
|
||||||
|
Date: '$Date$';
|
||||||
|
LogPath: 'JVCL\run'
|
||||||
|
);
|
||||||
|
{$ENDIF UNITVERSIONING}
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
StrUtils,
|
||||||
|
{$IFNDEF COMPILER12_UP}
|
||||||
|
JvJCLUtils,
|
||||||
|
{$ENDIF ~COMPILER12_UP}
|
||||||
|
JvConsts, JvResources, JvTypes;
|
||||||
|
|
||||||
|
{$R *.lfm}
|
||||||
|
|
||||||
|
procedure SaveAnsiFileFromString(const AFile, AText: string);
|
||||||
|
var
|
||||||
|
AnsiText: AnsiString;
|
||||||
|
begin
|
||||||
|
AnsiText := AnsiString(AText);
|
||||||
|
with TFileStream.Create(AFile, fmCreate) do
|
||||||
|
try
|
||||||
|
WriteBuffer(AnsiText[1], Length(AnsiText));
|
||||||
|
finally
|
||||||
|
Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function LoadAnsiFileToString(const AFile: string): string;
|
||||||
|
var
|
||||||
|
AnsiText: AnsiString;
|
||||||
|
begin
|
||||||
|
with TFileStream.Create(AFile, fmOpenRead) do
|
||||||
|
try
|
||||||
|
SetLength(AnsiText, Size);
|
||||||
|
if AnsiText <> '' then
|
||||||
|
ReadBuffer(AnsiText[1], Size);
|
||||||
|
finally
|
||||||
|
Free;
|
||||||
|
end;
|
||||||
|
Result := string(AnsiText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
//=== { TJvSpeller } =========================================================
|
||||||
|
|
||||||
|
constructor TJvSpeller.Create(AOwner: TComponent);
|
||||||
|
var
|
||||||
|
I: Integer;
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
for I := 1 to 26 do
|
||||||
|
begin
|
||||||
|
FDicIndex[I] := 1;
|
||||||
|
FUserDicIndex[I] := 1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.Add(Sender: TObject);
|
||||||
|
var
|
||||||
|
S: string;
|
||||||
|
begin
|
||||||
|
S := FSpellerDialog.TxtSpell.Text;
|
||||||
|
if S = '' then
|
||||||
|
Exit;
|
||||||
|
FUserDic := FUserDic + LowerCase(S) + Cr;
|
||||||
|
FUserDicChanged := True;
|
||||||
|
with TStringList.Create do
|
||||||
|
try
|
||||||
|
Text := FUserDic;
|
||||||
|
Sort;
|
||||||
|
FUserDic := Text;
|
||||||
|
finally
|
||||||
|
Free;
|
||||||
|
end;
|
||||||
|
IndexUserDictionary;
|
||||||
|
Skip(Sender);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.Change(Sender: TObject);
|
||||||
|
var
|
||||||
|
S: string;
|
||||||
|
begin
|
||||||
|
S := FSpellerDialog.TxtSpell.Text;
|
||||||
|
if S <> '' then
|
||||||
|
begin
|
||||||
|
FSourceText := Copy(FSourceText, 1, FWordBegin - 1) + S +
|
||||||
|
Copy(FSourceText, FWordEnd, Length(FSourceText));
|
||||||
|
FWordEnd := FWordEnd + (Length(S) - (FWordEnd - FWordBegin));
|
||||||
|
Skip(Sender);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.IndexDictionary;
|
||||||
|
var
|
||||||
|
I, P, StartPos: Integer;
|
||||||
|
begin
|
||||||
|
FDicIndex[1] := 1;
|
||||||
|
for I := 2 to 26 do
|
||||||
|
begin
|
||||||
|
if FDicIndex[I - 1] <> 1 then
|
||||||
|
StartPos := FDicIndex[I - 1]
|
||||||
|
else
|
||||||
|
StartPos := 1;
|
||||||
|
P := PosEx(Cr + Chr(96 + I), FDict, StartPos);
|
||||||
|
if P <> 0 then
|
||||||
|
FDicIndex[I] := P
|
||||||
|
else
|
||||||
|
FDicIndex[I] := FDicIndex[I - 1];
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.IndexUserDictionary;
|
||||||
|
var
|
||||||
|
I, P, StartPos: Integer;
|
||||||
|
begin
|
||||||
|
FUserDicIndex[1] := 1;
|
||||||
|
for I := 2 to 26 do
|
||||||
|
begin
|
||||||
|
if FUserDicIndex[I - 1] <> 1 then
|
||||||
|
StartPos := FUserDicIndex[I - 1]
|
||||||
|
else
|
||||||
|
StartPos := 1;
|
||||||
|
P := PosEx(Cr + Chr(96 + I), FUserDic, StartPos);
|
||||||
|
if P <> 0 then
|
||||||
|
FUserDicIndex[I] := P
|
||||||
|
else
|
||||||
|
FUserDicIndex[I] := FUserDicIndex[I - 1];
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.LoadDictionary(const AFile: string);
|
||||||
|
begin
|
||||||
|
if FileExists(AFile) then
|
||||||
|
FDict := LoadAnsiFileToString(AFile)
|
||||||
|
else
|
||||||
|
FDict := '';
|
||||||
|
IndexDictionary;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.LoadUserDictionary(const AFile: string);
|
||||||
|
begin
|
||||||
|
UserDictionary := AFile;
|
||||||
|
FUserDicChanged := False;
|
||||||
|
if FileExists(AFile) then
|
||||||
|
FUserDic := LoadAnsiFileToString(AFile)
|
||||||
|
else
|
||||||
|
FUserDic := '';
|
||||||
|
IndexUserDictionary;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvSpeller.ParseWord: string;
|
||||||
|
begin
|
||||||
|
if WordBegin and WordEnd then
|
||||||
|
Result := Copy(FSourceText, FWordBegin, FWordEnd - FWordBegin)
|
||||||
|
else
|
||||||
|
Result := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.SetDictionary(const Value: TFileName);
|
||||||
|
begin
|
||||||
|
if FDictionary <> Value then
|
||||||
|
begin
|
||||||
|
FDictionary := Value;
|
||||||
|
LoadDictionary(FDictionary);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.SetUserDictionary(const Value: TFileName);
|
||||||
|
begin
|
||||||
|
if FUserDictionary <> Value then
|
||||||
|
begin
|
||||||
|
FUserDictionary := Value;
|
||||||
|
LoadUserDictionary(FUserDictionary);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.Skip(Sender: TObject);
|
||||||
|
begin
|
||||||
|
FSpellerDialog.TxtSpell.Text := '';
|
||||||
|
SpellNext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.CreateSpellerDialog(const SpellWord: string);
|
||||||
|
begin
|
||||||
|
FSpellerDialog := TJvSpellerForm.Create(Application);
|
||||||
|
with FSpellerDialog do
|
||||||
|
begin
|
||||||
|
FSpeller := Self;
|
||||||
|
BtnSkip.OnClick := @Skip;
|
||||||
|
BtnChange.OnClick := @Change;
|
||||||
|
BtnAdd.OnClick := @Add;
|
||||||
|
BtnAdd.Enabled := UserDictionary <> '';
|
||||||
|
TxtSpell.Text := SpellWord;
|
||||||
|
LblContext.Caption := Copy(FSourceText, FWordBegin, 75);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.Spell(var SourceText: string);
|
||||||
|
var
|
||||||
|
Spw, S: string;
|
||||||
|
StartPos, Index: Integer;
|
||||||
|
begin
|
||||||
|
if FDict = '' then
|
||||||
|
raise EJVCLException.CreateRes(@RsENoDictionaryLoaded);
|
||||||
|
|
||||||
|
FSourceText := SourceText;
|
||||||
|
FWordEnd := 1;
|
||||||
|
|
||||||
|
Spw := ParseWord;
|
||||||
|
while Spw <> '' do
|
||||||
|
begin
|
||||||
|
S := AnsiLowerCase(Spw);
|
||||||
|
Index := Ord(S[1]) - 96;
|
||||||
|
if (Index > 0) and (Index < 27) then
|
||||||
|
StartPos := FDicIndex[Index]
|
||||||
|
else
|
||||||
|
StartPos := 1;
|
||||||
|
|
||||||
|
if PosEx(S + Cr, FDict, StartPos) = 0 then
|
||||||
|
begin
|
||||||
|
if FUserDic <> '' then
|
||||||
|
begin
|
||||||
|
if (Index > 0) and (Index < 27) then
|
||||||
|
StartPos := FUserDicIndex[Index]
|
||||||
|
else
|
||||||
|
StartPos := 1;
|
||||||
|
if PosEx(S + Cr, FUserDic, StartPos) = 0 then
|
||||||
|
begin
|
||||||
|
CreateSpellerDialog(Spw);
|
||||||
|
try
|
||||||
|
if FSpellerDialog.ShowModal = mrOk then
|
||||||
|
SourceText := FSourceText;
|
||||||
|
// (rom) the user dictionary has to be saved always!
|
||||||
|
if FUserDicChanged then
|
||||||
|
if FUserDic <> '' then
|
||||||
|
SaveAnsiFileFromString(UserDictionary, FUserDic);
|
||||||
|
finally
|
||||||
|
FSpellerDialog.Free;
|
||||||
|
end;
|
||||||
|
Exit;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
CreateSpellerDialog(Spw);
|
||||||
|
try
|
||||||
|
if FSpellerDialog.ShowModal = mrOk then
|
||||||
|
SourceText := FSourceText;
|
||||||
|
// (rom) the user dictionary has to be saved always!
|
||||||
|
if FUserDicChanged then
|
||||||
|
if FUserDic <> '' then
|
||||||
|
SaveAnsiFileFromString(UserDictionary, FUserDic);
|
||||||
|
finally
|
||||||
|
FSpellerDialog.Free;
|
||||||
|
end;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Spw := ParseWord;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TJvSpeller.SpellNext;
|
||||||
|
var
|
||||||
|
Spw, S: string;
|
||||||
|
Index, StartPos: Integer;
|
||||||
|
begin
|
||||||
|
Spw := ParseWord;
|
||||||
|
while Spw <> '' do
|
||||||
|
begin
|
||||||
|
S := AnsiLowerCase(Spw);
|
||||||
|
Index := Ord(S[1]) - 96;
|
||||||
|
if (Index > 0) and (Index < 27) then
|
||||||
|
StartPos := FDicIndex[Index]
|
||||||
|
else
|
||||||
|
StartPos := 1;
|
||||||
|
|
||||||
|
if PosEx(S + Cr, FDict, StartPos) = 0 then
|
||||||
|
begin
|
||||||
|
if FUserDic <> '' then
|
||||||
|
begin
|
||||||
|
if (Index > 0) and (Index < 27) then
|
||||||
|
StartPos := FUserDicIndex[Index]
|
||||||
|
else
|
||||||
|
StartPos := 1;
|
||||||
|
if PosEx(S + Cr, FUserDic, StartPos) = 0 then
|
||||||
|
begin
|
||||||
|
FSpellerDialog.TxtSpell.Text := Spw;
|
||||||
|
FSpellerDialog.LblContext.Caption := Copy(FSourceText, FWordBegin, 75);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
FSpellerDialog.TxtSpell.Text := Spw;
|
||||||
|
FSpellerDialog.LblContext.Caption := Copy(FSourceText, FWordBegin, 75);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Spw := ParseWord;
|
||||||
|
end;
|
||||||
|
FSpellerDialog.ModalResult := mrOk;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvSpeller.WordBegin: Boolean;
|
||||||
|
var
|
||||||
|
L: Integer;
|
||||||
|
begin
|
||||||
|
L := Length(FSourceText);
|
||||||
|
FWordBegin := FWordEnd;
|
||||||
|
while (FWordBegin <= L) and (not CharInSet(FSourceText[FWordBegin], ['a'..'z', 'A'..'Z'])) do
|
||||||
|
Inc(FWordBegin);
|
||||||
|
Result := (FWordBegin <= L);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TJvSpeller.WordEnd: Boolean;
|
||||||
|
var
|
||||||
|
L: Integer;
|
||||||
|
begin
|
||||||
|
FWordEnd := FWordBegin;
|
||||||
|
L := Length(FSourceText);
|
||||||
|
while (FWordEnd <= L) and CharInSet(FSourceText[FWordEnd], ['a'..'z', 'A'..'Z']) do
|
||||||
|
Inc(FWordEnd);
|
||||||
|
Result := (FWordEnd <= L);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{$IFDEF UNITVERSIONING}
|
||||||
|
initialization
|
||||||
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
||||||
|
|
||||||
|
finalization
|
||||||
|
UnregisterUnitVersion(HInstance);
|
||||||
|
{$ENDIF UNITVERSIONING}
|
||||||
|
|
||||||
|
end.
|
Reference in New Issue
Block a user