Files
lazarus-ccr/components/jvcllaz/examples/JvSpellChecker/JvSpellCheckerForm.pas
wp_xxyyzz bfdb6c5eaa jvcllaz: Fix a series of issues with Linux
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6318 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2018-04-18 22:20:45 +00:00

337 lines
11 KiB
ObjectPascal

{******************************************************************
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+}
uses
LCLIntf, LMessages,
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 AsyncClose(Data: PtrInt);
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.AsyncClose(Data: PtrInt);
begin
Close;
end;
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 := '..' + PathDelim + 'Dict' + PathDelim + '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)
Application.QueueAsyncCall(@AsyncClose, 0);
// PostMessage(Handle, LM_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.