{********************************************************************} { } { Developer Express Visual Component Library } { ExpressSpellChecker } { } { Copyright (c) 1998-2008 Developer Express Inc. } { ALL RIGHTS RESERVED } { } { The entire contents of this file is protected by U.S. and } { International Copyright Laws. Unauthorized reproduction, } { reverse-engineering, and distribution of all or any portion of } { the code contained in this file is strictly prohibited and may } { result in severe civil and criminal penalties and will be } { prosecuted to the maximum extent possible under the law. } { } { RESTRICTIONS } { } { THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES } { (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE } { SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS } { LICENSED TO DISTRIBUTE THE EXPRESSSPELLCHECKER AND ALL } { ACCOMPANYING VCL CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY. } { } { THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED } { FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE } { COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE } { AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT } { AND PERMISSION FROM DEVELOPER EXPRESS INC. } { } { CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON } { ADDITIONAL RESTRICTIONS. } { } {********************************************************************} unit dxSpellChecker; {$I cxVer.inc} interface uses Types, Windows, SysUtils, Messages, Classes, Graphics, cxClasses, Controls, Forms, StdCtrls, cxControls, dxSpellCheckerRules, dxSpellCheckerAlgorithms, cxLookAndFeels, cxTextEdit, cxRichEdit, Menus; type TdxCustomSpellChecker = class; TdxSpellCheckerWordList = class; TdxSpellCheckerTextParser = class; TdxCustomSpellCheckerDictionary = class; TdxSpellCheckerSuggestionList = class; TdxSpellCheckerSpellingOptions = class; TdxSpellCheckerCheckAsYouTypeOptions = class; EdxSpellCheckerException = class(Exception); { TdxSpellCheckerLanguages } TdxSpellCheckerLanguages = class(TLanguages) public function GetDefaultLanguageLCID: DWORD; function GetLCID(const AName: string): DWORD; end; { TdxSpellCheckerSuggestion } TdxSpellCheckerSuggestion = class private FDictionary: TdxCustomSpellCheckerDictionary; FDistance: Integer; FWord: WideString; public constructor Create(const AWord: WideString; ADictionary: TdxCustomSpellCheckerDictionary; ADistance: Integer); property Dictionary: TdxCustomSpellCheckerDictionary read FDictionary; property Distance: Integer read FDistance; property Word: WideString read FWord; end; { TdxSpellCheckerSuggestionList } TdxSpellCheckerSuggestionList = class(TcxObjectList) private function GetItem(Index: Integer): TdxSpellCheckerSuggestion; public procedure Add(const AWord: WideString; ADictionary: TdxCustomSpellCheckerDictionary; ADistance: Integer); procedure Delete(AIndex: Integer); procedure FixCapitalization(const AMask: WideString); procedure RemoveDuplicates; procedure SortByWord; procedure SortByDistance; procedure SaveToStrings(AStrings: TStrings); property Items[Index: Integer]: TdxSpellCheckerSuggestion read GetItem; default; end; { TdxSpellCheckerReplacementItem } TdxSpellCheckerReplacementItem = class private FReplacement: WideString; FText: WideString; public constructor Create(const AText, AReplacement: WideString); property Replacement: WideString read FReplacement; property Text: WideString read FText; end; { TdxSpellCheckerReplacementList } TdxSpellCheckerReplacementList = class(TcxObjectList) private FAllowDuplicates: Boolean; function GetItem(AIndex: Integer): TdxSpellCheckerReplacementItem; public constructor Create(AllowDuplicates: Boolean = False); procedure Add(const AText, AReplacement: WideString); function FindReplacement(const AText: WideString): TdxSpellCheckerReplacementItem; procedure SortByText; property Items[Index: Integer]: TdxSpellCheckerReplacementItem read GetItem; default; end; { TdxSpellCheckerCustomEditAdapter } TdxSpellCheckerCustomEditAdapter = class(TObject) protected FEdit: TWinControl; FText: WideString; FSelStart: Integer; FSelLength: Integer; function EditorHandleAllocated: Boolean; function GetEditorHandle: THandle; virtual; function GetHideSelection: Boolean; virtual; abstract; function GetReadOnly: Boolean; virtual; abstract; function GetSelLength: Integer; virtual; abstract; function GetSelStart: Integer; virtual; abstract; function GetSelText: string; virtual; abstract; function GetText: WideString; virtual; procedure Post(AUpdateValue: Boolean = True); virtual; procedure SetHideSelection(AValue: Boolean); virtual; abstract; procedure SetSelLength(AValue: Integer); virtual; abstract; procedure SetSelStart(AValue: Integer); virtual; abstract; procedure SetSelText(const AValue: string); virtual; abstract; function IsInplace: Boolean; public constructor Create(AEdit: TWinControl); virtual; procedure ClearSelection; virtual; abstract; procedure GetSpellingBounds(out ASpellingStart, ASpellingEnd: Integer); virtual; property EditorHandle: THandle read GetEditorHandle; property HideSelection: Boolean read GetHideSelection write SetHideSelection; property ReadOnly: Boolean read GetReadOnly; property SelLength: Integer read GetSelLength write SetSelLength; property SelStart: Integer read GetSelStart write SetSelStart; property SelText: string read GetSelText write SetSelText; property Text: WideString read GetText; end; { TdxSpellCheckercxTextEditAdapter } TdxSpellCheckercxTextEditAdapter = class(TdxSpellCheckerCustomEditAdapter) private function GetEdit: TcxCustomTextEdit; protected function GetHideSelection: Boolean; override; function GetReadOnly: Boolean; override; function GetSelLength: Integer; override; function GetSelStart: Integer; override; function GetSelText: string; override; procedure SetHideSelection(AValue: Boolean); override; procedure SetSelLength(AValue: Integer); override; procedure SetSelStart(AValue: Integer); override; procedure SetSelText(const AValue: string); override; property Edit: TcxCustomTextEdit read GetEdit; public procedure ClearSelection; override; end; { TdxSpellCheckercxRichEditAdapter } TdxSpellCheckercxRichEditAdapter = class(TdxSpellCheckercxTextEditAdapter) private function GetEdit: TcxCustomRichEdit; protected function GetReadOnly: Boolean; override; function GetText: WideString; override; property Edit: TcxCustomRichEdit read GetEdit; end; { TdxSpellCheckerEditAdapter } TdxSpellCheckerEditAdapter = class(TdxSpellCheckerCustomEditAdapter) private function GetEdit: TCustomEdit; protected function GetHideSelection: Boolean; override; function GetReadOnly: Boolean; override; function GetSelLength: Integer; override; function GetSelStart: Integer; override; function GetSelText: string; override; procedure SetHideSelection(AValue: Boolean); override; procedure SetSelLength(AValue: Integer); override; procedure SetSelStart(AValue: Integer); override; procedure SetSelText(const AValue: string); override; property Edit: TCustomEdit read GetEdit; public procedure ClearSelection; override; end; { TdxSpellCheckerTextParser } TdxSpellCheckerSentence = record StartTextPosition: Integer; StartWordPosition: Integer; Text: WideString; end; TdxSpellCheckerTextParser = class(TdxCustomSpellCheckerTextHelper) private FSpellChecker: TdxCustomSpellChecker; function LastWideCharIsSpace(const S: WideString): Boolean; protected FDelimiters: WideString; FUrlDelimiters: WideString; FPrevWord: WideString; FSpellingEnd: Integer; FSpellingStart: Integer; FText: WideString; function IsDelimiter(AChar: WideChar): Boolean; function IsUrlDelimiter(AChar: WideChar): Boolean; procedure UpdateTextInfo(AAdapter: TdxSpellCheckerCustomEditAdapter); virtual; property SpellChecker: TdxCustomSpellChecker read FSpellChecker; public constructor Create(ASpellChecker: TdxCustomSpellChecker); virtual; destructor Destroy; override; function GetNextWord(var AStart, ALength: Integer; out ADelimiters: WideString): WideString; virtual; function GetPrevWord: WideString; override; function WordExists(const AWord: WideString): Boolean; override; function GetNextDelimiterPosition(var AStart: Integer; ADirection: Integer): Boolean; function GetSentence(APos: Integer): TdxSpellCheckerSentence; virtual; function GetSuggestions(const AWord: WideString): TdxSpellCheckerSuggestionList; virtual; property Delimiters: WideString read FDelimiters write FDelimiters; property UrlDelimiters: WideString read FUrlDelimiters write FUrlDelimiters; property Text: WideString read FText; end; TdxSpellCheckerTextParserClass = class of TdxSpellCheckerTextParser; { TdxSpellCheckerUndoItem } TdxSpellCheckerUndoItem = class private FMisspelledWord: WideString; FMisspelledWordPosition: Integer; FPrevWord: WideString; FReplacement: WideString; public constructor Create(const AMisspelledWord, AReplacement, APrevWord: WideString; AMisspelledWordPosition: Integer); property MisspelledWord: WideString read FMisspelledWord; property MisspelledWordPosition: Integer read FMisspelledWordPosition; property PrevWord: WideString read FPrevWord; property Replacement: WideString read FReplacement; end; { TdxSpellCheckerUndoManager } TdxSpellCheckerUndoManager = class(TPersistent) private FUndoList: TcxObjectList; function GetCount: Integer; function GetItem(AIndex: Integer): TdxSpellCheckerUndoItem; function GetLast: TdxSpellCheckerUndoItem; public constructor Create; destructor Destroy; override; procedure Add(const AMisspelledWord, AReplacement, APrevWord: WideString; AMisspelledWordPosition: Integer); procedure UndoLast; property Count: Integer read GetCount; property Last: TdxSpellCheckerUndoItem read GetLast; property Items[AIndex: Integer]: TdxSpellCheckerUndoItem read GetItem; default; end; { TdxSpellCheckerCustomCheckMode } TdxSpellCheckerCustomCheckMode = class(TPersistent) private FAdapter: TdxSpellCheckerCustomEditAdapter; FParser: TdxSpellCheckerTextParser; FSpellChecker: TdxCustomSpellChecker; FSpellingEnd: Integer; FSpellingStart: Integer; function GetPrevWord: WideString; procedure SetPrevWord(const AValue: WideString); procedure SetSpellingEnd(const AValue: Integer); procedure SetSpellingStart(const AValue: Integer); protected FLastCode: TdxSpellCheckerSpellingCode; FMisspelledLen: Integer; FMisspelledStart: Integer; FMisspelledWord: WideString; function CheckWord(const AWord: WideString): TdxSpellCheckerSpellingCode; virtual; procedure CreateParser; virtual; function GetNextWord(out AWord: WideString): Boolean; virtual; function GetOwner: TPersistent; override; function GetParserClass: TdxSpellCheckerTextParserClass; virtual; function InternalProcessWord(const AWord: WideString): Boolean; virtual; function IsNeedChangeWord(const AWord: WideString; out AReplacement: WideString): Boolean; virtual; function IsNeedDeleteWord(const AWord: WideString): Boolean; virtual; function IsNeedIgnoreWord(const AWord: WideString): Boolean; virtual; function IsValidWord(const AWord: WideString): Boolean; virtual; procedure SaveSelection; virtual; procedure SelectMisspelledWord; virtual; procedure SelectMisspelledWordAfterUndo; virtual; procedure UpdateByDictionary(ADictionary: TdxCustomSpellCheckerDictionary); virtual; procedure UpdateSpellingBounds(ADelta: Integer); virtual; procedure UpdateTextInfo; virtual; procedure ValidateSpellingBounds; virtual; property PrevWord: WideString read GetPrevWord write SetPrevWord; property SpellingEnd: Integer read FSpellingEnd write SetSpellingEnd; property SpellingStart: Integer read FSpellingStart write SetSpellingStart; public constructor Create(AOwner: TdxCustomSpellChecker; AAdapter: TdxSpellCheckerCustomEditAdapter); virtual; destructor Destroy; override; procedure Add; virtual; function CanUndo: Boolean; virtual; procedure Change(const AWord: WideString); virtual; procedure ChangeAll(const AWord: WideString); virtual; procedure Delete; virtual; procedure DeleteAll; virtual; function GetSuggestions(const AWord: WideString): TdxSpellCheckerSuggestionList; virtual; function GetNextMisspelledWord: Boolean; virtual; procedure Ignore; virtual; procedure IgnoreAll; virtual; procedure UndoLast; virtual; property Adapter: TdxSpellCheckerCustomEditAdapter read FAdapter; property LastCode: TdxSpellCheckerSpellingCode read FLastCode; property MisspelledWord: WideString read FMisspelledWord; property Parser: TdxSpellCheckerTextParser read FParser; property SpellChecker: TdxCustomSpellChecker read FSpellChecker; end; TdxSpellCheckerCustomCheckModeClass = class of TdxSpellCheckerCustomCheckMode; { TdxSpellCheckerDialogCheckMode } TdxSpellCheckerDialogCheckMode = class(TdxSpellCheckerCustomCheckMode) private FCheckedSelection: Boolean; FSaveHideSelection: Boolean; FSaveSelLength: Integer; FSaveSelStart: Integer; FUndoManager: TdxSpellCheckerUndoManager; function GetLanguages: TdxSpellCheckerLanguages; protected procedure CreateParser; override; function IsCheckingSelectedText: Boolean; virtual; procedure SaveSelection; override; procedure ValidateSpellingBounds; override; procedure ValidateSpellingBoundsAfterUndo; property UndoManager: TdxSpellCheckerUndoManager read FUndoManager; public constructor Create(AOwner: TdxCustomSpellChecker; AAdapter: TdxSpellCheckerCustomEditAdapter); override; destructor Destroy; override; function CanUndo: Boolean; override; procedure Change(const AWord: WideString); override; procedure Delete; override; procedure Ignore; override; function ShowDialog: Integer; virtual; procedure UndoLast; override; property Languages: TdxSpellCheckerLanguages read GetLanguages; end; { TdxSpellCheckerOutlookCheckMode } TdxSpellCheckerOutlookCheckMode = class(TdxSpellCheckerDialogCheckMode) protected procedure SelectMisspelledWordAfterUndo; override; public function ShowDialog: Integer; override; end; { TdxSpellCheckerWordCheckMode } TdxSpellCheckerWordCheckMode = class(TdxSpellCheckerDialogCheckMode) private function GetMisspelledSentence: TdxSpellCheckerSentence; protected procedure SelectMisspelledWordAfterUndo; override; public procedure ChangeSentence(const ASentence: WideString); virtual; function ShowDialog: Integer; override; property MisspelledSentence: TdxSpellCheckerSentence read GetMisspelledSentence; end; { TdxSpellCheckerWordList } TdxSpellCheckerWordList = class private FCount: Integer; FLangID: Cardinal; FLock: TRTLCriticalSection; FTable: PPointerList; FTableSize: Integer; function GetCodePage: Cardinal; protected function ElfHash(P: PWideChar): Integer; function FindWord(const S: WideString): PWideChar; function NewWord(S: PWideChar): PWideChar; function AllocWord(ACharCount: Cardinal): PWideChar; procedure DisposeWord(AWord: PWideChar); function GetNextWord(AWord: PWideChar): Pointer; procedure SetNextWord(AWord: PWideChar; AValue: Pointer); function WordLength(AWord: PWideChar): Integer; property CodePage: Cardinal read GetCodePage; public constructor Create(ALangID: Cardinal; ATableSize: Integer); destructor Destroy; override; procedure Add(const S: WideString); procedure Clear; function Find(const S: WideString): Boolean; procedure LoadFromStrings(AStrings: TStrings); procedure SaveToStrings(AStrings: TStrings); property Count: Integer read FCount; property LangID: Cardinal read FLangID write FLangID; end; { TdxSpellCheckerSuggestionBuilder } TdxSpellCheckerSuggestionBuilder = class private FAlphabet: WideString; FDictionary: TdxCustomSpellCheckerDictionary; FSuggestions: TdxSpellCheckerSuggestionList; FWord: WideString; procedure SetAlphabet(const AValue: WideString); protected function CanAddToSuggestions(const ATestWord: WideString): Boolean; virtual; procedure DoAddSuggestions; virtual; abstract; property Alphabet: WideString read FAlphabet write SetAlphabet; property Dictionary: TdxCustomSpellCheckerDictionary read FDictionary; property Suggestions: TdxSpellCheckerSuggestionList read FSuggestions; property Word: WideString read FWord; public constructor Create(ADictionary: TdxCustomSpellCheckerDictionary); virtual; procedure AddSuggestions(const AWord: WideString; ASuggestions: TdxSpellCheckerSuggestionList); virtual; end; { TdxNearMissStrategy } TdxNearMissStrategy = class(TdxSpellCheckerSuggestionBuilder) protected procedure CheckAddToSuggestions(const ATestWord: WideString; ADistance: Integer = 2); procedure CheckChangeOneLetter; procedure CheckInsertLetter; procedure CheckInsertSpace; procedure CheckDeleteLetter; procedure DoAddSuggestions; override; procedure InterchangeTwoLetters; end; { TdxDictionaryLoadThread } TdxDictionaryLoadThread = class(TcxThread) private FDictionary: TdxCustomSpellCheckerDictionary; protected procedure Execute; override; property Dictionary: TdxCustomSpellCheckerDictionary read FDictionary; public constructor Create(ADictionary: TdxCustomSpellCheckerDictionary); function IsLoadComplete: Boolean; end; { TdxCustomSpellCheckerDictionary } TdxSpellCheckerDictionaryLoadingEvent = procedure (Sender: TdxCustomSpellCheckerDictionary; var AHandled: Boolean) of object; TdxSpellCheckerDictionaryLoadedEvent = procedure (Sender: TdxCustomSpellCheckerDictionary) of object; TdxSpellCheckerDictionaryLoadMode = (dlmDefault, dlmDirectLoad, dlmThreadedLoad); TdxCustomSpellCheckerDictionary = class(TPersistent) private FAlphabet: WideString; FCodePage: Cardinal; FEnabled: Boolean; FLanguage: DWORD; FLoaded: Boolean; FLoadThread: TdxDictionaryLoadThread; FSpellChecker: TdxCustomSpellChecker; FWords: TdxSpellCheckerWordList; FOnLoading: TdxSpellCheckerDictionaryLoadingEvent; FOnLoaded: TdxSpellCheckerDictionaryLoadedEvent; function GetActive: Boolean; function GetWordCount: Integer; procedure SetEnabled(AValue: Boolean); procedure SetCodePage(AValue: Cardinal); procedure SetLanguage(const AValue: DWORD); protected function CanLoad: Boolean; virtual; procedure Cleanup; virtual; function CreateSuggestionBuilder: TdxSpellCheckerSuggestionBuilder; virtual; procedure DirectLoad; virtual; procedure DoActivate; virtual; function DoLoad: Boolean; virtual; procedure DoLoadedEvent; virtual; function DoLoadingEvent: Boolean; virtual; function DoUnload: Boolean; virtual; function GetActiveAlphabet: WideString; virtual; function GetDisplayName: string; virtual; procedure LoadingComplete; virtual; function LoadingTerminated: Boolean; {$IFDEF DELPHI9} inline; {$ENDIF} procedure LoadUsingThread; virtual; procedure ThreadDone(Sender: TObject); virtual; procedure Update; virtual; procedure UpdateLoadedOnLoadEvent; property LoadThread: TdxDictionaryLoadThread read FLoadThread; property Words: TdxSpellCheckerWordList read FWords; public constructor Create(ASpellChecker: TdxCustomSpellChecker); virtual; destructor Destroy; override; procedure Assign(Source: TPersistent); override; procedure Activate; function HasWord(const AWord: WideString): Boolean; virtual; procedure Clear; procedure Load(AMode: TdxSpellCheckerDictionaryLoadMode = dlmDefault); procedure Unload; property Active: Boolean read GetActive; property Alphabet: WideString read FAlphabet write FAlphabet; property CodePage: Cardinal read FCodePage write SetCodePage default CP_ACP; property Enabled: Boolean read FEnabled write SetEnabled default True; property Language: DWORD read FLanguage write SetLanguage default 0; property Loaded: Boolean read FLoaded; property SpellChecker: TdxCustomSpellChecker read FSpellChecker; property WordCount: Integer read GetWordCount; property OnLoaded: TdxSpellCheckerDictionaryLoadedEvent read FOnLoaded write FOnLoaded; property OnLoading: TdxSpellCheckerDictionaryLoadingEvent read FOnLoading write FOnLoading; end; TdxCustomSpellCheckerDictionaryClass = class of TdxCustomSpellCheckerDictionary; { TdxUserSpellCheckerDictionary } TdxUserSpellCheckerDictionaryOption = (udFileMustExist, udSaveOnUnload); TdxUserSpellCheckerDictionaryOptions = set of TdxUserSpellCheckerDictionaryOption; TdxUserSpellCheckerDictionary = class(TdxCustomSpellCheckerDictionary) private FDictionaryPath: TFileName; FOptions: TdxUserSpellCheckerDictionaryOptions; protected function DoLoad: Boolean; override; function DoUnload: Boolean; override; function GetDisplayName: string; override; public constructor Create(ASpellChecker: TdxCustomSpellChecker); override; procedure Assign(Source: TPersistent); override; procedure AddWord(const AWord: WideString); procedure LoadFromStrings(AStrings: TStrings); procedure SaveToStrings(AStrings: TStrings); published property Alphabet; property CodePage; property Enabled; property DictionaryPath: TFileName read FDictionaryPath write FDictionaryPath; property Options: TdxUserSpellCheckerDictionaryOptions read FOptions write FOptions default [udSaveOnUnload]; property OnLoaded; property OnLoading; end; { TdxSpellCheckerDictionaryItem } TdxSpellCheckerDictionaries = class; TdxSpellCheckerDictionaryItem = class(TCollectionItem) private FDictionaryType: TdxCustomSpellCheckerDictionary; FDictionaryTypeClass: TdxCustomSpellCheckerDictionaryClass; FDictionaryEvents: TNotifyEvent; function GetCollection: TdxSpellCheckerDictionaries; function GetDictionaryTypeClassName: string; procedure SetDictionaryType(AValue: TdxCustomSpellCheckerDictionary); procedure SetDictionaryTypeClassName(const AValue: string); procedure SetDictionaryTypeClass(AValue: TdxCustomSpellCheckerDictionaryClass); protected function GetDisplayName: string; override; procedure RecreateDictionaryType; public destructor Destroy; override; procedure Assign(Source: TPersistent); override; property Collection: TdxSpellCheckerDictionaries read GetCollection; property DictionaryTypeClass: TdxCustomSpellCheckerDictionaryClass read FDictionaryTypeClass write SetDictionaryTypeClass; published property DictionaryTypeClassName: string read GetDictionaryTypeClassName write SetDictionaryTypeClassName; property DictionaryType: TdxCustomSpellCheckerDictionary read FDictionaryType write SetDictionaryType; property DictionaryEvents: TNotifyEvent read FDictionaryEvents write FDictionaryEvents; end; { TdxSpellCheckerDictionaries } TdxSpellCheckerDictionaries = class(TCollection) private FSpellChecker: TdxCustomSpellChecker; function GetItem(Index: Integer): TdxSpellCheckerDictionaryItem; procedure SetItem(Index: Integer; AValue: TdxSpellCheckerDictionaryItem); protected function GetOwner: TPersistent; override; property SpellChecker: TdxCustomSpellChecker read FSpellChecker; public constructor Create(ASpellChecker: TdxCustomSpellChecker); function Add: TdxSpellCheckerDictionaryItem; function GetNamePath: string; override; property Items[Index: Integer]: TdxSpellCheckerDictionaryItem read GetItem write SetItem; default; end; { TdxSpellCheckerSpellingOptions } TdxSpellCheckerSpellingOptionsChangedEvent = procedure (Sender: TdxSpellCheckerSpellingOptions) of object; TdxSpellCheckerSpellingOptions = class(TPersistent) private FSpellChecker: TdxCustomSpellChecker; FIgnoreMixedCaseWords: Boolean; FIgnoreUpperCaseWords: Boolean; FIgnoreMarkupTags: Boolean; FIgnoreRepeatedWords: Boolean; FCheckSelectedTextFirst: Boolean; FIgnoreUrls: Boolean; FCheckFromCursorPos: Boolean; FIgnoreEmails: Boolean; FIgnoreWordsWithNumbers: Boolean; FOnChanged: TdxSpellCheckerSpellingOptionsChangedEvent; procedure SetCheckFromCursorPos(AValue: Boolean); procedure SetCheckSelectedTextFirst(AValue: Boolean); procedure SetIgnoreEmails(AValue: Boolean); procedure SetIgnoreMarkupTags(AValue: Boolean); procedure SetIgnoreMixedCaseWords(AValue: Boolean); procedure SetIgnoreRepeatedWords(AValue: Boolean); procedure SetIgnoreUpperCaseWords(AValue: Boolean); procedure SetIgnoreUrls(AValue: Boolean); procedure SetIgnoreWordsWithNumbers(AValue: Boolean); protected procedure Changed; virtual; procedure DoChanged; virtual; procedure PopulateRules(ARules: TdxSpellCheckerRules; AParser: TdxSpellCheckerTextParser); virtual; property SpellChecker: TdxCustomSpellChecker read FSpellChecker; property IgnoreMarkupTags: Boolean read FIgnoreMarkupTags write SetIgnoreMarkupTags default True; public constructor Create(ASpellChecker: TdxCustomSpellChecker); virtual; procedure Assign(Source: TPersistent); override; property OnChanged: TdxSpellCheckerSpellingOptionsChangedEvent read FOnChanged write FOnChanged; published property CheckFromCursorPos: Boolean read FCheckFromCursorPos write SetCheckFromCursorPos default False; property CheckSelectedTextFirst: Boolean read FCheckSelectedTextFirst write SetCheckSelectedTextFirst default True; property IgnoreEmails: Boolean read FIgnoreEmails write SetIgnoreEmails default True; property IgnoreMixedCaseWords: Boolean read FIgnoreMixedCaseWords write SetIgnoreMixedCaseWords default True; property IgnoreRepeatedWords: Boolean read FIgnoreRepeatedWords write SetIgnoreRepeatedWords default False; property IgnoreUpperCaseWords: Boolean read FIgnoreUpperCaseWords write SetIgnoreUpperCaseWords default True; property IgnoreUrls: Boolean read FIgnoreUrls write SetIgnoreUrls default True; property IgnoreWordsWithNumbers: Boolean read FIgnoreWordsWithNumbers write SetIgnoreWordsWithNumbers default True; end; { TdxSpellCheckerCheckAsYouTypeOptions } TdxSpellCheckerUnderlineStyle = (usAuto, usWavyLine, usLine); TdxSpellCheckerPopupMenuItem = (scmiAddToDictionary, scmiDelete, scmiIgnoreAll, scmiSpelling, scmiSuggestions); TdxSpellCheckerPopupMenuItems = set of TdxSpellCheckerPopupMenuItem; TdxSpellCheckerCheckAsYouTypeOptionsChangedEvent = procedure( ASender: TdxSpellCheckerCheckAsYouTypeOptions) of object; TdxSpellCheckerCheckAsYouTypeOptions = class(TPersistent) private FActive: Boolean; FModifyControlPopupMenu: Boolean; FPopupMenu: TComponent; FPopupMenuItems: TdxSpellCheckerPopupMenuItems; FSpellChecker: TdxCustomSpellChecker; FSuggestionCount: Integer; FUnderlineColor: TColor; FUnderlineStyle: TdxSpellCheckerUnderlineStyle; FOnChanged: TdxSpellCheckerCheckAsYouTypeOptionsChangedEvent; procedure SetActive(AValue: Boolean); procedure SetPopupMenu(AValue: TComponent); procedure SetModifyControlPopupMenu(AValue: Boolean); procedure SetPopupMenuItems(AValue: TdxSpellCheckerPopupMenuItems); procedure SetSuggestionCount(AValue: Integer); procedure SetUnderlineColor(AValue: TColor); procedure SetUnderlineStyle(AValue: TdxSpellCheckerUnderlineStyle); protected procedure Changed; virtual; procedure DoChanged; virtual; procedure Notification(AComponent: TComponent; AOperation: TOperation); virtual; property SpellChecker: TdxCustomSpellChecker read FSpellChecker; public constructor Create(ASpellChecker: TdxCustomSpellChecker); virtual; procedure Assign(Source: TPersistent); override; property OnChanged: TdxSpellCheckerCheckAsYouTypeOptionsChangedEvent read FOnChanged write FOnChanged; published property Active: Boolean read FActive write SetActive default False; property ModifyControlPopupMenu: Boolean read FModifyControlPopupMenu write SetModifyControlPopupMenu default True; property PopupMenu: TComponent read FPopupMenu write SetPopupMenu; property PopupMenuItems: TdxSpellCheckerPopupMenuItems read FPopupMenuItems write SetPopupMenuItems default [scmiAddToDictionary, scmiDelete, scmiIgnoreAll, scmiSpelling, scmiSuggestions]; property SuggestionCount: Integer read FSuggestionCount write SetSuggestionCount default 5; property UnderlineColor: TColor read FUnderlineColor write SetUnderlineColor default clRed; property UnderlineStyle: TdxSpellCheckerUnderlineStyle read FUnderlineStyle write SetUnderlineStyle default usAuto; end; { TdxSpellCheckerCustomCheckAsYouTypeManager } TdxSpellCheckerCustomCheckAsYouTypeManager = class(TcxIUnknownObject, IdxSpellCheckerEditHelper) private FSpellChecker: TdxCustomSpellChecker; function GetIsSpellCheckerReady: Boolean; function GetOptions: TdxSpellCheckerCheckAsYouTypeOptions; protected function DoCheckAsYouTypeStart(AControl: TWinControl): Boolean; virtual; function GetCheckMode: TdxSpellCheckerCustomCheckMode; virtual; procedure DoCheckAsYouTypeFinish(AControl: TWinControl); virtual; procedure DoOptionsChanged; virtual; abstract; procedure InnerShowPopupMenu(APopup: TComponent; const P: TPoint); procedure Reset; virtual; procedure SpellingOptionsChanged; virtual; procedure StartManualSpelling(AAdapter: TdxSpellCheckerCustomEditAdapter); virtual; procedure ValidateRules(AParser: TdxSpellCheckerTextParser); virtual; public constructor Create(ASpellChecker: TdxCustomSpellChecker); virtual; procedure BeginManualCheck; virtual; procedure EndManualCheck; virtual; procedure Refresh(AFullRefresh: Boolean = False); virtual; abstract; // IdxSpellCheckerEditHelper procedure CheckAsYouTypeFinish; virtual; abstract; procedure CheckAsYouTypeStart(AControl: TWinControl); virtual; abstract; procedure DoDrawMisspelleds(AControl: TWinControl); virtual; abstract; function IsSpellCheckerDialogControl(AWnd: THandle): Boolean; virtual; abstract; procedure LayoutChanged(AControl: TWinControl); virtual; abstract; function QueryPopup(APopup: TComponent; const P: TPoint): Boolean; virtual; abstract; procedure SelectionChanged(AControl: TWinControl); virtual; abstract; procedure TextChanged(AControl: TWinControl); virtual; abstract; property IsSpellCheckerReady: Boolean read GetIsSpellCheckerReady; property Options: TdxSpellCheckerCheckAsYouTypeOptions read GetOptions; property SpellChecker: TdxCustomSpellChecker read FSpellChecker; end; { TdxCustomSpellChecker } TdxSpellCheckerSpellingFormType = (sftOutlook, sftWord); TdxSpellCheckerEvent = procedure (Sender: TdxCustomSpellChecker) of object; TdxSpellCheckerCheckWordEvent = procedure (Sender: TdxCustomSpellChecker; const AWord: WideString; out AValid: Boolean; var AHandled: Boolean) of object; TdxSpellCheckerAddWordEvent = procedure (AUserDictionary: TdxUserSpellCheckerDictionary; const AWord: WideString; var AHandled: Boolean) of object; TdxSpellCheckerEnabledDictionariesLoadedEvent = procedure (Sender: TdxCustomSpellChecker; const AWord: WideString; var AHandled: Boolean) of object; TdxSpellCheckerSpellingComplete = procedure (Sender: TdxCustomSpellChecker; var AHandled: Boolean) of object; TdxSpellCheckerCheckAsYouTypeEvent = procedure (Sender: TdxCustomSpellChecker; AControl: TWinControl) of object; TdxSpellCheckerCheckAsYouTypePopupEvent = procedure(ASender: TdxCustomSpellChecker; APopupMenu: TComponent; var AHandled: Boolean) of object; TdxSpellCheckerCheckAsYouTypeStartEvent = procedure (Sender: TdxCustomSpellChecker; AControl: TWinControl; var AAllow: Boolean) of object; TdxSpellCheckerCheckControlInContainerEvent = procedure (Sender: TdxCustomSpellChecker; AControl: TWinControl; var AAllow: Boolean; var AContinue: Boolean) of object; TdxSpellCheckerGetSuggestionsEvent = procedure (Sender: TdxCustomSpellChecker; ASuggestions: TdxSpellCheckerSuggestionList) of object; TdxCustomSpellChecker = class(TComponent) private FAutoLoadDictionaries: Boolean; FChangeList: TdxSpellCheckerReplacementList; FCheckAsYouTypeManager: TdxSpellCheckerCustomCheckAsYouTypeManager; FCheckAsYouTypeOptions: TdxSpellCheckerCheckAsYouTypeOptions; FCheckGroupMode: Boolean; FCheckingContainer: Boolean; FCheckMode: TdxSpellCheckerCustomCheckMode; FCreating: Boolean; FDeleteList: TdxSpellCheckerWordList; FDialogLookAndFeel: TcxLookAndFeel; FDictionaryItems: TdxSpellCheckerDictionaries; FIgnoreList: TdxSpellCheckerWordList; FLanguages: TdxSpellCheckerLanguages; FLastDialogResult: Integer; FRules: TdxSpellCheckerRules; FSimilarity: TdxStringSimilarityCalculator; FSpellingFormType: TdxSpellCheckerSpellingFormType; FSpellingOptions: TdxSpellCheckerSpellingOptions; FUseThreadedLoad: Boolean; FOnAddWord: TdxSpellCheckerAddWordEvent; FOnAfterCheck: TdxSpellCheckerEvent; FOnBeforeCheck: TdxSpellCheckerEvent; FOnCheckAsYouTypeFinish: TdxSpellCheckerCheckAsYouTypeEvent; FOnCheckAsYouTypeStart: TdxSpellCheckerCheckAsYouTypeStartEvent; FOnCheckControlInContainer: TdxSpellCheckerCheckControlInContainerEvent; FOnCheckWord: TdxSpellCheckerCheckWordEvent; FOnEnabledDictionariesLoaded: TdxSpellCheckerEvent; FOnGetSuggestions: TdxSpellCheckerGetSuggestionsEvent; FOnCheckAsYouTypePopup: TdxSpellCheckerCheckAsYouTypePopupEvent; FOnSpellingComplete: TdxSpellCheckerSpellingComplete; function GetActiveDictionary( Index: Integer): TdxCustomSpellCheckerDictionary; function GetActiveDictionaryCount: Integer; function GetCheckMode: TdxSpellCheckerCustomCheckMode; function GetDictionary(Index: Integer): TdxCustomSpellCheckerDictionary; function GetDictionaryCount: Integer; function GetEnabledDictionary( Index: Integer): TdxCustomSpellCheckerDictionary; function GetEnabledDictionaryCount: Integer; function GetUserDictionary(Index: Integer): TdxUserSpellCheckerDictionary; function GetUserDictionaryCount: Integer; procedure SetCheckAsYouTypeOptions(AValue: TdxSpellCheckerCheckAsYouTypeOptions); procedure SetDialogLookAndFeel(AValue: TcxLookAndFeel); procedure SetDictionaryItems(AValue: TdxSpellCheckerDictionaries); procedure SetSpellingOptions(AValue: TdxSpellCheckerSpellingOptions); protected procedure AddToIgnoreList(const AWord: WideString); virtual; procedure CheckCallEnabledDictionariesLoaded; function CreateSimilarity: TdxStringSimilarityCalculator; virtual; function CreateSpellingOptions: TdxSpellCheckerSpellingOptions; virtual; function DoAddWord(AUserDictionary: TdxUserSpellCheckerDictionary; const AWord: WideString): Boolean; virtual; procedure DoAfterCheck; virtual; procedure DoBeforeCheck; virtual; procedure DoCheck(ADialogCheckMode: TdxSpellCheckerDialogCheckMode); virtual; procedure DoCheckAsYouTypeFinish(AControl: TWinControl); virtual; function DoCheckAsYouTypePopup(APopup: TComponent): Boolean; virtual; function DoCheckAsYouTypeStart(AControl: TWinControl): Boolean; virtual; procedure DoCheckContainer(AContainer: TWinControl; ARecursive: Boolean); virtual; function DoCheckControlInContainer(AControl: TWinControl; var AContinue: Boolean): Boolean; virtual; function DoCheckWord(const AWord: WideString; var AValid: Boolean): Boolean; virtual; procedure DoEnabledDictionariesLoaded; virtual; procedure DoGetSuggestions(ASuggestions: TdxSpellCheckerSuggestionList); virtual; function DoSpellingComplete: Boolean; virtual; procedure BeginManualCheck; procedure EndManualCheck; function GetDialogCheckModeClass: TdxSpellCheckerCustomCheckModeClass; virtual; procedure InternalCheck(AAdapter: TdxSpellCheckerCustomEditAdapter); virtual; procedure LoadDictionariesDirect(AIgnoreDisabled: Boolean = True); virtual; procedure LoadDictionariesUsingThread(AIgnoreDisabled: Boolean = True); virtual; procedure Loaded; override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure SpellingComplete; procedure SpellingOptionsChanged; virtual; procedure ValidateRules(AParser: TdxSpellCheckerTextParser); virtual; procedure UpdateByDictionary(ADictionary: TdxCustomSpellCheckerDictionary); virtual; procedure UpdateCheckAsYouTypeManagerState; virtual; property ChangeList: TdxSpellCheckerReplacementList read FChangeList; property CheckAsYouTypeManager: TdxSpellCheckerCustomCheckAsYouTypeManager read FCheckAsYouTypeManager; property CheckingContainer: Boolean read FCheckingContainer; property DeleteList: TdxSpellCheckerWordList read FDeleteList; property IgnoreList: TdxSpellCheckerWordList read FIgnoreList; property Languages: TdxSpellCheckerLanguages read FLanguages; property OnAddWord: TdxSpellCheckerAddWordEvent read FOnAddWord write FOnAddWord; property OnAfterCheck: TdxSpellCheckerEvent read FOnAfterCheck write FOnAfterCheck; property OnBeforeCheck: TdxSpellCheckerEvent read FOnBeforeCheck write FOnBeforeCheck; property OnCheckAsYouTypeFinish: TdxSpellCheckerCheckAsYouTypeEvent read FOnCheckAsYouTypeFinish write FOnCheckAsYouTypeFinish; property OnCheckAsYouTypePopup: TdxSpellCheckerCheckAsYouTypePopupEvent read FOnCheckAsYouTypePopup write FOnCheckAsYouTypePopup; property OnCheckAsYouTypeStart: TdxSpellCheckerCheckAsYouTypeStartEvent read FOnCheckAsYouTypeStart write FOnCheckAsYouTypeStart; property OnCheckControlInContainer: TdxSpellCheckerCheckControlInContainerEvent read FOnCheckControlInContainer write FOnCheckControlInContainer; property OnCheckWord: TdxSpellCheckerCheckWordEvent read FOnCheckWord write FOnCheckWord; property OnEnabledDictionariesLoaded: TdxSpellCheckerEvent read FOnEnabledDictionariesLoaded write FOnEnabledDictionariesLoaded; property OnGetSuggestions: TdxSpellCheckerGetSuggestionsEvent read FOnGetSuggestions write FOnGetSuggestions; property OnSpellingComplete: TdxSpellCheckerSpellingComplete read FOnSpellingComplete write FOnSpellingComplete; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Check(AEdit: TCustomEdit); overload; procedure Check(AEdit: TcxCustomTextEdit); overload; procedure CheckContainer(AContainer: TWinControl; ARecursive: Boolean); procedure AddWordToUserDictionary(const AWord: WideString); function FindDictionaryByWord(const AWord: WideString): TdxCustomSpellCheckerDictionary; function FindFirstEnabledUserDictionary: TdxUserSpellCheckerDictionary; function GetSuggestions(const AWord: WideString): TdxSpellCheckerSuggestionList; function HasWordInDictionaries(const AWord: WideString): Boolean; function HasEnabledUserDictionary: Boolean; function IsValidWord(const AWord: WideString): Boolean; procedure PopulateLanguages(AList: TStrings); procedure ShowSpellingCompleteMessage; virtual; procedure LoadDictionaries(AIgnoreDisabled: Boolean = True); procedure UnloadDictionaries; function GetTwoWordsDistance(const AWord1, AWord2: WideString): Integer; property ActiveDictionaries[Index: Integer]: TdxCustomSpellCheckerDictionary read GetActiveDictionary; property ActiveDictionaryCount: Integer read GetActiveDictionaryCount; property AutoLoadDictionaries: Boolean read FAutoLoadDictionaries write FAutoLoadDictionaries default False; property CheckMode: TdxSpellCheckerCustomCheckMode read GetCheckMode; property CheckAsYouTypeOptions: TdxSpellCheckerCheckAsYouTypeOptions read FCheckAsYouTypeOptions write SetCheckAsYouTypeOptions; property DialogLookAndFeel: TcxLookAndFeel read FDialogLookAndFeel write SetDialogLookAndFeel; property Dictionaries[Index: Integer]: TdxCustomSpellCheckerDictionary read GetDictionary; property DictionaryCount: Integer read GetDictionaryCount; property DictionaryItems: TdxSpellCheckerDictionaries read FDictionaryItems write SetDictionaryItems; property EnabledDictionaries[Index: Integer]: TdxCustomSpellCheckerDictionary read GetEnabledDictionary; property EnabledDictionaryCount: Integer read GetEnabledDictionaryCount; property Rules: TdxSpellCheckerRules read FRules; property SpellingFormType: TdxSpellCheckerSpellingFormType read FSpellingFormType write FSpellingFormType default sftOutlook; property SpellingOptions: TdxSpellCheckerSpellingOptions read FSpellingOptions write SetSpellingOptions; property UserDictionaries[Index: Integer]: TdxUserSpellCheckerDictionary read GetUserDictionary; property UserDictionaryCount: Integer read GetUserDictionaryCount; property UseThreadedLoad: Boolean read FUseThreadedLoad write FUseThreadedLoad default False; end; { TdxSpellChecker } TdxSpellChecker = class(TdxCustomSpellChecker) published property AutoLoadDictionaries; property CheckAsYouTypeOptions; property DialogLookAndFeel; property DictionaryItems; property SpellingFormType; property SpellingOptions; property UseThreadedLoad; property OnAddWord; property OnAfterCheck; property OnBeforeCheck; property OnCheckAsYouTypeFinish; property OnCheckAsYouTypePopup; property OnCheckAsYouTypeStart; property OnCheckControlInContainer; property OnCheckWord; property OnEnabledDictionariesLoaded; property OnGetSuggestions; property OnSpellingComplete; end; function GetRegisteredDictionaryTypes: TcxRegisteredClasses; implementation uses Dialogs, cxGeometry, dxOffice11, dxSpellCheckerStrs, dxSpellCheckerUtils, dxSpellCheckerDialogs, dxISpellDecompressor, dxSpellCheckerCheckAsYouType, cxContainer, cxEdit; var FRegisteredDictionaryTypes: TcxRegisteredClasses; function GetRegisteredDictionaryTypes: TcxRegisteredClasses; begin if FRegisteredDictionaryTypes = nil then begin FRegisteredDictionaryTypes := TcxRegisteredClasses.Create; FRegisteredDictionaryTypes.Sorted := True; end; Result := FRegisteredDictionaryTypes; end; procedure FreeRegisteredDictionaryTypes; begin FreeAndNil(FRegisteredDictionaryTypes); end; { TdxSpellCheckerLanguages } function TdxSpellCheckerLanguages.GetDefaultLanguageLCID: DWORD; begin Result := GetSystemDefaultLCID; end; function TdxSpellCheckerLanguages.GetLCID(const AName: string): DWORD; var I: Integer; begin Result := 0; for I := 0 to Count - 1 do begin if AnsiCompareText(AName, Name[I]) = 0 then begin Result := LocaleID[I]; Break; end; end; end; { TdxSpellCheckerSuggestion } constructor TdxSpellCheckerSuggestion.Create(const AWord: WideString; ADictionary: TdxCustomSpellCheckerDictionary; ADistance: Integer); begin inherited Create; FDictionary := ADictionary; FWord := AWord; FDistance := ADistance; end; { TdxSpellCheckerSuggestionList } procedure TdxSpellCheckerSuggestionList.Add(const AWord: WideString; ADictionary: TdxCustomSpellCheckerDictionary; ADistance: Integer); begin inherited Add(TdxSpellCheckerSuggestion.Create(AWord, ADictionary, ADistance)); end; procedure TdxSpellCheckerSuggestionList.Delete(AIndex: Integer); begin Items[AIndex].Free; inherited Delete(AIndex); end; procedure TdxSpellCheckerSuggestionList.FixCapitalization(const AMask: WideString); var I: Integer; begin if Length(AMask) = 0 then Exit; case GetWordCapitalizationType(AMask) of ctUpper: for I := 0 to Count - 1 do with Items[I] do FWord := WideUpperCase(FWord); ctCapitalized: for I := 0 to Count - 1 do with Items[I] do if Length(FWord) > 0 then FWord[1] := WideUpperCase(FWord[1])[1]; end; end; procedure TdxSpellCheckerSuggestionList.RemoveDuplicates; var I: Integer; begin SortByWord; I := 1; while I < Count do begin while (I < Count) and WideSameText(Items[I].Word, Items[I - 1].Word) do begin Items[I].Free; Delete(I); end; Inc(I); end; SortByDistance; end; procedure TdxSpellCheckerSuggestionList.SaveToStrings(AStrings: TStrings); var I: Integer; begin AStrings.BeginUpdate; try AStrings.Clear; for I := 0 to Count - 1 do AStrings.AddObject(Items[I].Word, Pointer(Items[I].Distance)); finally AStrings.EndUpdate; end; end; function CompareSuggestionsByDistance(Item1, Item2: Pointer): Integer; begin Result := TdxSpellCheckerSuggestion(Item1).Distance - TdxSpellCheckerSuggestion(Item2).Distance; end; procedure TdxSpellCheckerSuggestionList.SortByDistance; begin Sort(CompareSuggestionsByDistance); end; function CompareSuggestionsByWord(Item1, Item2: Pointer): Integer; begin Result := WideCompareText(TdxSpellCheckerSuggestion(Item1).Word, TdxSpellCheckerSuggestion(Item2).Word); end; procedure TdxSpellCheckerSuggestionList.SortByWord; begin Sort(CompareSuggestionsByWord); end; function TdxSpellCheckerSuggestionList.GetItem( Index: Integer): TdxSpellCheckerSuggestion; begin Result := TdxSpellCheckerSuggestion(inherited Items[Index]); end; { TdxSpellCheckerReplacementItem } constructor TdxSpellCheckerReplacementItem.Create( const AText, AReplacement: WideString); begin inherited Create; FText := AText; FReplacement := AReplacement; end; { TdxSpellCheckerReplacementList } constructor TdxSpellCheckerReplacementList.Create(AllowDuplicates: Boolean = False); begin inherited Create; Capacity := 256; FAllowDuplicates := AllowDuplicates; end; procedure TdxSpellCheckerReplacementList.Add( const AText, AReplacement: WideString); var AItem: TdxSpellCheckerReplacementItem; begin if FAllowDuplicates then inherited Add(TdxSpellCheckerReplacementItem.Create(AText, AReplacement)) else begin AItem := FindReplacement(AText); if AItem = nil then begin inherited Add(TdxSpellCheckerReplacementItem.Create(AText, AReplacement)); SortByText; end else AItem.FReplacement := AReplacement; end; end; function TdxSpellCheckerReplacementList.FindReplacement( const AText: WideString): TdxSpellCheckerReplacementItem; var L, H, I, C: Integer; begin Result := nil; L := 0; H := Count - 1; while L <= H do begin I := (L + H) shr 1; C := WideCompareText(TdxSpellCheckerReplacementItem(List^[I]).Text, AText); if C < 0 then L := I + 1 else begin H := I - 1; if C = 0 then begin Result := TdxSpellCheckerReplacementItem(List^[I]); Break; end; end; end; end; function TdxSpellCheckerReplacementList.GetItem( AIndex: Integer): TdxSpellCheckerReplacementItem; begin Result := TdxSpellCheckerReplacementItem(inherited Items[AIndex]); end; function CompareReplacementByText(Item1, Item2: Pointer): Integer; begin Result := WideCompareText(TdxSpellCheckerReplacementItem(Item1).Text, TdxSpellCheckerReplacementItem(Item2).Text); end; procedure TdxSpellCheckerReplacementList.SortByText; begin Sort(CompareReplacementByText); end; { TdxSpellCheckerCustomEditAdapter } constructor TdxSpellCheckerCustomEditAdapter.Create(AEdit: TWinControl); begin FEdit := AEdit; end; procedure TdxSpellCheckerCustomEditAdapter.GetSpellingBounds( out ASpellingStart, ASpellingEnd: Integer); begin ASpellingStart := 1; if EditorHandleAllocated then ASpellingEnd := SendMessageW(EditorHandle, WM_GETTEXTLENGTH, 0, 0) + 1 else ASpellingEnd := Length(FText); end; function TdxSpellCheckerCustomEditAdapter.EditorHandleAllocated: Boolean; begin Result := (FEdit <> nil) and FEdit.HandleAllocated end; function TdxSpellCheckerCustomEditAdapter.GetEditorHandle: THandle; begin if EditorHandleAllocated then Result := FEdit.Handle else Result := 0; end; function TdxSpellCheckerCustomEditAdapter.GetText: WideString; var ALen: Integer; begin if EditorHandleAllocated then begin ALen := SendMessageW(EditorHandle, WM_GETTEXTLENGTH, 0, 0) + 1; SetLength(Result, ALen); GetWindowTextW(EditorHandle, Pointer(Result), ALen); FText := Result; end else Result := FText; end; procedure TdxSpellCheckerCustomEditAdapter.Post(AUpdateValue: Boolean = True); var AEdit: IdxSpellCheckerSupport; begin if Supports(GetInnerControlContainer(FEdit), IdxSpellCheckerSupport, AEdit) and AUpdateValue and not EditorHandleAllocated then begin FText := PWideChar(FText); AEdit.SetValue(FText); end; end; function TdxSpellCheckerCustomEditAdapter.IsInplace: Boolean; var AContainer: TcxCustomEdit; begin AContainer := TcxCustomEdit(GetInnerControlContainer(FEdit)); Result := (AContainer <> nil) and AContainer.IsInplace; end; { TdxSpellCheckercxTextEditAdapter } procedure TdxSpellCheckercxTextEditAdapter.ClearSelection; begin if EditorHandleAllocated then Edit.ClearSelection else SelText := ''; end; function TdxSpellCheckercxTextEditAdapter.GetHideSelection: Boolean; begin Result := Edit.Properties.HideSelection; end; function TdxSpellCheckercxTextEditAdapter.GetReadOnly: Boolean; begin Result := Edit.Properties.ReadOnly; end; function TdxSpellCheckercxTextEditAdapter.GetSelLength: Integer; begin Result := Edit.SelLength; end; function TdxSpellCheckercxTextEditAdapter.GetSelStart: Integer; begin Result := Edit.SelStart end; function TdxSpellCheckercxTextEditAdapter.GetSelText: string; begin Result := Edit.SelText end; procedure TdxSpellCheckercxTextEditAdapter.SetHideSelection(AValue: Boolean); begin Edit.Properties.HideSelection := AValue end; procedure TdxSpellCheckercxTextEditAdapter.SetSelLength(AValue: Integer); begin Edit.SelLength := AValue; end; procedure TdxSpellCheckercxTextEditAdapter.SetSelStart(AValue: Integer); begin Edit.SelStart := AValue; end; procedure TdxSpellCheckercxTextEditAdapter.SetSelText(const AValue: string); begin Edit.SelText := AValue; end; function TdxSpellCheckercxTextEditAdapter.GetEdit: TcxCustomTextEdit; begin Result := TcxCustomTextEdit(FEdit); end; { TdxSpellCheckercxRichEditAdapter } function TdxSpellCheckercxRichEditAdapter.GetReadOnly: Boolean; begin Result := Edit.ActiveProperties.ReadOnly; end; function TdxSpellCheckercxRichEditAdapter.GetText: WideString; var ASource, ADest: WideString; ALen: Integer; begin ASource := inherited GetText; ALen := Length(ASource); SetLength(ADest, ALen); ALen := AdjustRichLineBreaksW(PWideChar(ADest), PWideChar(ASource), Edit.RichVersion >= 200); SetLength(ADest, ALen); Result := ADest; end; function TdxSpellCheckercxRichEditAdapter.GetEdit: TcxCustomRichEdit; begin Result := TcxCustomRichEdit(inherited Edit); end; { TdxSpellCheckerEditAdapter } procedure TdxSpellCheckerEditAdapter.ClearSelection; begin SelText := ''; end; function TdxSpellCheckerEditAdapter.GetHideSelection: Boolean; begin if FEdit is TMemo then Result := TMemo(FEdit).HideSelection else if FEdit is TEdit then Result := TEdit(FEdit).HideSelection else Result := False; end; function TdxSpellCheckerEditAdapter.GetReadOnly: Boolean; begin Result := TEdit(FEdit).ReadOnly; end; function TdxSpellCheckerEditAdapter.GetSelLength: Integer; begin if EditorHandleAllocated then Result := Edit.SelLength else Result := FSelLength; end; function TdxSpellCheckerEditAdapter.GetSelStart: Integer; begin if EditorHandleAllocated then Result := Edit.SelStart else Result := FSelStart; end; function TdxSpellCheckerEditAdapter.GetSelText: string; begin if EditorHandleAllocated then Result := Edit.SelText else Result := Copy(FText, FSelStart, FSelLength); end; procedure TdxSpellCheckerEditAdapter.SetHideSelection(AValue: Boolean); begin if FEdit is TMemo then TMemo(FEdit).HideSelection := AValue else if FEdit is TEdit then TEdit(FEdit).HideSelection := AValue; end; procedure TdxSpellCheckerEditAdapter.SetSelLength(AValue: Integer); begin if EditorHandleAllocated then Edit.SelLength := AValue; FSelLength := AValue; end; procedure TdxSpellCheckerEditAdapter.SetSelStart(AValue: Integer); begin if EditorHandleAllocated then Edit.SelStart := AValue; FSelStart := AValue; end; procedure TdxSpellCheckerEditAdapter.SetSelText(const AValue: string); var AContainer: TWinControl; AIntf: IdxSpellCheckerSupport; begin FText := Copy(FText, 1, FSelStart) + AValue + Copy(FText, FSelStart + FSelLength + 1, Length(FText) - FSelStart - FSelLength); if EditorHandleAllocated then begin AContainer := GetInnerControlContainer(Edit); if Supports(TObject(AContainer), IdxSpellCheckerSupport, AIntf) then AIntf.SetSelText(AValue) else Edit.SelText := AValue; end; end; function TdxSpellCheckerEditAdapter.GetEdit: TCustomEdit; begin Result := TCustomEdit(FEdit); end; { TdxSpellCheckerTextParser } constructor TdxSpellCheckerTextParser.Create(ASpellChecker: TdxCustomSpellChecker); begin inherited Create; FSpellChecker := ASpellChecker; FDelimiters := #0#9#10#11#13#32'.,<>=!?:;"()[]{}+|-/\'; FUrlDelimiters := #0#9#10#11#13#32',<>!;()[]{}|'; FText := #0; end; destructor TdxSpellCheckerTextParser.Destroy; begin inherited Destroy; end; function TdxSpellCheckerTextParser.GetNextWord(var AStart, ALength: Integer; out ADelimiters: WideString): WideString; procedure SkipFirstDelimiters; begin if (AStart <= FSpellingEnd) and WideIsSpace(FText[AStart]) then while (AStart < FSpellingEnd) and WideIsSpace(FText[AStart]) do begin ADelimiters := ADelimiters + FText[AStart]; Inc(AStart); end else while (AStart < FSpellingEnd) and not WideIsAlphaNumeric(FText[AStart]) do begin ADelimiters := ADelimiters + FText[AStart]; Inc(AStart); end; end; var APos: Integer; APreDelimiters: WideString; begin ADelimiters := ''; SkipFirstDelimiters; ALength := 0; APos := AStart; while (APos < FSpellingEnd) and not WideIsSpace(FText[APos]) do Inc(APos); ALength := APos - AStart; if ALength = 0 then Result := '' else begin Result := Copy(FText, AStart, ALength); if LastWideCharIsSpace(ADelimiters) and IsUrl(Result) then begin while (APos > AStart) and IsUrlDelimiter(FText[APos]) do Dec(APos); ALength := APos - AStart + 1; Result := Copy(FText, AStart, ALength); end else begin while (AStart < FSpellingEnd) and IsDelimiter(FText[AStart]) do begin ADelimiters := ADelimiters + FText[AStart]; Inc(AStart); end; ALength := 0; APos := AStart; while (APos < FSpellingEnd) and not IsDelimiter(FText[APos]) do Inc(APos); ALength := APos - AStart; Result := Copy(FText, AStart, ALength); if not WideStringContainsAlpha(Result) then begin APreDelimiters := ADelimiters + Result; Inc(AStart, ALength); Result := GetNextWord(AStart, ALength, ADelimiters); ADelimiters := APreDelimiters + ADelimiters; end; end; end; end; function TdxSpellCheckerTextParser.GetPrevWord: WideString; begin Result := FPrevWord; end; function TdxSpellCheckerTextParser.IsDelimiter(AChar: WideChar): Boolean; begin Result := WideCharPos(AChar, FDelimiters) > 0; end; function TdxSpellCheckerTextParser.IsUrlDelimiter(AChar: WideChar): Boolean; begin Result := WideCharPos(AChar, FUrlDelimiters) > 0; end; function TdxSpellCheckerTextParser.WordExists(const AWord: WideString): Boolean; begin Result := SpellChecker.HasWordInDictionaries(AWord); end; function TdxSpellCheckerTextParser.GetSentence(APos: Integer): TdxSpellCheckerSentence; function IsSentenceDelimiters(ACh: WideChar): Boolean; begin Result := (ACh = '.') or (WideIsSpace(ACh) and (ACh <> ' ')); end; function GetSentenceText(var AStartPos: Integer): WideString; var AEndPos: Integer; ALength: Integer; begin AEndPos := AStartPos; while (AStartPos > 0) and not IsSentenceDelimiters(Text[AStartPos]) do Dec(AStartPos); while (AStartPos > 0) and (IsSentenceDelimiters(Text[AStartPos]) or (Text[AStartPos] = ' ')) do Inc(AStartPos); ALength := Length(FText); while (AEndPos <= ALength) and (not IsSentenceDelimiters(Text[AEndPos]) or (Text[AEndPos] = '.')) do Inc(AEndPos); Result := Copy(Text, AStartPos, AEndPos - AStartPos); end; begin with Result do begin StartTextPosition := APos; Text := GetSentenceText(StartTextPosition); if StartTextPosition = 0 then Inc(StartTextPosition); StartWordPosition := APos - StartTextPosition; end; end; function TdxSpellCheckerTextParser.GetNextDelimiterPosition(var AStart: Integer; ADirection: Integer): Boolean; begin while (AStart <= FSpellingEnd) and (AStart >= FSpellingStart) and not WideIsSpace(FText[AStart]) do Inc(AStart, ADirection); Result := WideIsSpace(FText[AStart]); if Result and (ADirection < 0) then Inc(AStart, ADirection); end; function TdxSpellCheckerTextParser.GetSuggestions( const AWord: WideString): TdxSpellCheckerSuggestionList; begin Result := SpellChecker.GetSuggestions(AWord); end; procedure TdxSpellCheckerTextParser.UpdateTextInfo(AAdapter: TdxSpellCheckerCustomEditAdapter); begin AAdapter.GetSpellingBounds(FSpellingStart, FSpellingEnd); FText := AAdapter.Text; end; function TdxSpellCheckerTextParser.LastWideCharIsSpace(const S: WideString): Boolean; var I: Integer; begin I := Length(S); Result := (I > 0) and WideIsSpace(S[I]); end; { TdxSpellCheckerUndoItem } constructor TdxSpellCheckerUndoItem.Create(const AMisspelledWord, AReplacement, APrevWord: WideString; AMisspelledWordPosition: Integer); begin FMisspelledWord := AMisspelledWord; FReplacement := AReplacement; FPrevWord := APrevWord; FMisspelledWordPosition := AMisspelledWordPosition; end; { TdxSpellCheckerUndoManager } constructor TdxSpellCheckerUndoManager.Create; begin FUndoList := TcxObjectList.Create; end; destructor TdxSpellCheckerUndoManager.Destroy; begin FreeAndNil(FUndoList); inherited Destroy; end; procedure TdxSpellCheckerUndoManager.Add(const AMisspelledWord, AReplacement, APrevWord: WideString; AMisspelledWordPosition: Integer); begin FUndoList.Add(TdxSpellCheckerUndoItem.Create(AMisspelledWord, AReplacement, APrevWord, AMisspelledWordPosition)); end; procedure TdxSpellCheckerUndoManager.UndoLast; begin if Count > 0 then begin Last.Free; FUndoList.Delete(Count - 1); end; end; function TdxSpellCheckerUndoManager.GetCount: Integer; begin Result := FUndoList.Count; end; function TdxSpellCheckerUndoManager.GetItem(AIndex: Integer): TdxSpellCheckerUndoItem; begin Result := TdxSpellCheckerUndoItem(FUndoList[AIndex]); end; function TdxSpellCheckerUndoManager.GetLast: TdxSpellCheckerUndoItem; begin if Count > 0 then Result := Items[Count - 1] else Result := nil; end; { TdxSpellCheckerCustomCheckAsYouTypeManager } constructor TdxSpellCheckerCustomCheckAsYouTypeManager.Create( ASpellChecker: TdxCustomSpellChecker); begin FSpellChecker := ASpellChecker; end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.BeginManualCheck; begin end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.EndManualCheck; begin end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.DoCheckAsYouTypeFinish( AControl: TWinControl); begin SpellChecker.DoCheckAsYouTypeFinish(AControl); end; function TdxSpellCheckerCustomCheckAsYouTypeManager.DoCheckAsYouTypeStart( AControl: TWinControl): Boolean; begin Result := SpellChecker.DoCheckAsYouTypeStart(AControl); end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.InnerShowPopupMenu(APopup: TComponent; const P: TPoint); begin if not SpellChecker.DoCheckAsYouTypePopup(APopup) then ShowPopupMenu(nil, APopup, P.X, P.Y); end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.Reset; begin end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.SpellingOptionsChanged; begin if GetCheckMode <> nil then SpellChecker.ValidateRules(GetCheckMode.Parser); end; function TdxSpellCheckerCustomCheckAsYouTypeManager.GetIsSpellCheckerReady: Boolean; begin Result := SpellChecker.ActiveDictionaryCount > 0; end; function TdxSpellCheckerCustomCheckAsYouTypeManager.GetOptions: TdxSpellCheckerCheckAsYouTypeOptions; begin Result := SpellChecker.CheckAsYouTypeOptions; end; function TdxSpellCheckerCustomCheckAsYouTypeManager.GetCheckMode: TdxSpellCheckerCustomCheckMode; begin Result := nil; end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.StartManualSpelling( AAdapter: TdxSpellCheckerCustomEditAdapter); begin SpellChecker.InternalCheck(AAdapter); end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.ValidateRules( AParser: TdxSpellCheckerTextParser); begin SpellChecker.ValidateRules(AParser); end; { TdxSpellCheckerCustomCheckMode } constructor TdxSpellCheckerCustomCheckMode.Create(AOwner: TdxCustomSpellChecker; AAdapter: TdxSpellCheckerCustomEditAdapter); begin FSpellChecker := AOwner; FAdapter := AAdapter; SaveSelection; CreateParser; FMisspelledStart := SpellingStart; end; destructor TdxSpellCheckerCustomCheckMode.Destroy; begin FreeAndNil(FParser); inherited Destroy; end; procedure TdxSpellCheckerCustomCheckMode.Add; begin if SpellChecker.HasEnabledUserDictionary then SpellChecker.AddWordToUserDictionary(FMisspelledWord); end; function TdxSpellCheckerCustomCheckMode.CanUndo: Boolean; begin Result := False; end; procedure TdxSpellCheckerCustomCheckMode.Change(const AWord: WideString); var ADelta: Integer; begin SelectMisspelledWord; ADelta := Length(AWord) - Length(Adapter.SelText); Adapter.SelText := AWord; PrevWord := AWord; UpdateSpellingBounds(ADelta); UpdateTextInfo; Inc(FMisspelledStart, Length(AWord)); end; procedure TdxSpellCheckerCustomCheckMode.ChangeAll(const AWord: WideString); begin SpellChecker.ChangeList.Add(MisspelledWord, AWord); Change(AWord); end; function TdxSpellCheckerCustomCheckMode.CheckWord( const AWord: WideString): TdxSpellCheckerSpellingCode; begin if not IsValidWord(AWord) then begin FLastCode := SpellChecker.Rules.ErrorCode; FMisspelledWord := AWord; end else FLastCode := scNoError; Result := FLastCode; end; procedure TdxSpellCheckerCustomCheckMode.Delete; begin Inc(FMisspelledLen); Dec(FMisspelledStart); SelectMisspelledWord; Adapter.ClearSelection; UpdateSpellingBounds(-FMisspelledLen); UpdateTextInfo; end; procedure TdxSpellCheckerCustomCheckMode.DeleteAll; begin SpellChecker.DeleteList.Add(MisspelledWord); Delete; end; function TdxSpellCheckerCustomCheckMode.GetNextWord(out AWord: WideString): Boolean; var ADelimetrs: WideString; begin AWord := Parser.GetNextWord(FMisspelledStart, FMisspelledLen, ADelimetrs); if AWord = '' then begin ValidateSpellingBounds; AWord := Parser.GetNextWord(FMisspelledStart, FMisspelledLen, ADelimetrs); end; if (Length(ADelimetrs) <> GetWideCharCount(' ', ADelimetrs)) then // todo: PrevWord := ''; Result := FMisspelledLen > 0; end; function TdxSpellCheckerCustomCheckMode.GetNextMisspelledWord: Boolean; var AWord: WideString; begin FMisspelledWord := ''; while GetNextWord(AWord) do begin if InternalProcessWord(AWord) then Continue; if CheckWord(AWord) <> scNoError then Break; PrevWord := AWord; Inc(FMisspelledStart, FMisspelledLen); end; if FMisspelledLen = 0 then FLastCode := scNoError else SelectMisspelledWord; Result := FLastCode <> scNoError; end; function TdxSpellCheckerCustomCheckMode.GetSuggestions( const AWord: WideString): TdxSpellCheckerSuggestionList; begin Result := Parser.GetSuggestions(AWord); end; function TdxSpellCheckerCustomCheckMode.InternalProcessWord( const AWord: WideString): Boolean; var AReplacement: WideString; begin Result := True; if IsNeedIgnoreWord(AWord) then begin FMisspelledWord := AWord; Ignore; end else if IsNeedChangeWord(AWord, AReplacement) then begin Change(AReplacement); PrevWord := AReplacement; end else begin if IsNeedDeleteWord(AWord) then Delete else Result := False; end; end; procedure TdxSpellCheckerCustomCheckMode.Ignore; begin Inc(FMisspelledStart, FMisspelledLen); end; procedure TdxSpellCheckerCustomCheckMode.IgnoreAll; begin SpellChecker.AddToIgnoreList(MisspelledWord); Ignore; end; function TdxSpellCheckerCustomCheckMode.IsValidWord( const AWord: WideString): Boolean; begin Result := SpellChecker.IsValidWord(AWord); end; procedure TdxSpellCheckerCustomCheckMode.UndoLast; begin end; procedure TdxSpellCheckerCustomCheckMode.CreateParser; begin if FParser = nil then FParser := GetParserClass.Create(SpellChecker); Adapter.GetSpellingBounds(FSpellingStart, FSpellingEnd); Dec(FSpellingStart); Dec(FSpellingEnd); Parser.UpdateTextInfo(Adapter); end; function TdxSpellCheckerCustomCheckMode.GetOwner: TPersistent; begin Result := FSpellChecker; end; function TdxSpellCheckerCustomCheckMode.GetParserClass: TdxSpellCheckerTextParserClass; begin Result := TdxSpellCheckerTextParser; end; function TdxSpellCheckerCustomCheckMode.IsNeedChangeWord(const AWord: WideString; out AReplacement: WideString): Boolean; var AItem: TdxSpellCheckerReplacementItem; begin AItem := SpellChecker.ChangeList.FindReplacement(AWord); Result := Assigned(AItem); if Result then AReplacement := AItem.Replacement; end; function TdxSpellCheckerCustomCheckMode.IsNeedDeleteWord(const AWord: WideString): Boolean; begin Result := SpellChecker.DeleteList.Find(AWord); end; function TdxSpellCheckerCustomCheckMode.IsNeedIgnoreWord(const AWord: WideString): Boolean; begin Result := SpellChecker.IgnoreList.Find(AWord); end; procedure TdxSpellCheckerCustomCheckMode.SaveSelection; begin end; procedure TdxSpellCheckerCustomCheckMode.SelectMisspelledWord; begin if FMisspelledLen > 0 then Adapter.SelStart := FMisspelledStart - 1; Adapter.SelLength := FMisspelledLen; end; procedure TdxSpellCheckerCustomCheckMode.SelectMisspelledWordAfterUndo; begin SelectMisspelledWord; end; procedure TdxSpellCheckerCustomCheckMode.UpdateByDictionary( ADictionary: TdxCustomSpellCheckerDictionary); begin end; procedure TdxSpellCheckerCustomCheckMode.UpdateSpellingBounds(ADelta: Integer); begin if FMisspelledStart < FSpellingStart then Inc(FSpellingStart, ADelta); if FMisspelledStart < FSpellingEnd then Inc(FSpellingEnd, ADelta); ValidateSpellingBounds; end; procedure TdxSpellCheckerCustomCheckMode.UpdateTextInfo; begin Parser.UpdateTextInfo(Adapter); FParser.FSpellingStart := SpellingStart; FParser.FSpellingEnd := SpellingEnd; end; procedure TdxSpellCheckerCustomCheckMode.ValidateSpellingBounds; begin end; function TdxSpellCheckerCustomCheckMode.GetPrevWord: WideString; begin Result := Parser.FPrevWord; end; procedure TdxSpellCheckerCustomCheckMode.SetPrevWord(const AValue: WideString); begin Parser.FPrevWord := AValue; end; procedure TdxSpellCheckerCustomCheckMode.SetSpellingEnd( const AValue: Integer); begin FSpellingEnd := AValue; end; procedure TdxSpellCheckerCustomCheckMode.SetSpellingStart( const AValue: Integer); begin FSpellingStart := AValue; end; { TdxSpellCheckerDialogCheckMode } constructor TdxSpellCheckerDialogCheckMode.Create(AOwner: TdxCustomSpellChecker; AAdapter: TdxSpellCheckerCustomEditAdapter); begin inherited Create(AOwner, AAdapter); FUndoManager := TdxSpellCheckerUndoManager.Create; FCheckedSelection := False; end; destructor TdxSpellCheckerDialogCheckMode.Destroy; begin FreeAndNil(FUndoManager); Adapter.HideSelection := FSaveHideSelection; Adapter.SelStart := FSaveSelStart; Adapter.SelLength := FSaveSelLength; inherited Destroy; end; function TdxSpellCheckerDialogCheckMode.CanUndo: Boolean; begin Result := FUndoManager.Count > 0; end; procedure TdxSpellCheckerDialogCheckMode.Change(const AWord: WideString); begin FUndoManager.Add(MisspelledWord, AWord, '', FMisspelledStart - 1); inherited Change(AWord); end; procedure TdxSpellCheckerDialogCheckMode.CreateParser; begin inherited CreateParser; if not SpellChecker.CheckingContainer then begin if SpellChecker.SpellingOptions.CheckFromCursorPos or IsCheckingSelectedText then begin SpellingStart := FSaveSelStart; Parser.GetNextDelimiterPosition(FSpellingStart, -1); end; if IsCheckingSelectedText then begin SpellingEnd := FSaveSelStart + FSaveSelLength; Parser.GetNextDelimiterPosition(FSpellingEnd, 1); end; end; Inc(FSpellingStart); Inc(FSpellingEnd); UpdateTextInfo; end; procedure TdxSpellCheckerDialogCheckMode.Delete; begin FUndoManager.Add(' ' + MisspelledWord, '', PrevWord, FMisspelledStart - 2); inherited Delete; end; procedure TdxSpellCheckerDialogCheckMode.Ignore; begin FUndoManager.Add(MisspelledWord, MisspelledWord, PrevWord, FMisspelledStart - 1); inherited Ignore; end; function TdxSpellCheckerDialogCheckMode.ShowDialog: Integer; begin if not Adapter.EditorHandleAllocated then Adapter.Post; Result := 0; end; function TdxSpellCheckerDialogCheckMode.IsCheckingSelectedText: Boolean; begin Result := SpellChecker.SpellingOptions.CheckSelectedTextFirst and (FSaveSelLength > 0) and not FCheckedSelection; end; procedure TdxSpellCheckerDialogCheckMode.UndoLast; var AItem: TdxSpellCheckerUndoItem; begin AItem := FUndoManager.Last; if CanUndo and (AItem <> nil) then begin Adapter.SelStart := AItem.MisspelledWordPosition; Adapter.SelLength := Length(AItem.Replacement); UpdateSpellingBounds(Length(AItem.MisspelledWord) - Adapter.SelLength); Adapter.SelText := AItem.MisspelledWord; UpdateTextInfo; SelectMisspelledWordAfterUndo; ValidateSpellingBoundsAfterUndo; PrevWord := AItem.PrevWord; FUndoManager.UndoLast; end; end; procedure TdxSpellCheckerDialogCheckMode.SaveSelection; begin FSaveHideSelection := Adapter.HideSelection; FSaveSelLength := Adapter.SelLength; FSaveSelStart := Adapter.SelStart; Adapter.HideSelection := False; end; procedure TdxSpellCheckerDialogCheckMode.ValidateSpellingBounds; procedure SetBeforeSelectionBounds; begin SpellingEnd := SpellingStart; SpellingStart := 1; FMisspelledStart := 1; end; var ASpellingStart, ASpellingEnd: Integer; begin if (SpellChecker.SpellingOptions.CheckFromCursorPos or IsCheckingSelectedText) and not SpellChecker.CheckingContainer then begin Adapter.GetSpellingBounds(ASpellingStart, ASpellingEnd); if ((FMisspelledStart >= ASpellingEnd) or (FMisspelledStart < SpellingStart)) and (SpellingStart > 1) and not IsCheckingSelectedText then begin SetBeforeSelectionBounds; UpdateTextInfo; end else if (FMisspelledStart >= SpellingEnd) and IsCheckingSelectedText and (ASpellingStart = SpellingStart) and (ASpellingEnd = SpellingEnd) and (MessageDlg(cxGetResourceString(@sdxSpellCheckerSelectionCheckIsFinished), mtInformation, [mbYes, mbNo], 0) = mrYes) then begin if SpellingEnd = ASpellingEnd then SetBeforeSelectionBounds else SpellingEnd := ASpellingEnd; FCheckedSelection := True; UpdateTextInfo; end; end; end; procedure TdxSpellCheckerDialogCheckMode.ValidateSpellingBoundsAfterUndo; var ASpellingStart, ASpellingEnd: Integer; begin Adapter.GetSpellingBounds(ASpellingStart, ASpellingEnd); if (FMisspelledStart >= SpellingEnd) and (SpellingEnd < ASpellingEnd) then begin SpellingStart := SpellingEnd; SpellingEnd := ASpellingEnd; UpdateTextInfo; end; end; function TdxSpellCheckerDialogCheckMode.GetLanguages: TdxSpellCheckerLanguages; begin Result := SpellChecker.Languages; end; { TdxSpellCheckerOutlookCheckMode } function TdxSpellCheckerOutlookCheckMode.ShowDialog: Integer; begin Result := dxShowOutlookSpellingDialog(SpellChecker); inherited ShowDialog; end; procedure TdxSpellCheckerOutlookCheckMode.SelectMisspelledWordAfterUndo; var AItem: TdxSpellCheckerUndoItem; begin AItem := FUndoManager.Last; if AItem <> nil then FMisspelledStart := FMisspelledStart - Length(AItem.Replacement) + Length(AItem.MisspelledWord); inherited; end; { TdxSpellCheckerWordCheckMode } procedure TdxSpellCheckerWordCheckMode.ChangeSentence(const ASentence: WideString); var AMisspelledSentence: TdxSpellCheckerSentence; ADelta: Integer; begin AMisspelledSentence := MisspelledSentence; FUndoManager.Add(AMisspelledSentence.Text, ASentence, '', AMisspelledSentence.StartTextPosition - 1); Adapter.SelStart := AMisspelledSentence.StartTextPosition - 1; Adapter.SelLength := Length(AMisspelledSentence.Text); PrevWord := ''; ADelta := Length(ASentence) - Adapter.SelLength; Adapter.SelText := ASentence; UpdateSpellingBounds(ADelta); UpdateTextInfo; FMisspelledStart := AMisspelledSentence.StartTextPosition - 1; end; procedure TdxSpellCheckerWordCheckMode.SelectMisspelledWordAfterUndo; var AItem: TdxSpellCheckerUndoItem; begin AItem := FUndoManager.Last; if AItem <> nil then begin FMisspelledStart := AItem.MisspelledWordPosition + 1; FMisspelledLen := Length(AItem.MisspelledWord); end; inherited SelectMisspelledWordAfterUndo; end; function TdxSpellCheckerWordCheckMode.ShowDialog: Integer; begin Result := dxShowWordSpellingDialog(SpellChecker); inherited ShowDialog; end; function TdxSpellCheckerWordCheckMode.GetMisspelledSentence: TdxSpellCheckerSentence; begin Result := Parser.GetSentence(FMisspelledStart); end; { TdxSpellCheckerSuggestionBuilder } constructor TdxSpellCheckerSuggestionBuilder.Create( ADictionary: TdxCustomSpellCheckerDictionary); begin inherited Create; FDictionary := ADictionary; Alphabet := ADictionary.GetActiveAlphabet; end; procedure TdxSpellCheckerSuggestionBuilder.AddSuggestions( const AWord: WideString; ASuggestions: TdxSpellCheckerSuggestionList); begin if (ASuggestions = nil) or (Length(AWord) = 0) then Exit; FSuggestions := ASuggestions; FWord := WideLowerCase(AWord); DoAddSuggestions; end; function TdxSpellCheckerSuggestionBuilder.CanAddToSuggestions( const ATestWord: WideString): Boolean; begin Result := Dictionary.HasWord(ATestWord); end; procedure TdxSpellCheckerSuggestionBuilder.SetAlphabet(const AValue: WideString); var I: Integer; ALower: WideString; begin FAlphabet := ''; if Length(AValue) > 0 then begin ALower := WideLowerCase(AValue); for I := 1 to Length(ALower) do if WideCharPos(ALower[I], FAlphabet) = 0 then FAlphabet := FAlphabet + ALower[I]; end; end; { TdxNearMissStrategy } procedure TdxNearMissStrategy.CheckAddToSuggestions(const ATestWord: WideString; ADistance: Integer = 2); begin if CanAddToSuggestions(ATestWord) then FSuggestions.Add(ATestWord, Dictionary, ADistance); end; procedure TdxNearMissStrategy.CheckDeleteLetter; var ATestWord: WideString; ASymbol: WideChar; ADeletePos, ALen: Integer; begin ALen := Length(FWord); if ALen < 2 then Exit; ADeletePos := 1; repeat ATestWord := FWord; ASymbol := ATestWord[ADeletePos]; Delete(ATestWord, ADeletePos, 1); CheckAddToSuggestions(ATestWord); Inc(ADeletePos); while (ADeletePos <= ALen) and (ASymbol = FWord[ADeletePos]) do Inc(ADeletePos); until ADeletePos > ALen; end; procedure TdxNearMissStrategy.InterchangeTwoLetters; var ATestWord: WideString; I, ALen: Integer; begin ALen := Length(Word); if ALen < 2 then Exit; for I := 1 to ALen - 1 do begin if FWord[I] = Word[I + 1] then Continue; ATestWord := Word; ATestWord[I] := ATestWord[I + 1]; ATestWord[I + 1] := Word[I]; CheckAddToSuggestions(ATestWord); end; end; procedure TdxNearMissStrategy.CheckChangeOneLetter; var ATestWord: WideString; ASymbol: WideChar; I, J: Integer; begin if Length(Alphabet) = 0 then Exit; for I := 1 to Length(Word) do for J := 1 to Length(Alphabet) do begin ASymbol := Alphabet[J]; if Word[I] <> ASymbol then begin ATestWord := Word; ATestWord[I] := ASymbol; CheckAddToSuggestions(ATestWord); end; end; end; procedure TdxNearMissStrategy.CheckInsertLetter; var ATestWord: WideString; ASymbol: WideChar; I, AInsertPos, ALen: Integer; begin if Length(Alphabet) = 0 then Exit; ALen := Length(Word); for I := 1 to Length(Alphabet) do begin ASymbol := Alphabet[I]; AInsertPos := 1; repeat ATestWord := Word; while (AInsertPos <= ALen) and (ASymbol = ATestWord[AInsertPos]) do Inc(AInsertPos); Insert(ASymbol, ATestWord, AInsertPos); CheckAddToSuggestions(ATestWord); if AInsertPos > ALen then Break else Inc(AInsertPos); until False; end; end; procedure TdxNearMissStrategy.CheckInsertSpace; var ATestWord1, ATestWord2: WideString; I, ALen: Integer; begin ALen := Length(Word); if ALen < 2 then Exit; for I := 1 to ALen - 1 do begin ATestWord1 := Copy(Word, 1, I); ATestWord2 := Copy(Word, I + 1, ALen); if CanAddToSuggestions(ATestWord1) and CanAddToSuggestions(ATestWord2) then FSuggestions.Add(ATestWord1 + ' ' + ATestWord2, Dictionary, 3); end; end; procedure TdxNearMissStrategy.DoAddSuggestions; begin CheckInsertLetter; CheckInsertSpace; CheckDeleteLetter; InterchangeTwoLetters; CheckChangeOneLetter; end; { TdxSpellCheckerWordList } constructor TdxSpellCheckerWordList.Create(ALangID: Cardinal; ATableSize: Integer); begin inherited Create; InitializeCriticalSection(FLock); FLangID := ALangID; FCount := 0; FTableSize := ATableSize; FTable := AllocMem(FTableSize * SizeOf(Integer)); end; destructor TdxSpellCheckerWordList.Destroy; begin Clear; FreeMem(FTable); DeleteCriticalSection(FLock); inherited Destroy; end; procedure TdxSpellCheckerWordList.Add(const S: WideString); var AIndex, ASrcLen, AWordLen: Integer; AWord, ATemp, P: PWideChar; begin ASrcLen := Length(S); if ASrcLen = 0 then Exit; EnterCriticalSection(FLock); try P := Pointer(S); AIndex := ElfHash(P) mod FTableSize; AWord := FTable^[AIndex]; if AWord = nil then FTable^[AIndex] := NewWord(P) else begin repeat AWordLen := WordLength(AWord); if (AWordLen = ASrcLen) and (CompareStringW(LangID, NORM_IGNORECASE, AWord, AWordLen, P, ASrcLen) = CSTR_EQUAL) then Exit; ATemp := GetNextWord(AWord); if ATemp = nil then begin SetNextWord(AWord, NewWord(P)); Break; end; AWord := ATemp; until False; end; Inc(FCount); finally LeaveCriticalSection(FLock); end; end; procedure TdxSpellCheckerWordList.Clear; var I: Integer; AWord, ATemp: PWideChar; begin if Count = 0 then Exit; EnterCriticalSection(FLock); try for I := 0 to FTableSize - 1 do begin AWord := FTable^[I]; while AWord <> nil do begin ATemp := AWord; AWord := GetNextWord(AWord); DisposeWord(ATemp); end; FTable^[I] := nil; end; FCount := 0; finally LeaveCriticalSection(FLock); end; end; function TdxSpellCheckerWordList.Find(const S: WideString): Boolean; begin Result := FindWord(S) <> nil; end; procedure TdxSpellCheckerWordList.LoadFromStrings(AStrings: TStrings); var I: Integer; begin if AStrings = nil then Exit; Clear; for I := 0 to AStrings.Count - 1 do Add(AStrings[I]); end; procedure TdxSpellCheckerWordList.SaveToStrings(AStrings: TStrings); var I: Integer; AWord: PWideChar; begin if AStrings = nil then Exit; AStrings.BeginUpdate; try AStrings.Clear; for I := 0 to FTableSize - 1 do begin AWord := FTable^[I]; while AWord <> nil do begin AStrings.Add(AWord); AWord := GetNextWord(AWord); end; end; finally AStrings.EndUpdate; end; end; function TdxSpellCheckerWordList.ElfHash(P: PWideChar): Integer; var I: Integer; ABuffer: array[0..256] of WideChar; begin LCMapStringW(LangID, LCMAP_LOWERCASE, P, -1, @ABuffer, SizeOf(ABuffer) div SizeOf(WideChar)); Result := 0; Pointer(P) := @ABuffer; while P^ <> #$00 do begin Result := (Result shl 4) + Ord(P^); I := Result and $F0000000; if (I <> 0) then Result := Result xor (I shr 24); Result := Result and (not I); Inc(P); end; end; function TdxSpellCheckerWordList.FindWord(const S: WideString): PWideChar; var AIndex, ASrcLen, AResultLen: Integer; P: PWideChar; begin Result := nil; ASrcLen := Length(S); if ASrcLen = 0 then Exit; P := Pointer(S); AIndex := ElfHash(P) mod FTableSize; Result := FTable^[AIndex]; while Result <> nil do begin AResultLen := WordLength(Result); if (AResultLen = ASrcLen) and (CompareStringW(LangID, NORM_IGNORECASE, Result, AResultLen, P, ASrcLen) = CSTR_EQUAL) then Exit; Result := GetNextWord(Result); end; end; function TdxSpellCheckerWordList.NewWord(S: PWideChar): PWideChar; var P : PWideChar; ALength: Integer; begin Result := nil; if S = nil then Exit; P := S; while (P^ <> #0) do Inc(P); ALength := P - S; if ALength > 0 then begin Result := AllocWord(ALength); Inc(ALength); //include terminated null Move(S^, Result^, ALength * SizeOf(WideChar)); end; end; function TdxSpellCheckerWordList.AllocWord(ACharCount: Cardinal): PWideChar; var ASize: Integer; begin ASize := (ACharCount + 1) * Sizeof(WideChar); //add terminated null Inc(ASize, SizeOf(Cardinal) * 3); //Size, Next, Length GetMem(Result, ASize); Cardinal(Pointer(Result)^) := ASize; //Size Inc(PChar(Result), SizeOf(Cardinal)); Pointer(Pointer(Result)^) := nil; //Next Inc(PChar(Result), SizeOf(Cardinal)); Cardinal(Pointer(Result)^) := ACharCount; //Length Inc(PChar(Result), SizeOf(Cardinal)); end; procedure TdxSpellCheckerWordList.DisposeWord(AWord: PWideChar); begin if AWord <> nil then begin Dec(PChar(AWord), SizeOf(Cardinal) * 3); FreeMem(AWord, Cardinal(Pointer(AWord)^)); end; end; function TdxSpellCheckerWordList.GetNextWord(AWord: PWideChar): Pointer; begin if AWord = nil then Result := nil else begin Dec(PChar(AWord), SizeOf(Cardinal) * 2); Result := Pointer(Pointer(AWord)^); end; end; procedure TdxSpellCheckerWordList.SetNextWord(AWord: PWideChar; AValue: Pointer); begin if AWord <> nil then begin Dec(PChar(AWord), SizeOf(Cardinal) * 2); Pointer(Pointer(AWord)^) := AValue; end; end; function TdxSpellCheckerWordList.WordLength(AWord: PWideChar): Integer; begin if AWord = nil then Result := 0 else begin Dec(PChar(AWord), SizeOf(Cardinal)); Result := Cardinal(Pointer(AWord)^); end; end; function TdxSpellCheckerWordList.GetCodePage: Cardinal; begin Result := LanguageToCodePage(FLangID); end; { TdxDictionaryLoadThread } constructor TdxDictionaryLoadThread.Create( ADictionary: TdxCustomSpellCheckerDictionary); begin inherited Create(False); FDictionary := ADictionary; OnTerminate := Dictionary.ThreadDone; end; function TdxDictionaryLoadThread.IsLoadComplete: Boolean; begin Result := not Terminated and Dictionary.Loaded; end; procedure TdxDictionaryLoadThread.Execute; begin ResetException; try with Dictionary do begin try Synchronize(UpdateLoadedOnLoadEvent); if not Loaded then FLoaded := DoLoad and not Terminated; except FLoaded := False; raise; end; if Loaded then Synchronize(LoadingComplete); end; except HandleException; end; end; { TdxCustomSpellCheckerDictionary } constructor TdxCustomSpellCheckerDictionary.Create(ASpellChecker: TdxCustomSpellChecker); begin inherited Create; FSpellChecker := ASpellChecker; FEnabled := True; FCodePage := CP_ACP; FWords := TdxSpellCheckerWordList.Create(CodePage, 269683); Language := GetSystemDefaultLangID; end; destructor TdxCustomSpellCheckerDictionary.Destroy; begin FLoadThread.Free; //don't use FreeAndNil FLoadThread := nil; Unload; FWords.Free; inherited Destroy; end; procedure TdxCustomSpellCheckerDictionary.Assign(Source: TPersistent); begin if Source is TdxCustomSpellCheckerDictionary then begin FAlphabet := TdxCustomSpellCheckerDictionary(Source).Alphabet; FCodePage := TdxCustomSpellCheckerDictionary(Source).CodePage; FEnabled := TdxCustomSpellCheckerDictionary(Source).Enabled; FLanguage := TdxCustomSpellCheckerDictionary(Source).Language; end; end; procedure TdxCustomSpellCheckerDictionary.Activate; begin DoActivate; end; procedure TdxCustomSpellCheckerDictionary.Load( AMode: TdxSpellCheckerDictionaryLoadMode = dlmDefault); begin case AMode of dlmDefault: begin if SpellChecker.UseThreadedLoad then LoadUsingThread else DirectLoad; end; dlmDirectLoad: DirectLoad; dlmThreadedLoad: LoadUsingThread; end; end; procedure TdxCustomSpellCheckerDictionary.Unload; begin if LoadThread <> nil then LoadThread.Terminate; if Loaded then FLoaded := not DoUnload; if not Loaded then Cleanup; Update; end; procedure TdxCustomSpellCheckerDictionary.Clear; begin Enabled := False; Unload; end; function TdxCustomSpellCheckerDictionary.CanLoad: Boolean; begin Result := not Loaded and (FLoadThread = nil); end; procedure TdxCustomSpellCheckerDictionary.Cleanup; begin Words.Clear; end; function TdxCustomSpellCheckerDictionary.CreateSuggestionBuilder: TdxSpellCheckerSuggestionBuilder; begin Result := TdxNearMissStrategy.Create(Self); end; procedure TdxCustomSpellCheckerDictionary.DirectLoad; begin if not CanLoad then Exit; ShowHourglassCursor; try try FLoaded := DoLoadingEvent or DoLoad; except FLoaded := False; raise; end; if Loaded then LoadingComplete; finally HideHourglassCursor; end; end; procedure TdxCustomSpellCheckerDictionary.DoActivate; begin Load; Enabled := True; end; function TdxCustomSpellCheckerDictionary.DoLoad: Boolean; begin Result := False; end; procedure TdxCustomSpellCheckerDictionary.DoLoadedEvent; begin if Assigned(FOnLoaded) then FOnLoaded(Self); end; function TdxCustomSpellCheckerDictionary.DoLoadingEvent: Boolean; begin Result := False; if Assigned(FOnLoading) then FOnLoading(Self, Result); end; function TdxCustomSpellCheckerDictionary.DoUnload: Boolean; begin Words.Clear; Result := True; end; function TdxCustomSpellCheckerDictionary.GetActiveAlphabet: WideString; begin if Alphabet = '' then Result := CreateDefaultAlphabet(CodePage) else Result := Alphabet; end; function TdxCustomSpellCheckerDictionary.GetDisplayName: string; begin Result := GetRegisteredDictionaryTypes.GetDescriptionByClass(ClassType); if Result = '' then Result := ClassName; end; procedure TdxCustomSpellCheckerDictionary.LoadingComplete; begin DoLoadedEvent; Update; end; function TdxCustomSpellCheckerDictionary.LoadingTerminated: Boolean; begin Result := (FLoadThread <> nil) and FLoadThread.Terminated; end; procedure TdxCustomSpellCheckerDictionary.LoadUsingThread; begin if not CanLoad then Exit; FreeAndNil(FLoadThread); FLoadThread := TdxDictionaryLoadThread.Create(Self); end; procedure TdxCustomSpellCheckerDictionary.ThreadDone(Sender: TObject); begin if not LoadThread.IsLoadComplete then Cleanup else SpellChecker.CheckCallEnabledDictionariesLoaded; end; procedure TdxCustomSpellCheckerDictionary.Update; begin SpellChecker.UpdateByDictionary(Self); end; procedure TdxCustomSpellCheckerDictionary.UpdateLoadedOnLoadEvent; begin FLoaded := DoLoadingEvent; end; function TdxCustomSpellCheckerDictionary.GetActive: Boolean; begin Result := Loaded and Enabled; end; function TdxCustomSpellCheckerDictionary.GetWordCount: Integer; begin Result := FWords.Count; end; function TdxCustomSpellCheckerDictionary.HasWord( const AWord: WideString): Boolean; begin Result := FWords.Find(AWord); end; procedure TdxCustomSpellCheckerDictionary.SetEnabled(AValue: Boolean); begin if FEnabled <> AValue then begin FEnabled := AValue; if Loaded then Update; end; end; procedure TdxCustomSpellCheckerDictionary.SetCodePage(AValue: Cardinal); begin if FCodePage <> AValue then begin FCodePage := AValue; if Loaded then begin Unload; Load; Update; end; end; end; procedure TdxCustomSpellCheckerDictionary.SetLanguage(const AValue: DWORD); begin if FLanguage <> AValue then begin FLanguage := AValue; FWords.LangID := FLanguage; end; end; { TdxUserSpellCheckerDictionary } constructor TdxUserSpellCheckerDictionary.Create( ASpellChecker: TdxCustomSpellChecker); begin inherited Create(ASpellChecker); FOptions := [udSaveOnUnload]; end; procedure TdxUserSpellCheckerDictionary.Assign(Source: TPersistent); begin inherited Assign(Source); if Source is TdxUserSpellCheckerDictionary then begin DictionaryPath := TdxUserSpellCheckerDictionary(Source).DictionaryPath; Options := TdxUserSpellCheckerDictionary(Source).Options; end; end; procedure TdxUserSpellCheckerDictionary.AddWord(const AWord: WideString); begin Words.Add(AWord); Update; end; procedure TdxUserSpellCheckerDictionary.LoadFromStrings(AStrings: TStrings); begin Words.LoadFromStrings(AStrings); Update; end; procedure TdxUserSpellCheckerDictionary.SaveToStrings(AStrings: TStrings); begin if AStrings = nil then Exit; Words.SaveToStrings(AStrings); if AStrings is TStringList then TStringList(AStrings).Sort; end; function TdxUserSpellCheckerDictionary.DoLoad: Boolean; var AStrings: TStrings; begin Result := True; if not FileExists(DictionaryPath) then if not (udFileMustExist in Options) then Exit; AStrings := TStringList.Create; try AStrings.LoadFromFile(DictionaryPath); Words.LoadFromStrings(AStrings); finally AStrings.Free; end; end; function TdxUserSpellCheckerDictionary.DoUnload: Boolean; var AStrings: TStrings; begin Result := True; if udSaveOnUnload in Options then begin AStrings := TStringList.Create; try Words.SaveToStrings(AStrings); try AStrings.SaveToFile(DictionaryPath); except if not (csDestroying in SpellChecker.ComponentState) then raise; end; finally AStrings.Free; end; end; end; function TdxUserSpellCheckerDictionary.GetDisplayName: string; var AFileName: TFileName; begin Result := inherited GetDisplayName; AFileName := ExtractFileName(DictionaryPath); if AFileName <> '' then Result := Format('%s (%s)', [Result, AFileName]); end; { TdxSpellCheckerDictionaryItem } destructor TdxSpellCheckerDictionaryItem.Destroy; begin FreeAndNil(FDictionaryType); inherited Destroy; end; procedure TdxSpellCheckerDictionaryItem.Assign(Source: TPersistent); begin if Source is TdxSpellCheckerDictionaryItem then begin DictionaryTypeClassName := TdxSpellCheckerDictionaryItem(Source).DictionaryTypeClassName; DictionaryType := TdxSpellCheckerDictionaryItem(Source).DictionaryType; end; end; function TdxSpellCheckerDictionaryItem.GetDisplayName: string; begin if FDictionaryType <> nil then Result := FDictionaryType.GetDisplayName else Result := inherited GetDisplayName; end; procedure TdxSpellCheckerDictionaryItem.RecreateDictionaryType; begin FreeAndNil(FDictionaryType); if FDictionaryTypeClass <> nil then FDictionaryType := FDictionaryTypeClass.Create(Collection.SpellChecker); end; function TdxSpellCheckerDictionaryItem.GetCollection: TdxSpellCheckerDictionaries; begin Result := TdxSpellCheckerDictionaries(inherited Collection); end; function TdxSpellCheckerDictionaryItem.GetDictionaryTypeClassName: string; begin if FDictionaryType = nil then Result := '' else Result := FDictionaryType.ClassName; end; procedure TdxSpellCheckerDictionaryItem.SetDictionaryType( AValue: TdxCustomSpellCheckerDictionary); begin if (FDictionaryType <> nil) and (AValue <> nil) then FDictionaryType.Assign(AValue); end; procedure TdxSpellCheckerDictionaryItem.SetDictionaryTypeClass( AValue: TdxCustomSpellCheckerDictionaryClass); begin if FDictionaryTypeClass <> AValue then begin FDictionaryTypeClass := AValue; RecreateDictionaryType; end; end; procedure TdxSpellCheckerDictionaryItem.SetDictionaryTypeClassName( const AValue: string); begin if not SameText(DictionaryTypeClassName, AValue) then begin with GetRegisteredDictionaryTypes do DictionaryTypeClass := TdxCustomSpellCheckerDictionaryClass(FindByClassName(AValue)); end; end; { TdxSpellCheckerDictionaries } constructor TdxSpellCheckerDictionaries.Create(ASpellChecker: TdxCustomSpellChecker); begin inherited Create(TdxSpellCheckerDictionaryItem); FSpellChecker := ASpellChecker; end; function TdxSpellCheckerDictionaries.Add: TdxSpellCheckerDictionaryItem; begin Result := TdxSpellCheckerDictionaryItem(inherited Add); end; function TdxSpellCheckerDictionaries.GetNamePath: string; var S: string; begin S := SpellChecker.Name; if S = '' then S := SpellChecker.GetNamePath; Result := S + '.' + 'Items'; end; function TdxSpellCheckerDictionaries.GetOwner: TPersistent; begin Result := SpellChecker; end; function TdxSpellCheckerDictionaries.GetItem( Index: Integer): TdxSpellCheckerDictionaryItem; begin Result := TdxSpellCheckerDictionaryItem(inherited Items[Index]); end; procedure TdxSpellCheckerDictionaries.SetItem(Index: Integer; AValue: TdxSpellCheckerDictionaryItem); begin inherited Items[Index] := AValue; end; { TdxSpellCheckerSpellingOptions } constructor TdxSpellCheckerSpellingOptions.Create( ASpellChecker: TdxCustomSpellChecker); begin inherited Create; FSpellChecker := ASpellChecker; FIgnoreMixedCaseWords := True; FIgnoreUpperCaseWords := True; FIgnoreMarkupTags := True; FIgnoreRepeatedWords := False; FCheckSelectedTextFirst := True; FIgnoreUrls := True; FCheckFromCursorPos := False; FIgnoreEmails := True; FIgnoreWordsWithNumbers := True; end; procedure TdxSpellCheckerSpellingOptions.Assign(Source: TPersistent); begin if Source is TdxSpellCheckerSpellingOptions then begin FIgnoreMixedCaseWords := TdxSpellCheckerSpellingOptions(Source).IgnoreMixedCaseWords; FIgnoreUpperCaseWords := TdxSpellCheckerSpellingOptions(Source).IgnoreUpperCaseWords; FIgnoreMarkupTags := TdxSpellCheckerSpellingOptions(Source).IgnoreMarkupTags; FIgnoreRepeatedWords := TdxSpellCheckerSpellingOptions(Source).IgnoreRepeatedWords; FCheckSelectedTextFirst := TdxSpellCheckerSpellingOptions(Source).CheckSelectedTextFirst; FIgnoreUrls := TdxSpellCheckerSpellingOptions(Source).IgnoreUrls; FCheckFromCursorPos := TdxSpellCheckerSpellingOptions(Source).CheckFromCursorPos; FIgnoreEmails := TdxSpellCheckerSpellingOptions(Source).IgnoreEmails; FIgnoreWordsWithNumbers := TdxSpellCheckerSpellingOptions(Source).IgnoreWordsWithNumbers; end; end; procedure TdxSpellCheckerSpellingOptions.Changed; begin SpellChecker.SpellingOptionsChanged; DoChanged; end; procedure TdxSpellCheckerSpellingOptions.DoChanged; begin if Assigned(FOnChanged) then FOnChanged(Self); end; procedure TdxSpellCheckerSpellingOptions.PopulateRules( ARules: TdxSpellCheckerRules; AParser: TdxSpellCheckerTextParser); begin if AParser = nil then Exit; if not IgnoreRepeatedWords then ARules.Add(TdxSpellCheckerRepeatedWordsRule.Create(AParser)); if IgnoreEmails then ARules.Add(TdxSpellCheckerIgnoreEmailRule.Create(AParser)); if IgnoreMixedCaseWords then ARules.Add(TdxSpellCheckerIgnoreMixedCaseWordsRule.Create(AParser)); if IgnoreUpperCaseWords then ARules.Add(TdxSpellCheckerIgnoreUpperCaseWordsRule.Create(AParser)); if IgnoreUrls then ARules.Add(TdxSpellCheckerIgnoreUrlRule.Create(AParser)); if IgnoreWordsWithNumbers then ARules.Add(TdxSpellCheckerIgnoreWordsWithNumbersRule.Create(AParser)); end; procedure TdxSpellCheckerSpellingOptions.SetCheckFromCursorPos( AValue: Boolean); begin if FCheckFromCursorPos <> AValue then begin FCheckFromCursorPos := AValue; Changed; end; end; procedure TdxSpellCheckerSpellingOptions.SetCheckSelectedTextFirst( AValue: Boolean); begin if FCheckSelectedTextFirst <> AValue then begin FCheckSelectedTextFirst := AValue; Changed; end; end; procedure TdxSpellCheckerSpellingOptions.SetIgnoreEmails( AValue: Boolean); begin if FIgnoreEmails <> AValue then begin FIgnoreEmails := AValue; Changed; end; end; procedure TdxSpellCheckerSpellingOptions.SetIgnoreMarkupTags( AValue: Boolean); begin if FIgnoreMarkupTags <> AValue then begin FIgnoreMarkupTags := AValue; Changed; end; end; procedure TdxSpellCheckerSpellingOptions.SetIgnoreMixedCaseWords( AValue: Boolean); begin if FIgnoreMixedCaseWords <> AValue then begin FIgnoreMixedCaseWords := AValue; Changed; end; end; procedure TdxSpellCheckerSpellingOptions.SetIgnoreRepeatedWords( AValue: Boolean); begin if FIgnoreRepeatedWords <> AValue then begin FIgnoreRepeatedWords := AValue; Changed; end; end; procedure TdxSpellCheckerSpellingOptions.SetIgnoreUpperCaseWords( AValue: Boolean); begin if FIgnoreUpperCaseWords <> AValue then begin FIgnoreUpperCaseWords := AValue; Changed; end; end; procedure TdxSpellCheckerSpellingOptions.SetIgnoreUrls( AValue: Boolean); begin if FIgnoreUrls <> AValue then begin FIgnoreUrls := AValue; Changed; end; end; procedure TdxSpellCheckerSpellingOptions.SetIgnoreWordsWithNumbers( AValue: Boolean); begin if FIgnoreWordsWithNumbers <> AValue then begin FIgnoreWordsWithNumbers := AValue; Changed; end; end; { TdxSpellCheckerCheckAsYouTypeOptions } constructor TdxSpellCheckerCheckAsYouTypeOptions.Create( ASpellChecker: TdxCustomSpellChecker); begin FSpellChecker := ASpellChecker; FSuggestionCount := 5; FUnderlineColor := clRed; FUnderlineStyle := usAuto; FModifyControlPopupMenu := True; FPopupMenuItems := [scmiAddToDictionary, scmiDelete, scmiIgnoreAll, scmiSpelling, scmiSuggestions]; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.Assign(Source: TPersistent); begin if Source is TdxSpellCheckerCheckAsYouTypeOptions then begin FSuggestionCount := TdxSpellCheckerCheckAsYouTypeOptions(Source).FSuggestionCount; FUnderlineColor := TdxSpellCheckerCheckAsYouTypeOptions(Source).FUnderlineColor; FUnderlineStyle := TdxSpellCheckerCheckAsYouTypeOptions(Source).FUnderlineStyle; FModifyControlPopupMenu := TdxSpellCheckerCheckAsYouTypeOptions(Source).ModifyControlPopupMenu; FPopupMenu := TdxSpellCheckerCheckAsYouTypeOptions(Source).PopupMenu; FPopupMenuItems := TdxSpellCheckerCheckAsYouTypeOptions(Source).PopupMenuItems; Changed; end; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.Changed; begin if SpellChecker.CheckAsYouTypeManager <> nil then SpellChecker.CheckAsYouTypeManager.DoOptionsChanged; DoChanged; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.DoChanged; begin if Assigned(FOnChanged) then FOnChanged(Self); end; procedure TdxSpellCheckerCheckAsYouTypeOptions.Notification(AComponent: TComponent; AOperation: TOperation); begin if (AComponent = FPopupMenu) and (AOperation = opRemove) then PopupMenu := nil; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.SetActive(AValue: Boolean); begin if FActive <> AValue then begin FActive := AValue; SpellChecker.UpdateCheckAsYouTypeManagerState; Changed; end; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.SetPopupMenu(AValue: TComponent); begin if FPopupMenu <> AValue then begin if (FPopupMenu <> nil) and not (csDestroying in FPopupMenu.ComponentState) then FPopupMenu.RemoveFreeNotification(SpellChecker); FPopupMenu := AValue; if FPopupMenu <> nil then FPopupMenu.FreeNotification(SpellChecker); Changed; end; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.SetModifyControlPopupMenu(AValue: Boolean); begin if AValue <> FModifyControlPopupMenu then begin FModifyControlPopupMenu := AValue; Changed; end; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.SetPopupMenuItems(AValue: TdxSpellCheckerPopupMenuItems); begin if AValue <> FPopupMenuItems then begin FPopupMenuItems := AValue; Changed; end; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.SetSuggestionCount(AValue: Integer); begin if AValue <> FSuggestionCount then begin FSuggestionCount := AValue; Changed; end; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.SetUnderlineColor(AValue: TColor); begin if FUnderlineColor <> AValue then begin FUnderlineColor := AValue; Changed; end; end; procedure TdxSpellCheckerCheckAsYouTypeOptions.SetUnderlineStyle( AValue: TdxSpellCheckerUnderlineStyle); begin if FUnderlineStyle <> AValue then begin FUnderlineStyle := AValue; Changed; end; end; { TdxCustomSpellChecker } function IsContainer(AComponent: TComponent): Boolean; begin Result := (AComponent is TCustomForm) or (AComponent is TCustomFrame) or (AComponent is TDataModule); end; function IsSpellCheckerAlreadyExists(AOwner: TComponent; AInstance: TdxCustomSpellChecker): Boolean; var I: Integer; begin Result := False; for I := 0 to AOwner.ComponentCount - 1 do if (AOwner.Components[I] <> AInstance) and (AOwner.Components[I] is TdxCustomSpellChecker) then begin Result := True; Break; end; end; procedure SpellCheckerCreatingError; begin raise Exception.Create(cxGetResourceString(@sdxSpellCheckerMoreThanOne)); end; constructor TdxCustomSpellChecker.Create(AOwner: TComponent); var I: Integer; AComponent: TComponent; begin FCreating := True; if IsContainer(AOwner) and IsSpellCheckerAlreadyExists(AOwner, Self) then SpellCheckerCreatingError; for I := 0 to Application.ComponentCount - 1 do begin AComponent := Application.Components[I]; if IsContainer(AComponent) and IsSpellCheckerAlreadyExists(AComponent, Self) then SpellCheckerCreatingError; end; FCreating := False; inherited Create(AOwner); FAutoLoadDictionaries := False; FSpellingFormType := sftOutlook; FDictionaryItems := TdxSpellCheckerDictionaries.Create(Self); FSimilarity := CreateSimilarity; FRules := TdxSpellCheckerRules.Create; FDialogLookAndFeel := TcxLookAndFeel.Create(nil); FSpellingOptions := CreateSpellingOptions; FCheckAsYouTypeOptions := TdxSpellCheckerCheckAsYouTypeOptions.Create(Self); SpellingOptionsChanged; FLanguages := TdxSpellCheckerLanguages.Create; FChangeList := TdxSpellCheckerReplacementList.Create; FDeleteList := TdxSpellCheckerWordList.Create(LANG_SYSTEM_DEFAULT, 257); FIgnoreList := TdxSpellCheckerWordList.Create(LANG_SYSTEM_DEFAULT, 257); end; destructor TdxCustomSpellChecker.Destroy; begin if not FCreating then begin FreeAndNil(FChangeList); FreeAndNil(FDeleteList); FreeAndNil(FIgnoreList); FCheckAsYouTypeOptions.Active := False; UnloadDictionaries; FreeAndNil(FLanguages); FreeAndNil(FSpellingOptions); FreeAndNil(FRules); FreeAndNil(FSimilarity); FreeAndNil(FCheckAsYouTypeOptions); FreeAndNil(FDialogLookAndFeel); FreeAndNil(FDictionaryItems); end; inherited Destroy; end; procedure TdxCustomSpellChecker.Check(AEdit: TCustomEdit); var AEditAdapter: TdxSpellCheckerEditAdapter; begin AEditAdapter := TdxSpellCheckerEditAdapter.Create(AEdit); try InternalCheck(AEditAdapter); finally FreeAndNil(AEditAdapter); end; end; procedure TdxCustomSpellChecker.Check(AEdit: TcxCustomTextEdit); var AEditAdapter: TdxSpellCheckercxTextEditAdapter; begin if AEdit is TcxCustomRichEdit then AEditAdapter := TdxSpellCheckercxRichEditAdapter.Create(AEdit) else AEditAdapter := TdxSpellCheckercxTextEditAdapter.Create(AEdit); try InternalCheck(AEditAdapter); finally FreeAndNil(AEditAdapter); end; end; procedure TdxCustomSpellChecker.CheckContainer(AContainer: TWinControl; ARecursive: Boolean); begin if AContainer = nil then Exit; FCheckingContainer := True; try FCheckGroupMode := True; FLastDialogResult := mrOk; try DoCheckContainer(AContainer, ARecursive); finally FCheckGroupMode := False; end; if FLastDialogResult <> mrCancel then SpellingComplete; finally FCheckingContainer := False; end; end; procedure TdxCustomSpellChecker.AddToIgnoreList(const AWord: WideString); begin IgnoreList.Add(AWord); if Assigned(CheckAsYouTypeManager) then CheckAsYouTypeManager.Refresh; end; procedure TdxCustomSpellChecker.AddWordToUserDictionary( const AWord: WideString); var AUserDictionary: TdxUserSpellCheckerDictionary; begin AUserDictionary := FindFirstEnabledUserDictionary; if AUserDictionary <> nil then begin if not DoAddWord(AUserDictionary, AWord) then AUserDictionary.AddWord(AWord); end; end; function TdxCustomSpellChecker.FindDictionaryByWord(const AWord: WideString): TdxCustomSpellCheckerDictionary; var I: Integer; ADictionary: TdxCustomSpellCheckerDictionary; begin Result := nil; for I := 0 to ActiveDictionaryCount - 1 do begin ADictionary := ActiveDictionaries[I]; if ADictionary.HasWord(AWord) then begin Result := ADictionary; Break; end; end; end; function TdxCustomSpellChecker.FindFirstEnabledUserDictionary: TdxUserSpellCheckerDictionary; var I: Integer; ADictionary: TdxUserSpellCheckerDictionary; begin Result := nil; for I := 0 to UserDictionaryCount - 1 do begin ADictionary := UserDictionaries[I]; if ADictionary.Enabled then begin Result := ADictionary; Break; end; end; end; function TdxCustomSpellChecker.GetSuggestions(const AWord: WideString): TdxSpellCheckerSuggestionList; var I: Integer; ADictionary: TdxCustomSpellCheckerDictionary; begin Result := TdxSpellCheckerSuggestionList.Create; if not HasWordInDictionaries(AWord) then begin for I := 0 to ActiveDictionaryCount - 1 do begin ADictionary := ActiveDictionaries[I]; with ADictionary.CreateSuggestionBuilder do try AddSuggestions(AWord, Result); finally Free; end; end; Result.RemoveDuplicates; Result.FixCapitalization(AWord); DoGetSuggestions(Result); end; end; function TdxCustomSpellChecker.HasWordInDictionaries(const AWord: WideString): Boolean; begin Result := FindDictionaryByWord(AWord) <> nil; end; function TdxCustomSpellChecker.HasEnabledUserDictionary: Boolean; begin Result := FindFirstEnabledUserDictionary <> nil; end; function TdxCustomSpellChecker.IsValidWord(const AWord: WideString): Boolean; begin Result := False; if not DoCheckWord(AWord, Result) then Result := Rules.IsValid(AWord); end; procedure TdxCustomSpellChecker.PopulateLanguages(AList: TStrings); var I: Integer; ADictionary: TdxCustomSpellCheckerDictionary; ALanguageName: string; ALangID: Cardinal; begin for I := 0 to DictionaryItems.Count - 1 do begin ADictionary := Dictionaries[I]; if ADictionary is TdxUserSpellCheckerDictionary then Continue; if ADictionary.Language <> 0 then ALangID := ADictionary.Language else ALangID := Languages.GetDefaultLanguageLCID; ALanguageName := Languages.NameFromLocaleID[ALangID]; if AList.IndexOf(ALanguageName) < 0 then AList.AddObject(ALanguageName, Pointer(ALangID)); end; end; procedure TdxCustomSpellChecker.ShowSpellingCompleteMessage; begin ShowMessage(cxGetResourceString(@sdxSpellCheckerSpellingComplete)); end; procedure TdxCustomSpellChecker.LoadDictionaries(AIgnoreDisabled: Boolean = True); begin if UseThreadedLoad then LoadDictionariesUsingThread(AIgnoreDisabled) else LoadDictionariesDirect(AIgnoreDisabled); end; procedure TdxCustomSpellChecker.UnloadDictionaries; var I: Integer; begin ShowHourglassCursor; try for I := 0 to DictionaryCount - 1 do Dictionaries[I].Unload; finally HideHourglassCursor; end; end; function TdxCustomSpellChecker.GetTwoWordsDistance( const AWord1, AWord2: WideString): Integer; begin Result := FSimilarity.GetDistance(AWord1, AWord2); end; procedure TdxCustomSpellChecker.CheckCallEnabledDictionariesLoaded; begin if Assigned(CheckAsYouTypeManager) then CheckAsYouTypeManager.Refresh(True); if ActiveDictionaryCount = EnabledDictionaryCount then DoEnabledDictionariesLoaded; end; function TdxCustomSpellChecker.CreateSimilarity: TdxStringSimilarityCalculator; begin Result := TdxStringSimilarityCalculator.Create; end; function TdxCustomSpellChecker.CreateSpellingOptions: TdxSpellCheckerSpellingOptions; begin Result := TdxSpellCheckerSpellingOptions.Create(Self); end; procedure TdxCustomSpellChecker.BeginManualCheck; begin if Assigned(CheckAsYouTypeManager) then CheckAsYouTypeManager.BeginManualCheck; end; procedure TdxCustomSpellChecker.EndManualCheck; begin if Assigned(CheckAsYouTypeManager) then CheckAsYouTypeManager.EndManualCheck; end; function TdxCustomSpellChecker.DoAddWord(AUserDictionary: TdxUserSpellCheckerDictionary; const AWord: WideString): Boolean; begin Result := False; if Assigned(FOnAddWord) then FOnAddWord(AUserDictionary, AWord, Result); end; procedure TdxCustomSpellChecker.DoAfterCheck; begin if Assigned(FOnAfterCheck) then FOnAfterCheck(Self); end; procedure TdxCustomSpellChecker.DoBeforeCheck; begin if Assigned(FOnBeforeCheck) then FOnBeforeCheck(Self); end; procedure TdxCustomSpellChecker.DoCheck(ADialogCheckMode: TdxSpellCheckerDialogCheckMode); begin if ActiveDictionaryCount = 0 then raise EdxSpellCheckerException.Create(cxGetResourceString(@sdxSpellCheckerNoActiveDictionaries)); DoBeforeCheck; try ValidateRules(ADialogCheckMode.Parser); if not ADialogCheckMode.GetNextMisspelledWord then begin SpellingComplete; FLastDialogResult := mrOk; end else begin FLastDialogResult := ADialogCheckMode.ShowDialog; if FLastDialogResult = mrOk then SpellingComplete; end; finally DoAfterCheck; end; end; procedure TdxCustomSpellChecker.DoCheckAsYouTypeFinish(AControl: TWinControl); begin if Assigned(OnCheckAsYouTypeFinish) then OnCheckAsYouTypeFinish(Self, AControl); end; function TdxCustomSpellChecker.DoCheckAsYouTypePopup(APopup: TComponent): Boolean; begin Result := False; if Assigned(FOnCheckAsYouTypePopup) then FOnCheckAsYouTypePopup(Self, APopup, Result); end; function TdxCustomSpellChecker.DoCheckAsYouTypeStart(AControl: TWinControl): Boolean; begin Result := Assigned(AControl); if Result and Assigned(OnCheckAsYouTypeStart) then OnCheckAsYouTypeStart(Self, AControl, Result); end; procedure TdxCustomSpellChecker.DoCheckContainer(AContainer: TWinControl; ARecursive: Boolean); function StopCheck(AContinue: Boolean): Boolean; begin Result := not AContinue or (FLastDialogResult <> mrOk); if Result then FLastDialogResult := mrCancel; end; var I: Integer; AControl: TWinControl; AContinue: Boolean; L: TList; begin L := TList.Create; try AContainer.GetTabOrderList(L); for I := 0 to L.Count - 1 do begin AControl := TWinControl(L[I]); if not AControl.CanFocus or (not ARecursive and (AControl.Parent <> AContainer)) then Continue; AContinue := True; if (AControl is TcxCustomTextEdit) then begin if DoCheckControlInContainer(AControl, AContinue) then Check(TcxCustomTextEdit(AControl)); if StopCheck(AContinue) then Break; end else if (AControl is TCustomEdit) then begin if DoCheckControlInContainer(GetInnerControlContainer(AControl), AContinue) then Check(TCustomEdit(AControl)); if StopCheck(AContinue) then Break; end; end; finally L.Free; end; end; function TdxCustomSpellChecker.DoCheckControlInContainer(AControl: TWinControl; var AContinue: Boolean): Boolean; begin Result := True; if Assigned(FOnCheckControlInContainer) then FOnCheckControlInContainer(Self, AControl, Result, AContinue); end; function TdxCustomSpellChecker.DoCheckWord(const AWord: WideString; var AValid: Boolean): Boolean; begin Result := False; if Assigned(FOnCheckWord) then FOnCheckWord(Self, AWord, AValid, Result); end; procedure TdxCustomSpellChecker.DoEnabledDictionariesLoaded; begin if Assigned(FOnEnabledDictionariesLoaded) then FOnEnabledDictionariesLoaded(Self); end; procedure TdxCustomSpellChecker.DoGetSuggestions(ASuggestions: TdxSpellCheckerSuggestionList); begin if Assigned(FOnGetSuggestions) then FOnGetSuggestions(Self, ASuggestions); end; function TdxCustomSpellChecker.DoSpellingComplete: Boolean; begin Result := False; if Assigned(FOnSpellingComplete) then FOnSpellingComplete(Self, Result); end; function TdxCustomSpellChecker.GetDialogCheckModeClass: TdxSpellCheckerCustomCheckModeClass; begin if sftWord = SpellingFormType then Result := TdxSpellCheckerWordCheckMode else Result := TdxSpellCheckerOutlookCheckMode; end; procedure TdxCustomSpellChecker.InternalCheck(AAdapter: TdxSpellCheckerCustomEditAdapter); begin if ActiveDictionaryCount = 0 then Exit; BeginManualCheck; try FCheckMode := GetDialogCheckModeClass.Create(Self, AAdapter); try DoCheck(FCheckMode as TdxSpellCheckerDialogCheckMode); finally FreeAndNil(FCheckMode); end; finally EndManualCheck; end; end; procedure TdxCustomSpellChecker.LoadDictionariesDirect( AIgnoreDisabled: Boolean = True); var I: Integer; ADictionary: TdxCustomSpellCheckerDictionary; begin ShowHourglassCursor; try for I := 0 to DictionaryCount - 1 do begin ADictionary := Dictionaries[I]; if ADictionary.Enabled or not AIgnoreDisabled then ADictionary.Load; end; finally HideHourglassCursor; end; end; procedure TdxCustomSpellChecker.LoadDictionariesUsingThread( AIgnoreDisabled: Boolean = True); var I: Integer; ADictionary: TdxCustomSpellCheckerDictionary; begin for I := 0 to DictionaryCount - 1 do begin ADictionary := Dictionaries[I]; if ADictionary.Enabled or not AIgnoreDisabled then ADictionary.LoadUsingThread; end; end; procedure TdxCustomSpellChecker.Loaded; begin inherited Loaded; if AutoLoadDictionaries and not (csDesigning in ComponentState) then LoadDictionaries; end; procedure TdxCustomSpellChecker.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if CheckAsYouTypeOptions <> nil then CheckAsYouTypeOptions.Notification(AComponent, Operation); end; procedure TdxCustomSpellChecker.SpellingOptionsChanged; begin if Assigned(CheckMode) then ValidateRules(CheckMode.Parser); if Assigned(CheckAsYouTypeManager) then CheckAsYouTypeManager.SpellingOptionsChanged; end; procedure TdxCustomSpellChecker.ValidateRules(AParser: TdxSpellCheckerTextParser); begin Rules.Clear; if Assigned(AParser) then begin SpellingOptions.PopulateRules(Rules, AParser); Rules.Add(TdxSpellCheckerWordExistsRule.Create(AParser)); end; end; procedure TdxCustomSpellChecker.UpdateByDictionary( ADictionary: TdxCustomSpellCheckerDictionary); begin if Assigned(CheckMode) then CheckMode.UpdateByDictionary(ADictionary); if Assigned(CheckAsYouTypeManager) then CheckAsYouTypeManager.Reset; end; procedure TdxCustomSpellChecker.UpdateCheckAsYouTypeManagerState; begin FreeAndNil(FCheckAsYouTypeManager); if CheckAsYouTypeOptions.Active then begin FCheckAsYouTypeManager := TdxSpellCheckerCheckAsYouTypeManager.Create(Self); FCheckAsYouTypeManager.DoOptionsChanged; end; end; procedure TdxCustomSpellChecker.SpellingComplete; begin if not FCheckGroupMode and not DoSpellingComplete then ShowSpellingCompleteMessage; end; function TdxCustomSpellChecker.GetActiveDictionary( Index: Integer): TdxCustomSpellCheckerDictionary; var I, J: Integer; ADictionary: TdxCustomSpellCheckerDictionary; begin Result := nil; J := 0; for I := 0 to DictionaryItems.Count - 1 do begin ADictionary := DictionaryItems[I].DictionaryType; if (ADictionary <> nil) and (ADictionary.Active) then begin if J = Index then begin Result := ADictionary; Exit; end; Inc(J); end; end; end; function TdxCustomSpellChecker.GetActiveDictionaryCount: Integer; var I: Integer; ADictionary: TdxCustomSpellCheckerDictionary; begin Result := 0; for I := 0 to DictionaryItems.Count - 1 do begin ADictionary := DictionaryItems[I].DictionaryType; if (ADictionary <> nil) and (ADictionary.Active) then Inc(Result); end; end; function TdxCustomSpellChecker.GetCheckMode: TdxSpellCheckerCustomCheckMode; begin Result := FCheckMode; if (Result = nil) and Assigned(CheckAsYouTypeManager) then Result := CheckAsYouTypeManager.GetCheckMode; end; function TdxCustomSpellChecker.GetDictionary( Index: Integer): TdxCustomSpellCheckerDictionary; var I, J: Integer; ADictionary: TdxCustomSpellCheckerDictionary; begin Result := nil; J := 0; for I := 0 to DictionaryItems.Count - 1 do begin ADictionary := DictionaryItems[I].DictionaryType; if ADictionary <> nil then begin if J = Index then begin Result := ADictionary; Exit; end; Inc(J); end; end; end; function TdxCustomSpellChecker.GetDictionaryCount: Integer; var I: Integer; begin Result := 0; for I := 0 to DictionaryItems.Count - 1 do if DictionaryItems[I].DictionaryType <> nil then Inc(Result); end; function TdxCustomSpellChecker.GetEnabledDictionary( Index: Integer): TdxCustomSpellCheckerDictionary; var I, J: Integer; ADictionary: TdxCustomSpellCheckerDictionary; begin Result := nil; J := 0; for I := 0 to DictionaryItems.Count - 1 do begin ADictionary := DictionaryItems[I].DictionaryType; if (ADictionary <> nil) and (ADictionary.Enabled) then begin if J = Index then begin Result := DictionaryItems[I].DictionaryType; Exit; end; Inc(J); end; end; end; function TdxCustomSpellChecker.GetEnabledDictionaryCount: Integer; var I: Integer; ADictionary: TdxCustomSpellCheckerDictionary; begin Result := 0; for I := 0 to DictionaryItems.Count - 1 do begin ADictionary := DictionaryItems[I].DictionaryType; if (ADictionary <> nil) and (ADictionary.Enabled) then Inc(Result); end; end; function TdxCustomSpellChecker.GetUserDictionary( Index: Integer): TdxUserSpellCheckerDictionary; var I, J: Integer; begin Result := nil; J := 0; for I := 0 to DictionaryItems.Count - 1 do begin if DictionaryItems[I].DictionaryType is TdxUserSpellCheckerDictionary then begin if J = Index then begin Result := TdxUserSpellCheckerDictionary(DictionaryItems[I].DictionaryType); Exit; end; Inc(J); end; end; end; function TdxCustomSpellChecker.GetUserDictionaryCount: Integer; var I: Integer; begin Result := 0; for I := 0 to DictionaryItems.Count - 1 do if DictionaryItems[I].DictionaryType is TdxUserSpellCheckerDictionary then Inc(Result); end; procedure TdxCustomSpellChecker.SetCheckAsYouTypeOptions( AValue: TdxSpellCheckerCheckAsYouTypeOptions); begin FCheckAsYouTypeOptions.Assign(AValue); end; procedure TdxCustomSpellChecker.SetDialogLookAndFeel(AValue: TcxLookAndFeel); begin FDialogLookAndFeel.Assign(AValue); end; procedure TdxCustomSpellChecker.SetDictionaryItems( AValue: TdxSpellCheckerDictionaries); begin FDictionaryItems.Assign(AValue); end; procedure TdxCustomSpellChecker.SetSpellingOptions( AValue: TdxSpellCheckerSpellingOptions); begin FSpellingOptions.Assign(AValue); end; initialization GetRegisteredDictionaryTypes.Register( TdxUserSpellCheckerDictionary, cxGetResourceString(@sdxSpellCheckerUserDictionary)); finalization FreeRegisteredDictionaryTypes; end.