{********************************************************************} { } { Developer Express Visual Component Library } { ExpressSpellChecker } { } { Copyright (c) 1998-2009 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, RichEdit, Menus, dxCore, cxControls, dxSpellCheckerRules, dxSpellCheckerAlgorithms, cxLookAndFeels, cxEdit, cxTextEdit, cxRichEdit; type TdxCustomSpellChecker = class; TdxSpellCheckerWordList = class; TdxSpellCheckerTextParser = class; TdxCustomSpellCheckerDictionary = class; TdxSpellCheckerSuggestionList = class; TdxSpellCheckerAutoCorrectExceptions = class; TdxSpellCheckerAutoCorrectOptions = class; TdxSpellCheckerCustomAutoCorrectManager = class; TdxSpellCheckerSpellingOptions = class; TdxSpellCheckerCheckAsYouTypeOptions = class; TdxSpellCheckerSuggestionBuilder = class; EdxSpellCheckerException = class(EdxException); { TdxSpellCheckerPersistent } TdxSpellCheckerPersistent = class(TPersistent) private FSpellChecker: TdxCustomSpellChecker; public constructor Create(ASpellChecker: TdxCustomSpellChecker); virtual; property SpellChecker: TdxCustomSpellChecker read FSpellChecker; end; { TdxSpellCheckerLanguages } TdxSpellCheckerLanguages = class private FSysLangs: array of TLangRec; function LocalesCallback(LocaleID: PChar): Integer; stdcall; function GetCount: integer; function GetExt(Index: Integer): string; function GetID(Index: Integer): string; function GetLocaleID(Index: Integer): LCID; overload; function GetName(Index: Integer): string; function GetNameFromLocaleID(ID: LCID): string; function GetNameFromLCID(const ID: string): string; public constructor Create; function GetDefaultLanguageLCID: LCID; function GetLCID(const AName: string): LCID; function IndexOf(ID: LCID): Integer; property Count: Integer read GetCount; property Name[Index: Integer]: string read GetName; property NameFromLocaleID[ID: LCID]: string read GetNameFromLocaleID; property NameFromLCID[const ID: string]: string read GetNameFromLCID; property ID[Index: Integer]: string read GetID; property LocaleID[Index: Integer]: LCID read GetLocaleID; property Ext[Index: Integer]: string read GetExt; end; { TdxSpellCheckerSuggestion } TdxSpellCheckerSuggestion = class private FDictionary: TdxCustomSpellCheckerDictionary; FDistance: Integer; FWord: WideString; FOrder: Integer; 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 SortByDistance; procedure SortByWord; procedure SaveToStrings(AStrings: TStrings); property Items[Index: Integer]: TdxSpellCheckerSuggestion read GetItem; default; end; { TdxSpellCheckerReplacement } TdxSpellCheckerReplacement = class private FReplacement: WideString; FText: WideString; public constructor Create(const AText, AReplacement: WideString); procedure ChangeReplacement(const S: WideString); property Replacement: WideString read FReplacement; property Text: WideString read FText; end; { TdxSpellCheckerReplacementList } TdxSpellCheckerReplacementList = class(TcxObjectList) private FAllowDuplicates: Boolean; function GetItem(Index: Integer): TdxSpellCheckerReplacement; public constructor Create(AllowDuplicates: Boolean = False); procedure Add(const AText, AReplacement: WideString); function FindReplacement(const AText: WideString): TdxSpellCheckerReplacement; procedure SortByText; property Items[Index: Integer]: TdxSpellCheckerReplacement read GetItem; default; end; { TdxSpellCheckerCustomEditAdapter } TdxSpellCheckerCustomEditAdapter = class protected FEdit: TWinControl; FNeedImmediatePost: Boolean; FSelStart: Integer; FSelLength: Integer; FText: WideString; 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; procedure GetSpellingBounds(out ASpellingStart, ASpellingEnd: Integer); virtual; property Edit: TWinControl read FEdit; 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; TdxSpellCheckerCustomEditAdapterClass = class of TdxSpellCheckerCustomEditAdapter; { 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; 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; end; { TdxSpellCheckerTextAdapter } TdxSpellCheckerTextAdapter = class(TdxSpellCheckerEditAdapter) public constructor Create(const AText: WideString); reintroduce; virtual; 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; FMeaningfulCharacters: WideString; FPrevWord: WideString; FSpellingEnd: Integer; FSpellingStart: Integer; FText: WideString; FUrlDelimiters: WideString; function IsDelimiter(AChar: WideChar): Boolean; function IsMeaningfulCharacters(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 MeaningfulCharacters: WideString read FMeaningfulCharacters; property Text: WideString read FText; property UrlDelimiters: WideString read FUrlDelimiters write FUrlDelimiters; 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(Index: 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[Index: 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; FMisspellingLen: Integer; FMisspellingStart: 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 RestoreSelection; virtual; procedure StoreSelection; 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 FCheckedRange: Integer; FSaveHideSelection: Boolean; FSaveSelLength: Integer; FSaveSelStart: Integer; FSelectionChecked: Boolean; FUndoManager: TdxSpellCheckerUndoManager; function GetLanguages: TdxSpellCheckerLanguages; protected procedure CreateParser; override; function IsCheckingSelectedText: Boolean; virtual; procedure RestoreSelection; override; procedure StoreSelection; 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 } TdxMetaphoneTable = array[Word] of PWideChar; PdxMetaphoneTable = ^TdxMetaphoneTable; TdxSpellCheckerWordList = class private FCount: Integer; FFindMostValidCapitalizationInDuplicates: Boolean; FLangID: Cardinal; FLock: TRTLCriticalSection; FMetaphone: TdxDoubleMetaphone; FMetaphoneTable: PdxMetaphoneTable; FTable: PPointerList; FTableSize: Integer; FUpperCaseBuffer: array[0..256] of WideChar; FUseDoubleMetaphone: Boolean; function GetCodePage: Cardinal; procedure SetUseDoubleMetaphone(AValue: Boolean); procedure UpdateMetaphoneInfo(ANewWord: PWideChar; ALength: Integer); procedure UpdateWordsMetaphone; protected function ElfHash(P: PWideChar; ALength: Integer): Integer; function FindWord(const S: WideString): PWideChar; function NewWord(S: PWideChar; ALength: Integer): PWideChar; {$IFDEF DELPHI9} inline; {$ENDIF} function AllocWord(ACharCount: Cardinal): PWideChar; procedure DisposeWord(AWord: PWideChar); function GetNextMetaphoneWord(AWord: PWideChar): Pointer; {$IFDEF DELPHI9} inline; {$ENDIF} function GetNextWord(AWord: PWideChar): Pointer; {$IFDEF DELPHI9} inline; {$ENDIF} procedure SetNextMetaphoneWord(AWord: PWideChar; AValue: Pointer); {$IFDEF DELPHI9} inline; {$ENDIF} procedure SetNextWord(AWord: PWideChar; AValue: Pointer); {$IFDEF DELPHI9} inline; {$ENDIF} function WordLength(AWord: PWideChar): Integer; {$IFDEF DELPHI9} inline; {$ENDIF} 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 PopulateMetaphoneSuggestions(ASuggestionBuilder: TdxSpellCheckerSuggestionBuilder; const AWord: WideString); procedure SaveToStrings(AStrings: TStrings); property Count: Integer read FCount; property FindMostValidCapitalizationInDuplicates: Boolean read FFindMostValidCapitalizationInDuplicates write FFindMostValidCapitalizationInDuplicates; property LangID: Cardinal read FLangID write FLangID; property UseDoubleMetaphone: Boolean read FUseDoubleMetaphone write SetUseDoubleMetaphone; end; { TdxSpellCheckerSuggestionBuilder } TdxSpellCheckerSuggestionBuilder = class private FStartTicks: Cardinal; FAlphabet: WideString; FDictionary: TdxCustomSpellCheckerDictionary; FSuggestions: TdxSpellCheckerSuggestionList; FWord: WideString; function GetTimeout: Cardinal; procedure SetAlphabet(const AValue: WideString); function GetMaxDistance: Integer; function GetSimilarity: TdxStringSimilarityCalculator; protected function CanAddToSuggestions(const ATestWord: WideString): Boolean; virtual; procedure CheckAddMetaphoneSuggestion(ATestWord, AUpperWord1: PWideChar; AUpperWordLen1: Integer; AUpperWord2: PWideChar; AUpperWordLen2: Integer); procedure DoAddMetaphoneSuggestions; virtual; procedure DoAddSuggestions; virtual; abstract; function IsTimeOut: Boolean; property Alphabet: WideString read FAlphabet write SetAlphabet; property Dictionary: TdxCustomSpellCheckerDictionary read FDictionary; property MaxDistance: Integer read GetMaxDistance; property Similarity: TdxStringSimilarityCalculator read GetSimilarity; property Suggestions: TdxSpellCheckerSuggestionList read FSuggestions; property Timeout: Cardinal read GetTimeout; 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); procedure CheckChangeOneLetter; procedure CheckDeleteLetter; procedure CheckDoubleTwoChars; procedure CheckInsertLetter; procedure CheckInsertSpace; procedure CheckMoveChar; procedure DoAddSuggestions; override; procedure InterchangeTwoLetters; procedure LongInterchangeTwoLetters; 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(TdxSpellCheckerPersistent) private FAlphabet: WideString; FCheckCapitalization: Boolean; FCodePage: Cardinal; FEnabled: Boolean; FLanguage: DWORD; FLoaded: Boolean; FLoadThread: TdxDictionaryLoadThread; FTerminated: Boolean; FTimeout: Cardinal; FWords: TdxSpellCheckerWordList; FOnLoading: TdxSpellCheckerDictionaryLoadingEvent; FOnLoaded: TdxSpellCheckerDictionaryLoadedEvent; function GetActive: Boolean; function GetWordCount: Integer; procedure SetEnabled(AValue: Boolean); procedure SetCheckCapitalization(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; function IsCorrectCapitalization(const AWord, APattern: WideString): Boolean; virtual; function IsEnglishLanguage: Boolean; procedure LoadingComplete; virtual; function LoadingTerminated: Boolean; {$IFDEF DELPHI9} inline; {$ENDIF} procedure LoadUsingThread; virtual; procedure Terminate; procedure ThreadDone(Sender: TObject); virtual; procedure Update; virtual; procedure UpdateLoadedOnLoadEvent; procedure UpdateUsingMetaphone; property LoadThread: TdxDictionaryLoadThread read FLoadThread; property Timeout: Cardinal read FTimeout write FTimeout; property Words: TdxSpellCheckerWordList read FWords; public constructor Create(ASpellChecker: TdxCustomSpellChecker); override; 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 CheckCapitalization: Boolean read FCheckCapitalization write SetCheckCapitalization default False; 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 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 CheckCapitalization; 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; { TdxSpellCheckerCustomOptions } TdxSpellCheckerCustomOptions = class(TdxSpellCheckerPersistent) protected procedure InitializeOptions; virtual; public constructor Create(ASpellChecker: TdxCustomSpellChecker); override; end; { TdxSpellCheckerAutoCorrectExceptions } TdxSpellCheckerAutoCorrectExceptionAddEvent = procedure (Sender: TdxSpellCheckerAutoCorrectExceptions; var AException: WideString; var AAllow: Boolean) of object; TdxSpellCheckerAutoCorrectExceptionDeleteEvent = procedure (Sender: TdxSpellCheckerAutoCorrectExceptions; const AException: WideString; var AAllow: Boolean) of object; TdxSpellCheckerAutoCorrectExceptions = class private FAutoInclude: Boolean; FCount: Integer; FList: PPointerList; FLock: TRTLCriticalSection; FOptions: TdxSpellCheckerAutoCorrectOptions; FOnAdd: TdxSpellCheckerAutoCorrectExceptionAddEvent; FOnDelete: TdxSpellCheckerAutoCorrectExceptionDeleteEvent; function GetException(Index: Integer): WideString; protected procedure DisposeException(AException: PWideChar); function DoAdd(var AException: WideString): Boolean; function DoDelete(const AException: WideString): Boolean; procedure SetCount(AValue: Integer); virtual; property OnAdd: TdxSpellCheckerAutoCorrectExceptionAddEvent read FOnAdd write FOnAdd; property OnDelete: TdxSpellCheckerAutoCorrectExceptionDeleteEvent read FOnDelete write FOnDelete; public constructor Create(AOptions: TdxSpellCheckerAutoCorrectOptions); virtual; destructor Destroy; override; function Add(const AException: WideString): Integer; procedure Clear; procedure Delete(AIndex: Integer); overload; procedure Delete(const AException: WideString); overload; function Find(AException: WideString): Integer; procedure LoadFromFile(const AFileName: WideString); procedure LoadFromStream(AStream: TStream); procedure PopulateExceptions(AList: TStrings); procedure SaveToFile(const AFileName: WideString); procedure SaveToStream(AStream: TStream); property AutoInclude: Boolean read FAutoInclude write FAutoInclude; property Count: Integer read FCount; property Exceptions[Index: Integer]: WideString read GetException; default; end; { TdxSpellCheckerAutoCorrectReplacementList } TdxSpellCheckerAutoCorrectReplacementList = class(TdxSpellCheckerReplacementList) public procedure LoadFromFile(const AFileName: WideString); procedure LoadFromStream(AStream: TStream); procedure SaveToFile(const AFileName: WideString); procedure SaveToStream(AStream: TStream); end; { TdxSpellCheckerAutoCorrectOptions } TdxSpellCheckerAutoCorrectOptionsChangedEvent = procedure (Sender: TdxSpellCheckerAutoCorrectOptions) of object; TdxSpellCheckerAutoCorrectOptions = class(TdxSpellCheckerCustomOptions) private FActive: Boolean; FAutomaticallyUseSuggestions: Boolean; FCorrectCapsLock: Boolean; FCorrectInitialCaps: Boolean; FCorrectSentenceCaps: Boolean; FDisableCapsLock: Boolean; FFirstLetterExceptions: TdxSpellCheckerAutoCorrectExceptions; FInitialCapsExceptions: TdxSpellCheckerAutoCorrectExceptions; FReplacements: TdxSpellCheckerAutoCorrectReplacementList; FReplaceTextAsYouType: Boolean; FOnChanged: TdxSpellCheckerAutoCorrectOptionsChangedEvent; procedure FirstLetterExceptionAddHandler(Sender: TdxSpellCheckerAutoCorrectExceptions; var AException: WideString; var AAllow: Boolean); procedure InitialCapsExceptionAddHandler(Sender: TdxSpellCheckerAutoCorrectExceptions; var AException: WideString; var AAllow: Boolean); procedure SetActive(const AValue: Boolean); procedure SetAutomaticallyUseSuggestions(const AValue: Boolean); procedure SetCorrectCapsLock(const AValue: Boolean); procedure SetCorrectInitialCaps(const AValue: Boolean); procedure SetCorrectSentenceCaps(const AValue: Boolean); procedure SetDisableCapsLock(const AValue: Boolean); procedure SetReplaceTextAsYouType(const AValue: Boolean); protected procedure Changed; virtual; procedure DoChanged; virtual; procedure InitializeOptions; override; public destructor Destroy; override; procedure Assign(Source: TPersistent); override; function GetReplacement(const AText: WideString; out AReplacement: WideString): Boolean; virtual; property FirstLetterExceptions: TdxSpellCheckerAutoCorrectExceptions read FFirstLetterExceptions; property InitialCapsExceptions: TdxSpellCheckerAutoCorrectExceptions read FInitialCapsExceptions; property Replacements: TdxSpellCheckerAutoCorrectReplacementList read FReplacements; property OnChanged: TdxSpellCheckerAutoCorrectOptionsChangedEvent read FOnChanged write FOnChanged; published property Active: Boolean read FActive write SetActive default False; property AutomaticallyUseSuggestions: Boolean read FAutomaticallyUseSuggestions write SetAutomaticallyUseSuggestions default True; property CorrectCapsLock: Boolean read FCorrectCapsLock write SetCorrectCapsLock default True; property CorrectInitialCaps: Boolean read FCorrectInitialCaps write SetCorrectInitialCaps default True; property CorrectSentenceCaps: Boolean read FCorrectSentenceCaps write SetCorrectSentenceCaps default True; property DisableCapsLock: Boolean read FDisableCapsLock write SetDisableCapsLock default False; property ReplaceTextAsYouType: Boolean read FReplaceTextAsYouType write SetReplaceTextAsYouType default True; end; { TdxSpellCheckerSpellingOptions } TdxSpellCheckerSpellingOptionsChangedEvent = procedure (Sender: TdxSpellCheckerSpellingOptions) of object; TdxSpellCheckerSpellingOptions = class(TdxSpellCheckerCustomOptions) private 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 InitializeOptions; override; procedure PopulateRules(ARules: TdxSpellCheckerRules; AParser: TdxSpellCheckerTextParser); virtual; property IgnoreMarkupTags: Boolean read FIgnoreMarkupTags write SetIgnoreMarkupTags default True; public 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, scmiAutoCorrect, scmiDelete, scmiIgnoreAll, scmiSpelling, scmiSuggestions); TdxSpellCheckerPopupMenuItems = set of TdxSpellCheckerPopupMenuItem; TdxSpellCheckerCheckAsYouTypeOptionsChangedEvent = procedure( ASender: TdxSpellCheckerCheckAsYouTypeOptions) of object; TdxSpellCheckerCheckAsYouTypeOptions = class(TdxSpellCheckerCustomOptions) private FActive: Boolean; FModifyControlPopupMenu: Boolean; FPopupMenu: TComponent; FPopupMenuItems: TdxSpellCheckerPopupMenuItems; 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 InitializeOptions; override; procedure Notification(AComponent: TComponent; AOperation: TOperation); virtual; public 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, scmiAutoCorrect, 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; { TdxSpellCheckerCustomManager } TdxSpellCheckerCustomManager = class(TdxSpellCheckerPersistent) private FCheckMode: TdxSpellCheckerCustomCheckMode; FContainer: TcxCustomEdit; function GetEdit: TWinControl; procedure SetEdit(const AValue: TWinControl); protected FAdapter: TdxSpellCheckerCustomEditAdapter; procedure CreateAdapter(AAdapterClass: TdxSpellCheckerCustomEditAdapterClass; AControl: TWinControl); virtual; procedure DoActiveChanged; virtual; abstract; procedure DoFinalizeController; virtual; procedure DoFinish(AControl: TWinControl); virtual; abstract; procedure DoOptionsChanged; virtual; abstract; function DoStart(AControl: TWinControl): Boolean; virtual; abstract; function GetActive: Boolean; virtual; function GetAdapterClass(AControl: TWinControl): TdxSpellCheckerCustomEditAdapterClass; virtual; function GetCheckModeClass: TdxSpellCheckerCustomCheckModeClass; virtual; procedure FinalizeController; virtual; procedure InitializeController(AControl: TWinControl); virtual; function IsCheckSupported(AControl: TWinControl): Boolean; procedure ValidateAdapter; virtual; property Container: TcxCustomEdit read FContainer; public constructor Create(ASpellChecker: TdxCustomSpellChecker); override; procedure CheckFinish; virtual; procedure CheckStart(AControl: TWinControl); virtual; property Active: Boolean read GetActive; property Adapter: TdxSpellCheckerCustomEditAdapter read FAdapter; property CheckMode: TdxSpellCheckerCustomCheckMode read FCheckMode; property Edit: TWinControl read GetEdit write SetEdit; end; { TdxSpellCheckerAutoCorrectCustomRule } TdxSpellCheckerAutoCorrectCustomRule = class private FManager: TdxSpellCheckerCustomAutoCorrectManager; FWordDelimiters: WideString; function GetLastKey: WideChar; function GetOptions: TdxSpellCheckerAutoCorrectOptions; protected function GetActive: Boolean; virtual; procedure InitializeDelimiters; virtual; abstract; procedure SetWordDelimiters(const AWordDelimiters: WideString); function WideCapitalizeCase(const AWord: WideString): WideString; property LastKey: WideChar read GetLastKey; public constructor Create(AManager: TdxSpellCheckerCustomAutoCorrectManager); virtual; procedure AfterCorrect; virtual; procedure BeforeCorrect; virtual; function IsCheckWord(const AText: WideString; var AStart, ALength: Integer; var AWord: WideString): Boolean; virtual; abstract; function IsTerminating: Boolean; virtual; function IsWordDelimiter(AChar: WideChar): Boolean; procedure Undo; virtual; property Active: Boolean read GetActive; property Manager: TdxSpellCheckerCustomAutoCorrectManager read FManager; property Options: TdxSpellCheckerAutoCorrectOptions read GetOptions; property WordDelimiters: WideString read FWordDelimiters; end; { TdxSpellCheckerCustomAutoCorrectManager } TdxSpellCheckerCustomAutoCorrectManager = class(TdxSpellCheckerCustomManager) private FLastKey: WideChar; function GetOptions: TdxSpellCheckerAutoCorrectOptions; protected procedure CreateAdapter(AAdapterClass: TdxSpellCheckerCustomEditAdapterClass; AControl: TWinControl); override; function DoAutoCorrect(ARule: TdxSpellCheckerAutoCorrectCustomRule; var AWordRange: TdxSpellCheckerAutoCorrectWordRange): Boolean; virtual; procedure DoFinish(AControl: TWinControl); override; function DoStart(AControl: TWinControl): Boolean; override; function GetActive: Boolean; override; function GetAdapterClass( AControl: TWinControl): TdxSpellCheckerCustomEditAdapterClass; override; procedure Undo; virtual; function WideCapitalizeCase(const AWord: WideString): WideString; property LastKey: WideChar read FLastKey; public procedure KeyPress(AKey: Char); virtual; property Options: TdxSpellCheckerAutoCorrectOptions read GetOptions; end; { TdxSpellCheckerCustomCheckAsYouTypeManager } TdxSpellCheckerCustomCheckAsYouTypeManager = class(TdxSpellCheckerCustomManager) private FManualCheckCount: Integer; function GetIsManualCheckingActive: Boolean; function GetIsSpellCheckerReady: Boolean; function GetOptions: TdxSpellCheckerCheckAsYouTypeOptions; protected procedure DoFinish(AControl: TWinControl); override; function DoStart(AControl: TWinControl): Boolean; override; function GetActive: Boolean; override; procedure InnerShowPopupMenu(APopup: TComponent; const P: TPoint); procedure Reset; virtual; procedure SpellingOptionsChanged; virtual; procedure StartManualSpelling(AAdapter: TdxSpellCheckerCustomEditAdapter); virtual; procedure ValidateRules(AParser: TdxSpellCheckerTextParser); virtual; public procedure BeginManualCheck; virtual; procedure CheckFinish; override; procedure CheckStart(AControl: TWinControl); override; procedure EndManualCheck; virtual; procedure Refresh(AFullRefresh: Boolean = False); virtual; abstract; procedure DrawMisspellings(AControl: TWinControl); 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 IsManualCheckingActive: Boolean read GetIsManualCheckingActive; property IsSpellCheckerReady: Boolean read GetIsSpellCheckerReady; property Options: TdxSpellCheckerCheckAsYouTypeOptions read GetOptions; 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; TdxSpellCheckerAutoCorrectEvent = procedure (Sender: TdxCustomSpellChecker; ARule: TdxSpellCheckerAutoCorrectCustomRule; var AWordRange: TdxSpellCheckerAutoCorrectWordRange; var AAllow: Boolean) of object; TdxSpellCheckerEnabledDictionariesLoadedEvent = procedure (Sender: TdxCustomSpellChecker; const AWord: WideString; var AHandled: Boolean) of object; TdxSpellCheckerSpellingComplete = procedure (Sender: TdxCustomSpellChecker; var AHandled: Boolean) of object; TdxSpellCheckerCheckFinishEvent = procedure (Sender: TdxCustomSpellChecker; AControl: TWinControl) of object; TdxSpellCheckerCheckStartEvent = procedure (Sender: TdxCustomSpellChecker; AControl: TWinControl; var AAllow: Boolean) of object; TdxSpellCheckerCheckAsYouTypePopupEvent = procedure(ASender: TdxCustomSpellChecker; APopupMenu: TComponent; var AHandled: Boolean) of object; TdxSpellCheckerCheckControlInContainerEvent = procedure (Sender: TdxCustomSpellChecker; AControl: TWinControl; var AAllow: Boolean; var AContinue: Boolean) of object; TdxSpellCheckerGetSuggestionsEvent = procedure (Sender: TdxCustomSpellChecker; const AWord: WideString; ASuggestions: TdxSpellCheckerSuggestionList) of object; TdxCustomSpellChecker = class(TComponent, IdxSkinSupport, IdxSpellChecker) private FAutoCorrectManager: TdxSpellCheckerCustomAutoCorrectManager; FAutoCorrectOptions: TdxSpellCheckerAutoCorrectOptions; 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; FMetaphoneDistance: Integer; FRules: TdxSpellCheckerRules; FSimilarity: TdxStringSimilarityCalculator; FSpellingFormType: TdxSpellCheckerSpellingFormType; FSpellingOptions: TdxSpellCheckerSpellingOptions; FThreadedLoadPriority: TThreadPriority; FUseThreadedLoad: Boolean; FOnAddWord: TdxSpellCheckerAddWordEvent; FOnAfterCheck: TdxSpellCheckerEvent; FOnAutoCorrect: TdxSpellCheckerAutoCorrectEvent; FOnAutoCorrectFinish: TdxSpellCheckerCheckFinishEvent; FOnAutoCorrectStart: TdxSpellCheckerCheckStartEvent; FOnBeforeCheck: TdxSpellCheckerEvent; FOnCheckAsYouTypeFinish: TdxSpellCheckerCheckFinishEvent; FOnCheckAsYouTypeStart: TdxSpellCheckerCheckStartEvent; 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 SetAutoCorrectOptions(AValue: TdxSpellCheckerAutoCorrectOptions); procedure SetCheckAsYouTypeOptions(AValue: TdxSpellCheckerCheckAsYouTypeOptions); procedure SetDialogLookAndFeel(AValue: TcxLookAndFeel); procedure SetDictionaryItems(AValue: TdxSpellCheckerDictionaries); procedure SetMetaphoneDistance(AValue: Integer); 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; function DoAutoCorrect(ARule: TdxSpellCheckerAutoCorrectCustomRule; var AWordRange: TdxSpellCheckerAutoCorrectWordRange): Boolean; virtual; procedure DoAutoCorrectFinish(AControl: TWinControl); virtual; function DoAutoCorrectStart(AControl: TWinControl): Boolean; 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(const AWord: WideString; 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; // IdxSpellCheckerEditHelper procedure CheckFinish; procedure CheckStart(AControl: TWinControl); procedure DrawMisspellings(AControl: TWinControl); function IsSpellCheckerDialogControl(AWnd: THandle): Boolean; procedure KeyPress(AKey: Char); function QueryPopup(APopup: TComponent; const P: TPoint): Boolean; procedure LayoutChanged(AControl: TWinControl); procedure SelectionChanged(AControl: TWinControl); procedure TextChanged(AControl: TWinControl); procedure Undo; property AutoCorrectManager: TdxSpellCheckerCustomAutoCorrectManager read FAutoCorrectManager; 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 Similarity: TdxStringSimilarityCalculator read FSimilarity; property OnAddWord: TdxSpellCheckerAddWordEvent read FOnAddWord write FOnAddWord; property OnAfterCheck: TdxSpellCheckerEvent read FOnAfterCheck write FOnAfterCheck; property OnAutoCorrect: TdxSpellCheckerAutoCorrectEvent read FOnAutoCorrect write FOnAutoCorrect; property OnAutoCorrectFinish: TdxSpellCheckerCheckFinishEvent read FOnAutoCorrectFinish write FOnAutoCorrectFinish; property OnAutoCorrectStart: TdxSpellCheckerCheckStartEvent read FOnAutoCorrectStart write FOnAutoCorrectStart; property OnBeforeCheck: TdxSpellCheckerEvent read FOnBeforeCheck write FOnBeforeCheck; property OnCheckAsYouTypeFinish: TdxSpellCheckerCheckFinishEvent read FOnCheckAsYouTypeFinish write FOnCheckAsYouTypeFinish; property OnCheckAsYouTypePopup: TdxSpellCheckerCheckAsYouTypePopupEvent read FOnCheckAsYouTypePopup write FOnCheckAsYouTypePopup; property OnCheckAsYouTypeStart: TdxSpellCheckerCheckStartEvent 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(var AText: AnsiString); overload; procedure Check(var AText: WideString); overload; 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 AutoCorrectOptions: TdxSpellCheckerAutoCorrectOptions read FAutoCorrectOptions write SetAutoCorrectOptions; 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 MetaphoneDistance: Integer read FMetaphoneDistance write SetMetaphoneDistance default 3; property Rules: TdxSpellCheckerRules read FRules; property SpellingFormType: TdxSpellCheckerSpellingFormType read FSpellingFormType write FSpellingFormType default sftOutlook; property SpellingOptions: TdxSpellCheckerSpellingOptions read FSpellingOptions write SetSpellingOptions; property ThreadedLoadPriority: TThreadPriority read FThreadedLoadPriority write FThreadedLoadPriority default tpLower; 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 AutoCorrectOptions; property AutoLoadDictionaries; property CheckAsYouTypeOptions; property DialogLookAndFeel; property DictionaryItems; property MetaphoneDistance; property SpellingFormType; property SpellingOptions; property ThreadedLoadPriority; property UseThreadedLoad; property OnAddWord; property OnAfterCheck; property OnAutoCorrect; property OnAutoCorrectFinish; property OnAutoCorrectStart; 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, {$IFDEF DELPHI12} AnsiStrings, {$ENDIF} cxContainer, dxSpellCheckerAutoCorrect, dxSpellCheckerBaseForm, StrUtils, RTLConsts; const dxCharToCharEntity: array [0..3, 0..1] of WideString = ( ('&', '&'), ('"', '"'), ('<', '<'), ('>', '>')); dxBeforeSelectionChecked = 1; dxSelectionChecked = 2; dxAfterSelectionChecked = 3; var FRegisteredDictionaryTypes: TcxRegisteredClasses; type TcxCustomTextEditPropertiesAccess = class(TcxCustomTextEditProperties); TdxXMLNodes = class; { TdxXMLNode } TdxXMLNode = class(TPersistent) private FAttributes: TStringList; FChildList: TdxXMLNodes; FNodeName: WideString; FNameSpaces: WideString; FValues: TStringList; function GetChildCount: Integer; protected function FindAttribute(const AName: WideString): Integer; public constructor Create; virtual; destructor Destroy; override; procedure Assign(Source: TPersistent); override; procedure AddAttribute(const AName, AValue: WideString); procedure AddChild(ANode: TdxXMLNode); function FindChild(const ANodeName: WideString): TdxXMLNode; procedure FromStr(const S: WideString; var APos: Integer); function GetValue(const AAttribute: WideString): WideString; function HasChild: Boolean; procedure SetAttribute(const AName, AValue: WideString); function ToStr: UTF8String; property Attributes: TStringList read FAttributes; property ChildCount: Integer read GetChildCount; property ChildList: TdxXMLNodes read FChildList; property NameSpaces: WideString read FNameSpaces write FNameSpaces; property NodeName: WideString read FNodeName write FNodeName; property Values: TStringList read FValues; end; { TdxXMLNodes } TdxXMLNodes = class(TcxObjectList) private function GetItem(Index: Integer): TdxXMLNode; public property Items[Index: Integer]: TdxXMLNode read GetItem; default; end; { TdxXMLDocument } TdxXMLDocument = class(TPersistent) private FEncoding: WideString; FNodes: TdxXMLNodes; public constructor Create; virtual; destructor Destroy; override; function FindNode(const ANodeName: WideString): TdxXMLNode; procedure LoadFromFile(const AFileName: WideString); procedure LoadFromStream(AStream: TStream); procedure SaveToFile(const AFileName: WideString); procedure SaveToStream(AStream: TStream); property Encoding: WideString read FEncoding write FEncoding; property Nodes: TdxXMLNodes read FNodes; end; 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; procedure SpellCheckerCreatingError(APointer: Pointer); begin raise EdxSpellCheckerException.Create(cxGetResourceString(APointer)); end; procedure SkipSpace(const S: WideString; var APos: Integer); var ALen: Integer; begin ALen := Length(S); while (APos < ALen) and WideIsSpace(S[APos]) do Inc(APos); end; function GetNextPart(const S: WideString; var APos: Integer; ADelimiter: WideChar; ACanBeSpace: Boolean = True): WideString; var ALen: Integer; begin ALen := Length(S); Result := ''; while (APos < ALen) and not ((ADelimiter = S[APos]) or (ACanBeSpace and WideIsSpace(S[APos]))) do begin Result := Result + S[APos]; Inc(APos); end; if ALen > APos then Inc(APos); end; function XMLValueToValue(const S: WideString): WideString; function ReplaceCharEntry: WideString; var I: Integer; begin Result := S; for I := 0 to 3 do Result := AnsiReplaceText(Result, dxCharToCharEntity[I, 1], dxCharToCharEntity[I, 0]); end; var APos: Integer; ATmp: WideString; AChar: WideChar; AOldPos: Integer; begin Result := ReplaceCharEntry; APos := WidePos('&', Result); while APos > 0 do begin AOldPos := APos; Inc(APos); ATmp := GetNextPart(Result, APos, ';', False); if Length(ATmp) > 0 then begin AChar := #0; if ATmp[1] = '#' then AChar := WideChar(StrToInt(Copy(ATmp, 2, Length(ATmp) - 1))); ATmp := Result; Result := Copy(ATmp, 1, AOldPos - 1) + AChar; if APos < Length(ATmp) then Result := Result + Copy(ATmp, APos, Length(ATmp) - APos + 1); end; APos := AnsiPos('&', Result); end; end; function ValueToXMLValue(const S: WideString): UTF8String; var I: Integer; AStr: WideString; begin AStr := S; for I := 0 to 3 do AStr := AnsiReplaceText(AStr, dxCharToCharEntity[I, 0], dxCharToCharEntity[I, 1]); Result := UTF8Encode(AStr); end; { TdxCustomSpellCheckerDictionary } constructor TdxCustomSpellCheckerDictionary.Create(ASpellChecker: TdxCustomSpellChecker); begin inherited Create(ASpellChecker); FEnabled := True; FCodePage := CP_ACP; FWords := TdxSpellCheckerWordList.Create(CodePage, 269683); Language := GetSystemDefaultLangID; FTimeout := 0; end; destructor TdxCustomSpellCheckerDictionary.Destroy; begin Terminate; 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; FTimeout := TdxCustomSpellCheckerDictionary(Source).Timeout; 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; function TdxCustomSpellCheckerDictionary.IsCorrectCapitalization( const AWord, APattern: WideString): Boolean; begin case GetWordCapitalizationType(APattern) of ctLower: Result := GetWordCapitalizationType(AWord) in [ctLower,ctCapitalized, ctUpper]; ctUpper: Result := GetWordCapitalizationType(AWord) = ctUpper; ctCapitalized: Result := GetWordCapitalizationType(AWord) in [ctCapitalized, ctUpper]; ctMixed: Result := (AWord = APattern) or (GetWordCapitalizationType(AWord) = ctUpper); else Result := True; end; end; function TdxCustomSpellCheckerDictionary.IsEnglishLanguage: Boolean; begin Result := (Language and $3FF) = LANG_ENGLISH; end; procedure TdxCustomSpellCheckerDictionary.LoadingComplete; begin DoLoadedEvent; Update; end; function TdxCustomSpellCheckerDictionary.LoadingTerminated: Boolean; begin Result := FTerminated or (FLoadThread <> nil) and FLoadThread.Terminated; end; procedure TdxCustomSpellCheckerDictionary.LoadUsingThread; begin if not CanLoad then Exit; FreeAndNil(FLoadThread); FLoadThread := TdxDictionaryLoadThread.Create(Self); end; procedure TdxCustomSpellCheckerDictionary.Terminate; begin FTerminated := True; 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; procedure TdxCustomSpellCheckerDictionary.UpdateUsingMetaphone; begin FWords.UseDoubleMetaphone := IsEnglishLanguage and (SpellChecker.MetaphoneDistance > 1); 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; var AWordInDictionary: PWideChar; begin AWordInDictionary := FWords.FindWord(AWord); Result := (AWordInDictionary <> nil); if Result and CheckCapitalization then Result := IsCorrectCapitalization(AWord, AWordInDictionary); end; procedure TdxCustomSpellCheckerDictionary.SetEnabled(AValue: Boolean); begin if FEnabled <> AValue then begin FEnabled := AValue; if Loaded then Update; end; end; procedure TdxCustomSpellCheckerDictionary.SetCheckCapitalization(AValue: Boolean); begin if FCheckCapitalization <> AValue then begin FCheckCapitalization := AValue; Words.FindMostValidCapitalizationInDuplicates := AValue; if Loaded then begin Unload; Load; Update; end; 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; Words.LangID := FLanguage; UpdateUsingMetaphone; 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 := SysUtils.ExtractFileName(DictionaryPath); if AFileName <> '' then Result := Format('%s (%s)', [Result, AFileName]); end; { TdxSpellCheckerPersistent } constructor TdxSpellCheckerPersistent.Create(ASpellChecker: TdxCustomSpellChecker); begin inherited Create; FSpellChecker := ASpellChecker; end; { TdxDictionaryLoadThread } constructor TdxDictionaryLoadThread.Create( ADictionary: TdxCustomSpellCheckerDictionary); begin inherited Create(False); FDictionary := ADictionary; Priority := Dictionary.SpellChecker.ThreadedLoadPriority; 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; { TdxSpellCheckerCustomOptions } constructor TdxSpellCheckerCustomOptions.Create(ASpellChecker: TdxCustomSpellChecker); begin inherited Create(ASpellChecker); InitializeOptions; end; procedure TdxSpellCheckerCustomOptions.InitializeOptions; begin end; { TdxSpellCheckerAutoCorrectExceptions } constructor TdxSpellCheckerAutoCorrectExceptions.Create(AOptions: TdxSpellCheckerAutoCorrectOptions); begin inherited Create; InitializeCriticalSection(FLock); FOptions := AOptions; FAutoInclude := True; FCount := 0; end; destructor TdxSpellCheckerAutoCorrectExceptions.Destroy; begin Clear; DeleteCriticalSection(FLock); inherited Destroy; end; function TdxSpellCheckerAutoCorrectExceptions.Add(const AException: WideString): Integer; var ALen: Integer; S: WideString; begin S := AException; if not DoAdd(S) then begin Result := -1; Exit; end; ALen := Length(S); if ALen = 0 then begin Result := -1; Exit; end; Result := Find(S); if Result = -1 then begin EnterCriticalSection(FLock); try Result := Count; SetCount(Count + 1); FList^[Result] := AllocMem((ALen + 1) * SizeOf(WideChar)); Move(PWideChar(S)^, FList^[Result]^, ALen * SizeOf(WideChar)); finally LeaveCriticalSection(FLock); end; end; end; procedure TdxSpellCheckerAutoCorrectExceptions.Clear; var I: Integer; begin if Count = 0 then Exit; EnterCriticalSection(FLock); try for I := 0 to FCount - 1 do DisposeException(FList^[I]); SetCount(0); finally LeaveCriticalSection(FLock); end; end; procedure TdxSpellCheckerAutoCorrectExceptions.Delete(AIndex: Integer); var I: Integer; begin if (AIndex < 0) or (AIndex >= Count) or (FList[AIndex] = nil) or not DoDelete(PWideChar(FList^[AIndex])) then Exit; DisposeException(FList^[AIndex]); for I := AIndex to Count - 2 do FList^[I] := FList^[I + 1]; SetCount(Count - 1); end; procedure TdxSpellCheckerAutoCorrectExceptions.Delete(const AException: WideString); begin Delete(Find(AException)); end; function TdxSpellCheckerAutoCorrectExceptions.Find(AException: WideString): Integer; var I: Integer; begin Result := -1; for I := 0 to Count - 1 do if WideCompareStr(AException, Exceptions[I]) = 0 then begin Result := I; Break; end; end; procedure TdxSpellCheckerAutoCorrectExceptions.LoadFromFile(const AFileName: WideString); var AStream: TStream; begin AStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); try LoadFromStream(AStream); finally AStream.Free; end; end; procedure TdxSpellCheckerAutoCorrectExceptions.LoadFromStream(AStream: TStream); var ADocument: TdxXMLDocument; ANode, ARootNode: TdxXMLNode; I: Integer; begin Clear; ADocument := TdxXMLDocument.Create; try ADocument.LoadFromStream(AStream); ARootNode := ADocument.FindNode('block-list:block-list'); if ARootNode <> nil then for I := 0 to ARootNode.ChildCount - 1 do begin ANode := ARootNode.ChildList[I]; Add(ANode.GetValue('block-list:abbreviated-name')); end; finally FreeAndNil(ADocument); end; end; procedure TdxSpellCheckerAutoCorrectExceptions.PopulateExceptions(AList: TStrings); var I: Integer; begin if AList = nil then Exit; for I := 0 to Count - 1 do AList.Add(Exceptions[I]); end; procedure TdxSpellCheckerAutoCorrectExceptions.SaveToFile(const AFileName: WideString); var AStream: TStream; begin AStream := TFileStream.Create(AFileName, fmCreate); try SaveToStream(AStream); finally AStream.Free; end; end; procedure TdxSpellCheckerAutoCorrectExceptions.SaveToStream(AStream: TStream); var ADocument: TdxXMLDocument; ANode, ARootNode: TdxXMLNode; I: Integer; begin ADocument := TdxXMLDocument.Create; try ADocument.Encoding := 'UTF-8'; ARootNode := TdxXMLNode.Create; ARootNode.NodeName := 'block-list:block-list'; ARootNode.NameSpaces := 'http://www.devexpress.com'; ADocument.Nodes.Add(ARootNode); for I := 0 to Count - 1 do begin ANode := TdxXMLNode.Create; ANode.NodeName := 'block-list:block'; ANode.AddAttribute('block-list:abbreviated-name', Exceptions[I]); ARootNode.AddChild(ANode); end; ADocument.SaveToStream(AStream); finally FreeAndNil(ADocument); end; end; procedure TdxSpellCheckerAutoCorrectExceptions.DisposeException(AException: PWideChar); begin if AException <> nil then FreeMem(AException, Cardinal(Pointer(AException)^)); end; function TdxSpellCheckerAutoCorrectExceptions.DoAdd(var AException: WideString): Boolean; begin Result := True; if Assigned(OnAdd) then OnAdd(Self, AException, Result); end; function TdxSpellCheckerAutoCorrectExceptions.DoDelete(const AException: WideString): Boolean; begin Result := True; if Assigned(OnDelete) then OnDelete(Self, AException, Result); end; procedure TdxSpellCheckerAutoCorrectExceptions.SetCount(AValue: Integer); begin FCount := AValue; if FCount > 0 then FList := ReallocMemory(FList, FCount * SizeOf(Cardinal)) else if FList <> nil then begin FreeMem(FList); FList := nil; end; end; function TdxSpellCheckerAutoCorrectExceptions.GetException( Index: Integer): WideString; begin if (Index < 0) or (Index > Count - 1) then TList.Error(@SListIndexError, Index); Result := PWideChar(FList^[Index]); end; { TdxSpellCheckerReplacement } constructor TdxSpellCheckerReplacement.Create( const AText, AReplacement: WideString); begin inherited Create; FText := AText; FReplacement := AReplacement; end; procedure TdxSpellCheckerReplacement.ChangeReplacement(const S: WideString); begin FReplacement := S; 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: TdxSpellCheckerReplacement; begin if FAllowDuplicates then inherited Add(TdxSpellCheckerReplacement.Create(AText, AReplacement)) else begin AItem := FindReplacement(AText); if AItem = nil then begin inherited Add(TdxSpellCheckerReplacement.Create(AText, AReplacement)); SortByText; end else AItem.FReplacement := AReplacement; end; end; function TdxSpellCheckerReplacementList.FindReplacement( const AText: WideString): TdxSpellCheckerReplacement; 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(TdxSpellCheckerReplacement(List^[I]).Text, AText); if C < 0 then L := I + 1 else begin H := I - 1; if C = 0 then begin Result := TdxSpellCheckerReplacement(List^[I]); Break; end; end; end; end; function TdxSpellCheckerReplacementList.GetItem( Index: Integer): TdxSpellCheckerReplacement; begin Result := TdxSpellCheckerReplacement(inherited Items[Index]); end; function CompareReplacementByText(Item1, Item2: Pointer): Integer; begin Result := WideCompareText(TdxSpellCheckerReplacement(Item1).Text, TdxSpellCheckerReplacement(Item2).Text); end; procedure TdxSpellCheckerReplacementList.SortByText; begin Sort(CompareReplacementByText); end; { TdxSpellCheckerUndoItem } constructor TdxSpellCheckerUndoItem.Create( const AMisspelledWord, AReplacement, APrevWord: WideString; AMisspelledWordPosition: Integer); begin inherited Create; FMisspelledWord := AMisspelledWord; FReplacement := AReplacement; FPrevWord := APrevWord; FMisspelledWordPosition := AMisspelledWordPosition; end; { TdxSpellCheckerUndoManager } constructor TdxSpellCheckerUndoManager.Create; begin inherited Create; 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(Index: Integer): TdxSpellCheckerUndoItem; begin Result := TdxSpellCheckerUndoItem(FUndoList[Index]); end; function TdxSpellCheckerUndoManager.GetLast: TdxSpellCheckerUndoItem; begin if Count > 0 then Result := Items[Count - 1] else Result := nil; 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; { TdxSpellCheckerAutoCorrectCustomRule } constructor TdxSpellCheckerAutoCorrectCustomRule.Create( AManager: TdxSpellCheckerCustomAutoCorrectManager); begin inherited Create; FManager := AManager; InitializeDelimiters; end; procedure TdxSpellCheckerAutoCorrectCustomRule.AfterCorrect; begin end; procedure TdxSpellCheckerAutoCorrectCustomRule.BeforeCorrect; begin end; function TdxSpellCheckerAutoCorrectCustomRule.IsTerminating: Boolean; begin Result := True; end; function TdxSpellCheckerAutoCorrectCustomRule.IsWordDelimiter( AChar: WideChar): Boolean; begin Result := WideCharPos(AChar, FWordDelimiters) > 0; end; procedure TdxSpellCheckerAutoCorrectCustomRule.Undo; begin end; function TdxSpellCheckerAutoCorrectCustomRule.GetActive: Boolean; begin Result := False; end; procedure TdxSpellCheckerAutoCorrectCustomRule.SetWordDelimiters(const AWordDelimiters: WideString); begin FWordDelimiters := AWordDelimiters; end; function TdxSpellCheckerAutoCorrectCustomRule.WideCapitalizeCase(const AWord: WideString): WideString; begin Result := Manager.WideCapitalizeCase(AWord); end; function TdxSpellCheckerAutoCorrectCustomRule.GetLastKey: WideChar; begin Result := Manager.LastKey; end; function TdxSpellCheckerAutoCorrectCustomRule.GetOptions: TdxSpellCheckerAutoCorrectOptions; begin Result := Manager.SpellChecker.AutoCorrectOptions; end; { TdxXMLDocument } constructor TdxXMLDocument.Create; begin inherited Create; FNodes := TdxXMLNodes.Create; end; destructor TdxXMLDocument.Destroy; begin FreeAndNil(FNodes); inherited Destroy; end; function TdxXMLDocument.FindNode(const ANodeName: WideString): TdxXMLNode; var I: Integer; begin Result := nil; for I := 0 to Nodes.Count - 1 do if WideCompareText(Nodes[I].NodeName, ANodeName) = 0 then begin Result := Nodes[I]; Break; end; end; procedure TdxXMLDocument.LoadFromFile(const AFileName: WideString); var AStream: TStream; begin AStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); try LoadFromStream(AStream); finally AStream.Free; end; end; procedure TdxXMLDocument.LoadFromStream(AStream: TStream); function SkipStandartPart(const S, APart: WideString; var APos: Integer): Boolean; begin Result := False; SkipSpace(S, APos); if Pos(APart, S) <> APos then Exit; Inc(APos, Length(APart)); Result := True; end; var AXMLText: UTF8String; AStr: WideString; APos: Integer; ARootNode: TdxXMLNode; begin AStream.Position := 0; SetLength(AXMLText, AStream.Size - AStream.Position); AStream.Read(AXMLText[1], AStream.Size - AStream.Position); AStr := dxUTF8StringToWideString(AXMLText); APos := 1; SkipSpace(AStr, APos); if not SkipStandartPart(AStr, '', APos) then SpellCheckerCreatingError(@sdxSpellCheckerFileFormatMismatch); SkipSpace(AStr, APos); ARootNode := TdxXMLNode.Create; ARootNode.FromStr(AStr, APos); Nodes.Add(ARootNode); end; procedure TdxXMLDocument.SaveToFile(const AFileName: WideString); var AStream: TStream; begin AStream := TFileStream.Create(AFileName, fmCreate); try SaveToStream(AStream); finally AStream.Free; end; end; procedure TdxXMLDocument.SaveToStream(AStream: TStream); var S: UTF8String; I: Integer; begin if Length(Encoding) > 0 then begin S := ''; AStream.Write(Pointer(S)^, Length(S)); end; for I := 0 to Nodes.Count - 1 do begin S := Nodes[I].ToStr; AStream.Write(Pointer(S)^, Length(S)); end; end; { TdxSpellCheckerCustomCheckMode } constructor TdxSpellCheckerCustomCheckMode.Create(AOwner: TdxCustomSpellChecker; AAdapter: TdxSpellCheckerCustomEditAdapter); begin inherited Create; FSpellChecker := AOwner; FAdapter := AAdapter; StoreSelection; CreateParser; FMisspellingStart := SpellingStart; end; destructor TdxSpellCheckerCustomCheckMode.Destroy; begin FreeAndNil(FParser); RestoreSelection; 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(FMisspellingStart, 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(FMisspellingLen); Dec(FMisspellingStart); SelectMisspelledWord; Adapter.ClearSelection; UpdateSpellingBounds(-FMisspellingLen); UpdateTextInfo; end; procedure TdxSpellCheckerCustomCheckMode.DeleteAll; begin SpellChecker.DeleteList.Add(MisspelledWord); Delete; end; function TdxSpellCheckerCustomCheckMode.GetNextWord(out AWord: WideString): Boolean; var ADelimiters: WideString; begin AWord := Parser.GetNextWord(FMisspellingStart, FMisspellingLen, ADelimiters); if AWord = '' then begin ValidateSpellingBounds; AWord := Parser.GetNextWord(FMisspellingStart, FMisspellingLen, ADelimiters); end; if (Length(ADelimiters) <> GetWideCharCount(' ', ADelimiters)) then PrevWord := ''; Result := FMisspellingLen > 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(FMisspellingStart, FMisspellingLen); end; if FMisspellingLen = 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(FMisspellingStart, FMisspellingLen); 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.RestoreSelection; begin end; procedure TdxSpellCheckerCustomCheckMode.StoreSelection; begin 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: TdxSpellCheckerReplacement; 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.SelectMisspelledWord; begin if FMisspellingLen > 0 then Adapter.SelStart := FMisspellingStart - 1; Adapter.SelLength := FMisspellingLen; end; procedure TdxSpellCheckerCustomCheckMode.SelectMisspelledWordAfterUndo; begin SelectMisspelledWord; end; procedure TdxSpellCheckerCustomCheckMode.UpdateByDictionary( ADictionary: TdxCustomSpellCheckerDictionary); begin end; procedure TdxSpellCheckerCustomCheckMode.UpdateSpellingBounds(ADelta: Integer); begin if FMisspellingStart < FSpellingStart then Inc(FSpellingStart, ADelta); if FMisspellingStart < 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; { 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); try FStartTicks := GetTickCount; DoAddSuggestions; if (Suggestions.Count = 0) and Dictionary.Words.UseDoubleMetaphone then DoAddMetaphoneSuggestions; except on EAbort do; else raise; end; end; function TdxSpellCheckerSuggestionBuilder.CanAddToSuggestions( const ATestWord: WideString): Boolean; begin Result := Dictionary.HasWord(ATestWord); if IsTimeOut then Abort; end; procedure TdxSpellCheckerSuggestionBuilder.CheckAddMetaphoneSuggestion( ATestWord, AUpperWord1: PWideChar; AUpperWordLen1: Integer; AUpperWord2: PWideChar; AUpperWordLen2: Integer); var ADistance: Integer; begin ADistance := Similarity.GetDistance(AUpperWord1, AUpperWordLen1, AUpperWord2, AUpperWordLen2); if ADistance <= MaxDistance then Suggestions.Add(ATestWord, Dictionary, ADistance); if IsTimeOut then Abort; end; procedure TdxSpellCheckerSuggestionBuilder.DoAddMetaphoneSuggestions; begin Dictionary.Words.PopulateMetaphoneSuggestions(Self, Word); end; function TdxSpellCheckerSuggestionBuilder.IsTimeOut: Boolean; begin Result := (Timeout > 0) and (GetTickCount >= Timeout + FStartTicks); end; function TdxSpellCheckerSuggestionBuilder.GetMaxDistance: Integer; begin Result := Dictionary.SpellChecker.MetaphoneDistance * 2; end; function TdxSpellCheckerSuggestionBuilder.GetSimilarity: TdxStringSimilarityCalculator; begin Result := Dictionary.SpellChecker.Similarity; end; function TdxSpellCheckerSuggestionBuilder.GetTimeout: Cardinal; begin Result := Dictionary.Timeout; 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; { TdxSpellCheckerDialogCheckMode } constructor TdxSpellCheckerDialogCheckMode.Create(AOwner: TdxCustomSpellChecker; AAdapter: TdxSpellCheckerCustomEditAdapter); begin inherited Create(AOwner, AAdapter); FUndoManager := TdxSpellCheckerUndoManager.Create; FSelectionChecked := False; Adapter.HideSelection := False; end; destructor TdxSpellCheckerDialogCheckMode.Destroy; begin FreeAndNil(FUndoManager); inherited Destroy; end; function TdxSpellCheckerDialogCheckMode.CanUndo: Boolean; begin Result := FUndoManager.Count > 0; end; procedure TdxSpellCheckerDialogCheckMode.Change(const AWord: WideString); begin FUndoManager.Add(MisspelledWord, AWord, '', FMisspellingStart - 1); inherited Change(AWord); end; procedure TdxSpellCheckerDialogCheckMode.CreateParser; begin inherited CreateParser; FCheckedRange := 0; 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); FCheckedRange := dxSelectionChecked; end; end; Inc(FSpellingStart); Inc(FSpellingEnd); UpdateTextInfo; end; procedure TdxSpellCheckerDialogCheckMode.Delete; begin FUndoManager.Add(' ' + MisspelledWord, '', PrevWord, FMisspellingStart - 2); inherited Delete; end; procedure TdxSpellCheckerDialogCheckMode.Ignore; begin FUndoManager.Add(MisspelledWord, MisspelledWord, PrevWord, FMisspellingStart - 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); end; procedure TdxSpellCheckerDialogCheckMode.RestoreSelection; begin Adapter.HideSelection := FSaveHideSelection; Adapter.SelStart := FSaveSelStart; Adapter.SelLength := FSaveSelLength; 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.StoreSelection; begin FSaveHideSelection := Adapter.HideSelection; FSaveSelLength := Adapter.SelLength; FSaveSelStart := Adapter.SelStart; inherited StoreSelection; end; procedure TdxSpellCheckerDialogCheckMode.ValidateSpellingBounds; procedure SetBeforeSelectionBounds; begin SpellingEnd := SpellingStart; SpellingStart := 1; FMisspellingStart := 1; end; var ASpellingStart, ASpellingEnd: Integer; begin if (SpellChecker.SpellingOptions.CheckFromCursorPos or IsCheckingSelectedText) and not SpellChecker.CheckingContainer then begin Adapter.GetSpellingBounds(ASpellingStart, ASpellingEnd); if ((FMisspellingStart >= ASpellingEnd) or (FMisspellingStart < SpellingStart)) and (SpellingStart > 1) and not IsCheckingSelectedText then begin SetBeforeSelectionBounds; UpdateTextInfo; end else if IsCheckingSelectedText and (FMisspellingStart >= SpellingEnd) then begin if (FCheckedRange = dxSelectionChecked) and (FSelectionChecked or (MessageDlg(cxGetResourceString(@sdxSpellCheckerSelectionCheckIsFinished), mtInformation, [mbYes, mbNo], 0) = mrYes)) then begin SpellingEnd := ASpellingEnd; FSelectionChecked := True; UpdateTextInfo; FCheckedRange := dxAfterSelectionChecked; end else if (FCheckedRange = dxAfterSelectionChecked) and (FMisspellingStart > ASpellingStart) then begin SetBeforeSelectionBounds; FCheckedRange := dxBeforeSelectionChecked; end; end; end; end; procedure TdxSpellCheckerDialogCheckMode.ValidateSpellingBoundsAfterUndo; var ASpellingStart, ASpellingEnd: Integer; begin Adapter.GetSpellingBounds(ASpellingStart, ASpellingEnd); if (FMisspellingStart >= SpellingEnd) and (SpellingEnd < ASpellingEnd) then begin FCheckedRange := dxAfterSelectionChecked; 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 FMisspellingStart := FMisspellingStart - 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; FMisspellingStart := AMisspelledSentence.StartTextPosition - 1; end; procedure TdxSpellCheckerWordCheckMode.SelectMisspelledWordAfterUndo; var AItem: TdxSpellCheckerUndoItem; begin AItem := FUndoManager.Last; if AItem <> nil then begin FMisspellingStart := AItem.MisspelledWordPosition + 1; FMisspellingLen := 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(FMisspellingStart); 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); var ASuggestion: TdxSpellCheckerSuggestion; begin ASuggestion := TdxSpellCheckerSuggestion.Create(AWord, ADictionary, ADistance); ASuggestion.FOrder := Count; inherited Add(ASuggestion); 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 // ctLower: // for I := 0 to Count - 1 do // with Items[I] do // FWord := WideLowerCase(FWord); 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 Delete(I); 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; if Result = 0 then Result := TdxSpellCheckerSuggestion(Item1).FOrder - TdxSpellCheckerSuggestion(Item2).FOrder; end; procedure TdxSpellCheckerSuggestionList.SortByDistance; begin Sort(CompareSuggestionsByDistance); end; { function CompareSuggestionsByWord(Item1, Item2: Pointer): Integer; var AAlphabet: WideString; ALen, I: Integer; AWord1, AWord2: WideString; begin AWord1 := TdxSpellCheckerSuggestion(Item1).Word; AWord2 := TdxSpellCheckerSuggestion(Item2).Word; Result := Length(AWord2) - Length(AWord1); if Result = 0 then begin AAlphabet := TdxSpellCheckerSuggestion(Item1).Dictionary.Alphabet; ALen := Length(AWord1); for I := 1 to ALen do begin Result := WideCharPos(AWord2[I], AAlphabet) - WideCharPos(AWord1[I], AAlphabet); if Result <> 0 then Break; end; end; end; } function CompareSuggestionsByWord(Item1, Item2: Pointer): Integer; begin Result := WideCompareText(TdxSpellCheckerSuggestion(Item1).Word, TdxSpellCheckerSuggestion(Item2).Word); if Result = 0 then Result := TdxSpellCheckerSuggestion(Item1).FOrder - TdxSpellCheckerSuggestion(Item2).FOrder; end; procedure TdxSpellCheckerSuggestionList.SortByWord; begin Sort(CompareSuggestionsByWord); end; function TdxSpellCheckerSuggestionList.GetItem( Index: Integer): TdxSpellCheckerSuggestion; begin Result := TdxSpellCheckerSuggestion(inherited Items[Index]); 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); UseDoubleMetaphone := False; //free memory DeleteCriticalSection(FLock); inherited Destroy; end; procedure TdxSpellCheckerWordList.Add(const S: WideString); var AIndex, ASrcLen, AWordLen: Integer; AWord, ATemp, P: PWideChar; CT1, CT2: TdxCapitalizationType; begin ASrcLen := Length(S); if ASrcLen = 0 then Exit; EnterCriticalSection(FLock); try P := Pointer(S); AIndex := ElfHash(P, ASrcLen) mod FTableSize; AWord := FTable^[AIndex]; if AWord = nil then FTable^[AIndex] := NewWord(P, ASrcLen) else begin repeat AWordLen := WordLength(AWord); if (AWordLen = ASrcLen) and (CompareStringW(LangID, NORM_IGNORECASE, AWord, AWordLen, P, ASrcLen) = CSTR_EQUAL) then begin if FindMostValidCapitalizationInDuplicates then begin CT1 := GetWordCapitalizationType(AWord, AWordLen); if CT1 = ctLower then Exit; CT2 := GetWordCapitalizationType(P, AWordLen); if (CT1 <> CT2) and ((CT2 = ctLower) or ((CT2 = ctCapitalized) and (CT1 = ctUpper))) then Move(Pointer(P)^, Pointer(AWord)^, AWordLen * SizeOf(WideChar)); end; Exit; end; ATemp := GetNextWord(AWord); if ATemp = nil then begin SetNextWord(AWord, NewWord(P, ASrcLen)); 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.PopulateMetaphoneSuggestions( ASuggestionBuilder: TdxSpellCheckerSuggestionBuilder; const AWord: WideString); var AKey: Word; ASrcLen, ATestLen: Integer; ATestWord: PWideChar; ABuffer: array[0..256] of WideChar; begin ASrcLen := Length(AWord); if not UseDoubleMetaphone or (ASrcLen = 0) or (ASuggestionBuilder = nil) or Find(AWord) then Exit; //FUpperCaseBuffer contains an upper string here FMetaphone.DoubleMetaphone(@FUpperCaseBuffer, ASrcLen); AKey := FMetaphone.PrimaryKey; if AKey = 0 then Exit; ATestWord := FMetaphoneTable^[AKey]; while ATestWord <> nil do begin ATestLen := WordLength(ATestWord); if Abs(ATestLen - ASrcLen) <= ASuggestionBuilder.MaxDistance then begin LCMapStringW(LangID, LCMAP_UPPERCASE, ATestWord, ATestLen, @ABuffer, SizeOf(ABuffer) div SizeOf(WideChar)); ASuggestionBuilder.CheckAddMetaphoneSuggestion(ATestWord, @ABuffer, ATestLen, @FUpperCaseBuffer, ASrcLen); end; ATestWord := GetNextMetaphoneWord(ATestWord); end; 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; ALength: Integer): Integer; var I: Integer; begin LCMapStringW(LangID, LCMAP_UPPERCASE, P, ALength + 1, @FUpperCaseBuffer, SizeOf(FUpperCaseBuffer) div SizeOf(WideChar)); Result := 0; Pointer(P) := @FUpperCaseBuffer; 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, ASrcLen) 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; ALength: Integer): PWideChar; begin Result := AllocWord(ALength); if UseDoubleMetaphone then UpdateMetaphoneInfo(Result, ALength); Inc(ALength); //include terminated null Move(S^, Result^, ALength * SizeOf(WideChar)); end; function TdxSpellCheckerWordList.AllocWord(ACharCount: Cardinal): PWideChar; var ASize: Integer; begin ASize := (ACharCount + 1) * Sizeof(WideChar); //add terminated null Inc(ASize, SizeOf(Cardinal) * 4); //Size, NextMetaphone, Next, Length GetMem(Result, ASize); Cardinal(Pointer(Result)^) := ASize; //Size Inc(Integer(Result), SizeOf(Cardinal)); Pointer(Pointer(Result)^) := nil; //NextMetaphone Inc(Integer(Result), SizeOf(Cardinal)); Pointer(Pointer(Result)^) := nil; //Next Inc(Integer(Result), SizeOf(Cardinal)); Cardinal(Pointer(Result)^) := ACharCount; //Length Inc(Integer(Result), SizeOf(Cardinal)); end; procedure TdxSpellCheckerWordList.DisposeWord(AWord: PWideChar); begin if AWord <> nil then begin Dec(Integer(AWord), SizeOf(Cardinal) * 4); FreeMem(AWord, Cardinal(Pointer(AWord)^)); end; end; function TdxSpellCheckerWordList.GetNextMetaphoneWord(AWord: PWideChar): Pointer; begin if AWord = nil then Result := nil else begin Dec(Integer(AWord), SizeOf(Cardinal) * 3); Result := Pointer(Pointer(AWord)^); end; end; function TdxSpellCheckerWordList.GetNextWord(AWord: PWideChar): Pointer; begin if AWord = nil then Result := nil else begin Dec(Integer(AWord), SizeOf(Cardinal) * 2); Result := Pointer(Pointer(AWord)^); end; end; procedure TdxSpellCheckerWordList.SetNextMetaphoneWord(AWord: PWideChar; AValue: Pointer); begin if AWord <> nil then begin Dec(Integer(AWord), SizeOf(Cardinal) * 3); Pointer(Pointer(AWord)^) := AValue; end; end; procedure TdxSpellCheckerWordList.SetNextWord(AWord: PWideChar; AValue: Pointer); begin if AWord <> nil then begin Dec(Integer(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(Integer(AWord), SizeOf(Cardinal)); Result := Cardinal(Pointer(AWord)^); end; end; function TdxSpellCheckerWordList.GetCodePage: Cardinal; begin Result := LanguageToCodePage(FLangID); end; procedure TdxSpellCheckerWordList.SetUseDoubleMetaphone(AValue: Boolean); begin if FUseDoubleMetaphone <> AValue then begin FUseDoubleMetaphone := AValue; if AValue then begin FMetaphoneTable := AllocMem(SizeOf(TdxMetaphoneTable)); FMetaphone := TdxDoubleMetaphone.Create; end else begin FreeMem(FMetaphoneTable); FreeAndNil(FMetaphone); end; UpdateWordsMetaphone; end; end; procedure TdxSpellCheckerWordList.UpdateMetaphoneInfo(ANewWord: PWideChar; ALength: Integer); var AKey: Word; AWord, ATemp: PWideChar; begin FMetaphone.DoubleMetaphone(@FUpperCaseBuffer, ALength); AKey := FMetaphone.PrimaryKey; if AKey = 0 then Exit; AWord := FMetaphoneTable^[AKey]; if AWord = nil then FMetaphoneTable^[AKey] := ANewWord else begin repeat ATemp := GetNextMetaphoneWord(AWord); if ATemp = nil then begin SetNextMetaphoneWord(AWord, ANewWord); Break; end; AWord := ATemp; until False; end; end; procedure TdxSpellCheckerWordList.UpdateWordsMetaphone; var I, AWordLen: Integer; AWord: PWideChar; begin if Count = 0 then Exit; for I := 0 to FTableSize - 1 do begin AWord := FTable^[I]; while AWord <> nil do begin if UseDoubleMetaphone then begin AWordLen := WordLength(AWord); LCMapStringW(LangID, LCMAP_UPPERCASE, AWord, AWordLen, @FUpperCaseBuffer, SizeOf(FUpperCaseBuffer) div SizeOf(WideChar)); UpdateMetaphoneInfo(AWord, AWordLen); end else SetNextMetaphoneWord(AWord, nil); AWord := GetNextWord(AWord); end; 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; constructor TdxCustomSpellChecker.Create(AOwner: TComponent); var I: Integer; AComponent: TComponent; begin FCreating := True; if IsContainer(AOwner) and IsSpellCheckerAlreadyExists(AOwner, Self) then SpellCheckerCreatingError(@sdxSpellCheckerMoreThanOne); for I := 0 to Application.ComponentCount - 1 do begin AComponent := Application.Components[I]; if IsContainer(AComponent) and IsSpellCheckerAlreadyExists(AComponent, Self) then SpellCheckerCreatingError(@sdxSpellCheckerMoreThanOne); end; FThreadedLoadPriority := tpLower; FCreating := False; inherited Create(AOwner); FAutoLoadDictionaries := False; FSpellingFormType := sftOutlook; FMetaphoneDistance := 3; FDictionaryItems := TdxSpellCheckerDictionaries.Create(Self); FSimilarity := CreateSimilarity; FRules := TdxSpellCheckerRules.Create; FDialogLookAndFeel := TcxLookAndFeel.Create(nil); FSpellingOptions := CreateSpellingOptions; FAutoCorrectOptions := TdxSpellCheckerAutoCorrectOptions.Create(Self); FAutoCorrectManager := TdxSpellCheckerAutoCorrectManager.Create(Self); FCheckAsYouTypeOptions := TdxSpellCheckerCheckAsYouTypeOptions.Create(Self); FCheckAsYouTypeManager := TdxSpellCheckerCheckAsYouTypeManager.Create(Self); SpellingOptionsChanged; FLanguages := TdxSpellCheckerLanguages.Create; FChangeList := TdxSpellCheckerReplacementList.Create; FDeleteList := TdxSpellCheckerWordList.Create(LANG_SYSTEM_DEFAULT, 257); FIgnoreList := TdxSpellCheckerWordList.Create(LANG_SYSTEM_DEFAULT, 257); dxISpellChecker := Self; end; destructor TdxCustomSpellChecker.Destroy; begin if not FCreating then begin dxISpellChecker := nil; CheckAsYouTypeOptions.Active := False; FreeAndNil(FChangeList); FreeAndNil(FDeleteList); FreeAndNil(FIgnoreList); UnloadDictionaries; FreeAndNil(FLanguages); FreeAndNil(FSpellingOptions); FreeAndNil(FRules); FreeAndNil(FSimilarity); FreeAndNil(FCheckAsYouTypeManager); FreeAndNil(FCheckAsYouTypeOptions); FreeAndNil(FAutoCorrectOptions); FreeAndNil(FAutoCorrectManager); FreeAndNil(FDialogLookAndFeel); FreeAndNil(FDictionaryItems); end; inherited Destroy; end; procedure TdxCustomSpellChecker.Check(var AText: AnsiString); var AEditAdapter: TdxSpellCheckerTextAdapter; begin AEditAdapter := TdxSpellCheckerTextAdapter.Create(dxAnsiStringToWideString(AText)); try InternalCheck(AEditAdapter); AText := dxWideStringToAnsiString(AEditAdapter.Text); finally FreeAndNil(AEditAdapter); end; end; procedure TdxCustomSpellChecker.Check(var AText: WideString); var AEditAdapter: TdxSpellCheckerTextAdapter; begin AEditAdapter := TdxSpellCheckerTextAdapter.Create(AText); try InternalCheck(AEditAdapter); AText := AEditAdapter.Text; finally FreeAndNil(AEditAdapter); end; 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); 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(AWord, 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].Terminate; for I := 0 to DictionaryCount - 1 do Dictionaries[I].Unload; finally HideHourglassCursor; end; end; function TdxCustomSpellChecker.GetTwoWordsDistance( const AWord1, AWord2: WideString): Integer; begin Result := Similarity.GetDistance(PWideChar(WideUpperCase(AWord1)), Length(AWord1), PWideChar(WideUpperCase(AWord2)), Length(AWord2)) div 2; end; procedure TdxCustomSpellChecker.CheckCallEnabledDictionariesLoaded; begin 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 CheckAsYouTypeManager.BeginManualCheck; end; procedure TdxCustomSpellChecker.EndManualCheck; begin 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; function TdxCustomSpellChecker.DoAutoCorrect(ARule: TdxSpellCheckerAutoCorrectCustomRule; var AWordRange: TdxSpellCheckerAutoCorrectWordRange): Boolean; begin Result := True; if Assigned(FOnAutoCorrect) then FOnAutoCorrect(Self, ARule, AWordRange, Result); end; procedure TdxCustomSpellChecker.DoAutoCorrectFinish(AControl: TWinControl); begin if Assigned(OnAutoCorrectFinish) then OnAutoCorrectFinish(Self, AControl); end; function TdxCustomSpellChecker.DoAutoCorrectStart(AControl: TWinControl): Boolean; begin Result := Assigned(AControl); if Result and Assigned(OnAutoCorrectStart) then OnAutoCorrectStart(Self, AControl, Result); 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 TCustomEdit) or (AControl is TcxCustomTextEdit) then begin AControl := GetInnerControlContainer(AControl); if DoCheckControlInContainer(AControl, AContinue) then begin if AControl is TcxCustomTextEdit then Check(TcxCustomTextEdit(AControl)) else Check(TCustomEdit(AControl)); end; 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(const AWord: WideString; ASuggestions: TdxSpellCheckerSuggestionList); begin if Assigned(FOnGetSuggestions) then FOnGetSuggestions(Self, AWord, 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); 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.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) and CheckAsYouTypeManager.Active then Result := CheckAsYouTypeManager.CheckMode; 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.SetAutoCorrectOptions(AValue: TdxSpellCheckerAutoCorrectOptions); begin FAutoCorrectOptions.Assign(AValue); 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.SetMetaphoneDistance(AValue: Integer); var I: Integer; begin if AValue < 1 then AValue := 1; if AValue > 5 then AValue := 5; if FMetaphoneDistance <> AValue then begin FMetaphoneDistance := AValue; for I := 0 to DictionaryCount - 1 do Dictionaries[I].UpdateUsingMetaphone; end; end; procedure TdxCustomSpellChecker.SetSpellingOptions( AValue: TdxSpellCheckerSpellingOptions); begin FSpellingOptions.Assign(AValue); end; // IdxSpellCheckerEditHelper procedure TdxCustomSpellChecker.CheckFinish; begin CheckAsYouTypeManager.CheckFinish; AutoCorrectManager.CheckFinish; end; procedure TdxCustomSpellChecker.CheckStart(AControl: TWinControl); begin CheckAsYouTypeManager.CheckStart(AControl); AutoCorrectManager.CheckStart(AControl); end; procedure TdxCustomSpellChecker.DrawMisspellings(AControl: TWinControl); begin CheckAsYouTypeManager.DrawMisspellings(AControl); end; function TdxCustomSpellChecker.IsSpellCheckerDialogControl(AWnd: THandle): Boolean; begin Result := CheckAsYouTypeManager.Active and CheckAsYouTypeManager.IsManualCheckingActive; end; procedure TdxCustomSpellChecker.KeyPress(AKey: Char); begin AutoCorrectManager.KeyPress(AKey); end; function TdxCustomSpellChecker.QueryPopup( APopup: TComponent; const P: TPoint): Boolean; begin Result := CheckAsYouTypeManager.QueryPopup(APopup, P); end; procedure TdxCustomSpellChecker.LayoutChanged(AControl: TWinControl); begin CheckAsYouTypeManager.LayoutChanged(AControl); end; procedure TdxCustomSpellChecker.SelectionChanged(AControl: TWinControl); begin CheckAsYouTypeManager.SelectionChanged(AControl); end; procedure TdxCustomSpellChecker.TextChanged(AControl: TWinControl); begin CheckAsYouTypeManager.TextChanged(AControl); end; procedure TdxCustomSpellChecker.Undo; begin AutoCorrectManager.Undo; end; { TdxSpellCheckerCustomManager } constructor TdxSpellCheckerCustomManager.Create(ASpellChecker: TdxCustomSpellChecker); begin inherited Create(ASpellChecker); FContainer := nil; end; procedure TdxSpellCheckerCustomManager.CheckFinish; begin Edit := nil; end; procedure TdxSpellCheckerCustomManager.CheckStart(AControl: TWinControl); begin if Active then begin if (AControl <> nil) and IsCheckSupported(AControl) then Edit := AControl else Edit := nil; end; end; procedure TdxSpellCheckerCustomManager.CreateAdapter(AAdapterClass: TdxSpellCheckerCustomEditAdapterClass; AControl: TWinControl); begin FAdapter := AAdapterClass.Create(AControl); end; procedure TdxSpellCheckerCustomManager.DoFinalizeController; begin end; function TdxSpellCheckerCustomManager.GetActive: Boolean; begin Result := False; end; function TdxSpellCheckerCustomManager.GetAdapterClass(AControl: TWinControl): TdxSpellCheckerCustomEditAdapterClass; begin Result := nil; end; function TdxSpellCheckerCustomManager.GetCheckModeClass: TdxSpellCheckerCustomCheckModeClass; begin Result := TdxSpellCheckerCustomCheckMode; end; procedure TdxSpellCheckerCustomManager.FinalizeController; begin if Assigned(CheckMode) then begin DoFinish(Container); DoFinalizeController; FreeAndNil(FCheckMode); FreeAndNil(FAdapter); FContainer := nil; end; end; procedure TdxSpellCheckerCustomManager.InitializeController(AControl: TWinControl); var AAdapterClass: TdxSpellCheckerCustomEditAdapterClass; begin if (AControl = nil) or (GetParentForm(AControl) is TfmSpellCheckerBaseForm) then FContainer := nil else begin FContainer := TcxCustomEdit(GetInnerControlContainer(AControl)); AAdapterClass := GetAdapterClass(AControl); if Assigned(AAdapterClass) and DoStart(FContainer) then begin CreateAdapter(AAdapterClass, AControl); FCheckMode := GetCheckModeClass.Create(SpellChecker, Adapter); end; end; end; function TdxSpellCheckerCustomManager.IsCheckSupported(AControl: TWinControl): Boolean; var AItf: IdxSpellCheckerControl; begin Result := Assigned(AControl) and AControl.HandleAllocated and Supports(GetInnerControlContainer(AControl), IdxSpellCheckerControl, AItf) and AItf.SupportsSpelling; end; procedure TdxSpellCheckerCustomManager.ValidateAdapter; begin if Adapter = nil then CheckStart(FindControl(GetFocus)) end; function TdxSpellCheckerCustomManager.GetEdit: TWinControl; begin if Adapter <> nil then Result := Adapter.Edit else Result := nil; end; procedure TdxSpellCheckerCustomManager.SetEdit(const AValue: TWinControl); begin if Edit <> AValue then begin FinalizeController; InitializeController(AValue); end; end; { TdxSpellCheckerTextParser } constructor TdxSpellCheckerTextParser.Create(ASpellChecker: TdxCustomSpellChecker); begin inherited Create; FSpellChecker := ASpellChecker; FDelimiters := #0#9#10#11#13#32'.,<>=!?:;"()[]{}+|-/\'#$201C#$201D; FUrlDelimiters := #0#9#10#11#13#32',<>!;()[]{}|'; FMeaningfulCharacters := ''''; 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]) or IsMeaningfulCharacters(FText[AStart])) do begin ADelimiters := ADelimiters + FText[AStart]; Inc(AStart); end; ALength := 0; APos := AStart; while (APos < FSpellingEnd) and not (IsDelimiter(FText[APos]) or ((APos < FSpellingEnd - 1) and IsMeaningfulCharacters(FText[APos]) and not dxWideIsAlpha(FText[APos + 1]))) 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.IsMeaningfulCharacters(AChar: WideChar): Boolean; begin Result := WideCharPos(AChar, FMeaningfulCharacters) > 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; { TdxNearMissStrategy } procedure TdxNearMissStrategy.CheckAddToSuggestions(const ATestWord: WideString); begin if CanAddToSuggestions(ATestWord) then FSuggestions.Add(WidePatternCase(Word, ATestWord), Dictionary, 2); 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.CheckDeleteLetter; var ATestWord: WideString; ASymbol: WideChar; ADeletePos, ALen: Integer; begin ALen := Length(FWord); if ALen < 2 then Exit; ADeletePos := ALen; repeat ATestWord := FWord; ASymbol := ATestWord[ADeletePos]; Delete(ATestWord, ADeletePos, 1); CheckAddToSuggestions(ATestWord); Dec(ADeletePos); while (ADeletePos >= 1) and (ASymbol = FWord[ADeletePos]) do Dec(ADeletePos); until ADeletePos < 1; end; procedure TdxNearMissStrategy.CheckDoubleTwoChars; var AWord: PWideChar; ATestWord: WideString; ALen: Integer; I: Integer; begin ALen := Length(Word); if ALen < 4 then Exit; AWord := PWideChar(Word); for I := 0 to ALen - 3 do if PCardinal(@AWord[I])^ = PCardinal(@AWord[I + 2])^ then begin ATestWord := Word; Delete(ATestWord, I + 1, 2); CheckAddToSuggestions(PWideChar(ATestWord)); 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 := ALen - 1 downto 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, 2); end; end; procedure TdxNearMissStrategy.CheckMoveChar; var ATestWord: WideString; I, J: Integer; AChar: WideChar; ALen: Integer; begin ALen := Length(Word); if ALen < 2 then Exit; ATestWord := Word; for I := 1 to ALen do begin J := I + 1; while (J < ALen) and ((J - I) < 10) do begin AChar := ATestWord[J]; ATestWord[J] := ATestWord[J + 1]; ATestWord[J + 1] := AChar; if (J - I) >= 2 then CheckAddToSuggestions(ATestWord); Inc(J); end; ATestWord := Word; end; for I := ALen downto 1 do begin J := I - 1; while (J > 1) and ((I - J) < 10) do begin AChar := ATestWord[J]; ATestWord[J] := ATestWord[J - 1]; ATestWord[J - 1] := AChar; if (I - J) >=2 then CheckAddToSuggestions(ATestWord); Dec(J); end; ATestWord := Word; end; end; procedure TdxNearMissStrategy.DoAddSuggestions; begin InterchangeTwoLetters; LongInterchangeTwoLetters; CheckDeleteLetter; CheckInsertLetter; CheckMoveChar; CheckChangeOneLetter; CheckDoubleTwoChars; CheckInsertSpace; 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.LongInterchangeTwoLetters; var AWord: WideString; AChar: WideChar; ALen, I, J: Integer; begin ALen := Length(Word); if ALen < 3 then Exit; for I := 1 to ALen do for J := I + 2 to ALen do begin AWord := Word; AChar := AWord[I]; AWord[I] := AWord[J]; AWord[J] := AChar; CheckAddToSuggestions(AWord); end; end; { TdxSpellCheckerAutoCorrectOptions } destructor TdxSpellCheckerAutoCorrectOptions.Destroy; begin FreeAndNil(FFirstLetterExceptions); FreeAndNil(FInitialCapsExceptions); FreeAndNil(FReplacements); inherited Destroy; end; procedure TdxSpellCheckerAutoCorrectOptions.Assign(Source: TPersistent); begin if Source is TdxSpellCheckerAutoCorrectOptions then with TdxSpellCheckerAutoCorrectOptions(Source) do begin Self.FActive := Active; Self.FAutomaticallyUseSuggestions := AutomaticallyUseSuggestions; Self.FCorrectSentenceCaps := CorrectSentenceCaps; Self.FCorrectCapsLock := CorrectCapsLock; Self.FCorrectInitialCaps := CorrectInitialCaps; Self.FDisableCapsLock := DisableCapsLock; Self.FReplaceTextAsYouType := ReplaceTextAsYouType; end; inherited Assign(Source); end; function TdxSpellCheckerAutoCorrectOptions.GetReplacement(const AText: WideString; out AReplacement: WideString): Boolean; function WordContainsOnlyAlpha(const S: WideString): Boolean; var I: Integer; ALen: Integer; begin ALen := Length(S); Result := ALen > 0; if Result then for I := 1 to ALen do begin Result := dxWideIsAlpha(S[I]); if not Result then Break; end; end; var AItem: TdxSpellCheckerReplacement; ASuggestions: TdxSpellCheckerSuggestionList; begin AItem := Replacements.FindReplacement(AText); Result := AItem <> nil; if Result then AReplacement := AItem.Replacement else if AutomaticallyUseSuggestions and WordContainsOnlyAlpha(AText) then begin ASuggestions := SpellChecker.GetSuggestions(AText); try Result := ASuggestions.Count = 1; if Result then AReplacement := ASuggestions.Items[0].Word; finally FreeAndNil(ASuggestions); end; end; end; procedure TdxSpellCheckerAutoCorrectOptions.Changed; begin if SpellChecker.CheckAsYouTypeManager <> nil then SpellChecker.AutoCorrectManager.DoOptionsChanged; DoChanged; end; procedure TdxSpellCheckerAutoCorrectOptions.DoChanged; begin if Assigned(FOnChanged) then FOnChanged(Self); end; procedure TdxSpellCheckerAutoCorrectOptions.InitializeOptions; begin FActive := False; FAutomaticallyUseSuggestions := True; FCorrectSentenceCaps := True; FCorrectCapsLock := True; FCorrectInitialCaps := True; FDisableCapsLock := False; FReplaceTextAsYouType := True; FFirstLetterExceptions := TdxSpellCheckerAutoCorrectExceptions.Create(Self); FFirstLetterExceptions.OnAdd := FirstLetterExceptionAddHandler; FInitialCapsExceptions := TdxSpellCheckerAutoCorrectExceptions.Create(Self); FInitialCapsExceptions.OnAdd := InitialCapsExceptionAddHandler; FReplacements := TdxSpellCheckerAutoCorrectReplacementList.Create; end; procedure TdxSpellCheckerAutoCorrectOptions.FirstLetterExceptionAddHandler(Sender: TdxSpellCheckerAutoCorrectExceptions; var AException: WideString; var AAllow: Boolean); var ALen, I: Integer; ACorrectException: Boolean; begin AAllow := False; ALen := Length(AException); if ALen > 0 then begin ACorrectException := True; for I := 1 to ALen do begin ACorrectException := dxWideIsAlpha(AException[I]); if I = ALen then ACorrectException := ACorrectException or (AException[I] = '.'); if not ACorrectException then Break; end; if ACorrectException then begin if AException[ALen] <> '.' then AException := AException + '.'; AException := WideLowerCase(AException); AAllow := True; end; end; end; procedure TdxSpellCheckerAutoCorrectOptions.InitialCapsExceptionAddHandler(Sender: TdxSpellCheckerAutoCorrectExceptions; var AException: WideString; var AAllow: Boolean); var S: WideString; ALen, I: Integer; begin AAllow := False; AException := WideLowerCase(AException); ALen := Length(AException); if ALen > 2 then begin for I := 1 to ALen do begin AAllow := dxWideIsAlpha(AException[I]); if not AAllow then Break; end; if AAllow then begin S := WideUpperCase(AException); AException[1] := S[1]; AException[2] := S[2]; end; end; end; procedure TdxSpellCheckerAutoCorrectOptions.SetActive( const AValue: Boolean); begin if FActive <> AValue then begin FActive := AValue; SpellChecker.AutoCorrectManager.DoActiveChanged; Changed; end; end; procedure TdxSpellCheckerAutoCorrectOptions.SetAutomaticallyUseSuggestions( const AValue: Boolean); begin if FAutomaticallyUseSuggestions <> AValue then begin FAutomaticallyUseSuggestions := AValue; Changed; end; end; procedure TdxSpellCheckerAutoCorrectOptions.SetCorrectCapsLock( const AValue: Boolean); begin if FCorrectCapsLock <> AValue then begin FCorrectCapsLock := AValue; Changed; end; end; procedure TdxSpellCheckerAutoCorrectOptions.SetCorrectInitialCaps( const AValue: Boolean); begin if FCorrectInitialCaps <> AValue then begin FCorrectInitialCaps := AValue; Changed; end; end; procedure TdxSpellCheckerAutoCorrectOptions.SetCorrectSentenceCaps( const AValue: Boolean); begin if FCorrectSentenceCaps <> AValue then begin FCorrectSentenceCaps := AValue; Changed; end; end; procedure TdxSpellCheckerAutoCorrectOptions.SetDisableCapsLock( const AValue: Boolean); begin if FDisableCapsLock <> AValue then begin FDisableCapsLock := AValue; Changed; end; end; procedure TdxSpellCheckerAutoCorrectOptions.SetReplaceTextAsYouType( const AValue: Boolean); begin if FReplaceTextAsYouType <> AValue then begin FReplaceTextAsYouType := AValue; Changed; end; end; { TdxSpellCheckerSpellingOptions } 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.InitializeOptions; begin FIgnoreMixedCaseWords := True; FIgnoreUpperCaseWords := True; FIgnoreMarkupTags := True; FIgnoreRepeatedWords := False; FCheckSelectedTextFirst := True; FIgnoreUrls := True; FCheckFromCursorPos := False; FIgnoreEmails := True; FIgnoreWordsWithNumbers := True; 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 } 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.InitializeOptions; begin FSuggestionCount := 5; FUnderlineColor := clRed; FUnderlineStyle := usAuto; FModifyControlPopupMenu := True; FPopupMenuItems := [scmiAddToDictionary, scmiAutoCorrect, scmiDelete, scmiIgnoreAll, scmiSpelling, scmiSuggestions]; 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.CheckAsYouTypeManager.DoActiveChanged; 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; { TdxSpellCheckerAutoCorrectReplacementList } procedure TdxSpellCheckerAutoCorrectReplacementList.LoadFromFile(const AFileName: WideString); var AStream: TStream; begin AStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); try LoadFromStream(AStream); finally AStream.Free; end; end; procedure TdxSpellCheckerAutoCorrectReplacementList.LoadFromStream(AStream: TStream); var ADocument: TdxXMLDocument; ANode, ARootNode: TdxXMLNode; I: Integer; begin Clear; ADocument := TdxXMLDocument.Create; try ADocument.LoadFromStream(AStream); ARootNode := ADocument.FindNode('block-list:block-list'); if ARootNode <> nil then for I := 0 to ARootNode.ChildCount - 1 do begin ANode := ARootNode.ChildList[I]; Add(ANode.GetValue('block-list:abbreviated-name'), ANode.GetValue('block-list:name')); end; finally FreeAndNil(ADocument); end; end; procedure TdxSpellCheckerAutoCorrectReplacementList.SaveToFile(const AFileName: WideString); var AStream: TStream; begin AStream := TFileStream.Create(AFileName, fmCreate); try SaveToStream(AStream); finally AStream.Free; end; end; procedure TdxSpellCheckerAutoCorrectReplacementList.SaveToStream(AStream: TStream); var ADocument: TdxXMLDocument; ANode, ARootNode: TdxXMLNode; I: Integer; begin ADocument := TdxXMLDocument.Create; try ADocument.Encoding := 'UTF-8'; ARootNode := TdxXMLNode.Create; ARootNode.NodeName := 'block-list:block-list'; ARootNode.NameSpaces := 'http://www.devexpress.com'; ADocument.Nodes.Add(ARootNode); for I := 0 to Count - 1 do begin ANode := TdxXMLNode.Create; ANode.NodeName := 'block-list:block'; ANode.AddAttribute('block-list:abbreviated-name', Items[I].Text); ANode.AddAttribute('block-list:name', Items[I].Replacement); ARootNode.AddChild(ANode); end; ADocument.SaveToStream(AStream); finally FreeAndNil(ADocument); end; end; { TdxSpellCheckerCustomAutoCorrectManager } procedure TdxSpellCheckerCustomAutoCorrectManager.KeyPress(AKey: Char); begin ValidateAdapter; FLastKey := WideChar(AKey); end; procedure TdxSpellCheckerCustomAutoCorrectManager.CreateAdapter(AAdapterClass: TdxSpellCheckerCustomEditAdapterClass; AControl: TWinControl); begin FAdapter := AAdapterClass.Create(FContainer); end; function TdxSpellCheckerCustomAutoCorrectManager.DoAutoCorrect(ARule: TdxSpellCheckerAutoCorrectCustomRule; var AWordRange: TdxSpellCheckerAutoCorrectWordRange): Boolean; begin Result := SpellChecker.DoAutoCorrect(ARule, AWordRange); end; procedure TdxSpellCheckerCustomAutoCorrectManager.DoFinish(AControl: TWinControl); begin SpellChecker.DoAutoCorrectFinish(AControl); end; function TdxSpellCheckerCustomAutoCorrectManager.DoStart(AControl: TWinControl): Boolean; begin Result := SpellChecker.DoAutoCorrectStart(AControl); end; function TdxSpellCheckerCustomAutoCorrectManager.GetActive: Boolean; begin Result := Options.Active; end; function TdxSpellCheckerCustomAutoCorrectManager.GetAdapterClass( AControl: TWinControl): TdxSpellCheckerCustomEditAdapterClass; var AEdit: TcxCustomEdit; begin AEdit := TcxCustomEdit(GetInnerControlContainer(AControl)); if AEdit is TcxCustomRichEdit then Result := TdxSpellCheckercxRichEditAdapter else if AEdit is TcxCustomEdit then Result := TdxSpellCheckercxTextEditAdapter else Result := inherited GetAdapterClass(AControl); end; procedure TdxSpellCheckerCustomAutoCorrectManager.Undo; begin end; function TdxSpellCheckerCustomAutoCorrectManager.WideCapitalizeCase(const AWord: WideString): WideString; begin Result := dxSpellCheckerUtils.WideCapitalizeCase(AWord); end; function TdxSpellCheckerCustomAutoCorrectManager.GetOptions: TdxSpellCheckerAutoCorrectOptions; begin Result := SpellChecker.AutoCorrectOptions; end; { TdxSpellCheckerCustomCheckAsYouTypeManager } procedure TdxSpellCheckerCustomCheckAsYouTypeManager.BeginManualCheck; begin Inc(FManualCheckCount); end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.CheckFinish; begin if not IsManualCheckingActive then inherited CheckFinish; end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.CheckStart(AControl: TWinControl); begin if not IsManualCheckingActive then inherited CheckStart(AControl); end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.EndManualCheck; begin Dec(FManualCheckCount); if FManualCheckCount = 0 then CheckStart(FindControl(GetFocus)); end; function TdxSpellCheckerCustomCheckAsYouTypeManager.GetIsManualCheckingActive: Boolean; begin Result := FManualCheckCount > 0; 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 CheckMode <> nil then SpellChecker.ValidateRules(CheckMode.Parser); end; function TdxSpellCheckerCustomCheckAsYouTypeManager.GetIsSpellCheckerReady: Boolean; begin Result := SpellChecker.ActiveDictionaryCount > 0; end; function TdxSpellCheckerCustomCheckAsYouTypeManager.GetOptions: TdxSpellCheckerCheckAsYouTypeOptions; begin Result := SpellChecker.CheckAsYouTypeOptions; end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.DoFinish(AControl: TWinControl); begin SpellChecker.DoCheckAsYouTypeFinish(AControl); end; function TdxSpellCheckerCustomCheckAsYouTypeManager.DoStart(AControl: TWinControl): Boolean; begin Result := SpellChecker.DoCheckAsYouTypeStart(AControl); end; function TdxSpellCheckerCustomCheckAsYouTypeManager.GetActive: Boolean; begin Result := Options.Active; end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.StartManualSpelling( AAdapter: TdxSpellCheckerCustomEditAdapter); begin SpellChecker.InternalCheck(AAdapter); end; procedure TdxSpellCheckerCustomCheckAsYouTypeManager.ValidateRules( AParser: TdxSpellCheckerTextParser); begin SpellChecker.ValidateRules(AParser); end; { TdxSpellCheckerCustomEditAdapter } constructor TdxSpellCheckerCustomEditAdapter.Create(AEdit: TWinControl); begin inherited Create; FEdit := AEdit; FNeedImmediatePost := (AEdit <> nil) and not AEdit.Focused; end; procedure TdxSpellCheckerCustomEditAdapter.ClearSelection; begin SelText := ''; 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; ATemp: AnsiString; begin if EditorHandleAllocated then begin if IsWindowUnicode(EditorHandle) then begin ALen := SendMessageW(EditorHandle, WM_GETTEXTLENGTH, 0, 0) + 1; SetLength(FText, ALen); GetWindowTextW(EditorHandle, Pointer(FText), ALen); end else begin ALen := SendMessageA(EditorHandle, WM_GETTEXTLENGTH, 0, 0) + 1; SetLength(ATemp, ALen); GetWindowTextA(EditorHandle, Pointer(ATemp), ALen); FText := dxAnsiStringToWideString(ATemp); end; end; Result := FText; end; procedure TdxSpellCheckerCustomEditAdapter.Post(AUpdateValue: Boolean = True); var AEdit: IdxSpellCheckerControl; begin if Supports(GetInnerControlContainer(FEdit), IdxSpellCheckerControl, 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 } 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 TcxCustomTextEditPropertiesAccess(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); var AIntf: IdxSpellCheckerControl; begin if Supports(Edit, IdxSpellCheckerControl, AIntf) then AIntf.SetSelText(AValue, FNeedImmediatePost) else Edit.SelText := AValue; end; function TdxSpellCheckercxTextEditAdapter.GetEdit: TcxCustomTextEdit; begin Result := TcxCustomTextEdit(inherited Edit); 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 } 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 if FEdit is TcxCustomRichEdit then Result := True 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: IdxSpellCheckerControl; 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), IdxSpellCheckerControl, AIntf) then AIntf.SetSelText(AValue, FNeedImmediatePost) else Edit.SelText := AValue; end; end; function TdxSpellCheckerEditAdapter.GetEdit: TCustomEdit; begin Result := TCustomEdit(inherited Edit); end; { TdxSpellCheckerTextAdapter } constructor TdxSpellCheckerTextAdapter.Create(const AText: WideString); begin inherited Create(nil); FText := AText + #0; end; { TdxXMLNode } constructor TdxXMLNode.Create; begin inherited Create; FAttributes := TStringList.Create; FChildList := TdxXMLNodes.Create; FValues := TStringList.Create; end; destructor TdxXMLNode.Destroy; begin FreeAndNil(FAttributes); FreeAndNil(FValues); FreeAndNil(FChildList); inherited Destroy; end; procedure TdxXMLNode.Assign(Source: TPersistent); var I: Integer; ANode: TdxXMLNode; begin if Source is TdxXMLNode then with TdxXMLNode(Source) do begin Self.Attributes.Clear; for I := 0 to Attributes.Count - 1 do Self.Attributes.Add(Attributes[I]); Self.Values.Clear; for I := 0 to Values.Count - 1 do Self.Values.Add(Values[I]); Self.ChildList.Clear; for I := 0 to ChildList.Count - 1 do begin ANode := TdxXMLNode.Create; ANode.Assign(ChildList[I]); Self.ChildList.Add(ANode); end; Self.FNodeName := NodeName; Self.FNameSpaces := NameSpaces; end; inherited Assign(Source); end; procedure TdxXMLNode.AddAttribute(const AName, AValue: WideString); var AIndex: Integer; begin AIndex := FindAttribute(AName); if AIndex = -1 then begin Attributes.Add(AName); Values.Add(''); SetAttribute(AName, AValue); end else AddAttribute(AName, AValue); end; procedure TdxXMLNode.AddChild(ANode: TdxXMLNode); begin ChildList.Add(ANode); end; function TdxXMLNode.FindChild(const ANodeName: WideString): TdxXMLNode; var I: Integer; begin Result := nil; for I := 0 to ChildList.Count - 1 do if WideCompareText(ChildList[I].NodeName, ANodeName) = 0 then begin Result := ChildList[I]; Break; end; end; procedure TdxXMLNode.FromStr(const S: WideString; var APos: Integer); procedure ParseChild; var ANode: TdxXMLNode; ALen: Integer; ATmp: WideString; begin ALen := Length(S); SkipSpace(S, APos); ATmp := GetNextPart(S, APos, '<', False); if (ALen > APos) and (Length(ATmp) > 0) then SpellCheckerCreatingError(@sdxSpellCheckerFileFormatMismatch); while (APos < ALen) and (S[APos] <> '/') do begin ANode := TdxXMLNode.Create; Dec(APos); Application.MainForm.Caption := IntToStr(APos); ANode.FromStr(S, APos); AddChild(ANode); SkipSpace(S, APos); GetNextPart(S, APos, '<', False); end; Inc(APos); if WideCompareText(GetNextPart(S, APos, '>', False), NodeName) <> 0 then SpellCheckerCreatingError(@sdxSpellCheckerFileFormatMismatch); end; var ATmp: WideString; AAttribute, AValue: WideString; ANodeString: WideString; ANodePos: Integer; ALen: Integer; AHasChild: Boolean; begin SkipSpace(S, APos); ATmp := GetNextPart(S, APos, '<', False); if Length(ATmp) > 0 then SpellCheckerCreatingError(@sdxSpellCheckerFileFormatMismatch); NodeName := GetNextPart(S, APos, ' '); SkipSpace(S, APos); ANodeString := GetNextPart(S, APos, '>', False); ALen := Length(ANodeString); ANodePos := 1; AHasChild := False; while ALen > ANodePos do begin SkipSpace(ANodeString, ANodePos); if ANodePos < ALen then begin SkipSpace(ANodeString, ANodePos); if ANodePos <= ALen then begin AAttribute := GetNextPart(ANodeString, ANodePos, '='); AddAttribute(AAttribute, ''); SkipSpace(ANodeString, ANodePos); GetNextPart(ANodeString, ANodePos, '"', False); SkipSpace(ANodeString, ANodePos); AValue := XMLValueToValue(GetNextPart(ANodeString, ANodePos, '"', False)); SetAttribute(AAttribute, AValue); SkipSpace(ANodeString, ANodePos); end; SkipSpace(ANodeString, ANodePos); if ANodeString[ANodePos] = '/' then begin if ANodePos <> ALen then SpellCheckerCreatingError(@sdxSpellCheckerFileFormatMismatch); end else AHasChild := ANodePos = ALen; end; end; if AHasChild then ParseChild; end; function TdxXMLNode.GetValue(const AAttribute: WideString): WideString; var AIndex: Integer; begin Result := ''; AIndex := FindAttribute(AAttribute); if AIndex >= 0 then Result := Values[AIndex]; end; function TdxXMLNode.HasChild: Boolean; begin Result := ChildList.Count > 0; end; procedure TdxXMLNode.SetAttribute(const AName, AValue: WideString); var AIndex: Integer; begin AIndex := FindAttribute(AName); if AIndex > -1 then Values[AIndex] := AValue else AddAttribute(AName, AValue); end; function TdxXMLNode.ToStr: UTF8String; var I: Integer; begin Result := '<' + dxWideStringToUTF8String(NodeName); if Length(NameSpaces) > 0 then Result := Result + ' xmlns:block-list="' + dxWideStringToUTF8String(NameSpaces) + '"'; for I := 0 to Attributes.Count - 1 do Result := Result + ' ' + dxWideStringToUTF8String(Attributes[I]) + '="' + ValueToXMLValue(Values[I]) + '"'; if HasChild then begin Result := Result + '>'; for I := 0 to ChildList.Count - 1 do Result := Result + ChildList[I].ToStr; Result := Result + ''; end else Result := Result + '/>'; end; function TdxXMLNode.FindAttribute(const AName: WideString): Integer; begin for Result := 0 to Attributes.Count - 1 do if WideCompareText(AName, Attributes[Result]) = 0 then Exit; Result := -1; end; function TdxXMLNode.GetChildCount: Integer; begin Result := ChildList.Count; end; { TdxXMLNodes } function TdxXMLNodes.GetItem(Index: Integer): TdxXMLNode; begin Result := TdxXMLNode(inherited Items[Index]); end; { TdxSpellCheckerLanguages } var FTmpLanguages: TdxSpellCheckerLanguages; procedure InternalEnumLanguages(ALocaleID: PChar); stdcall; begin FTmpLanguages.LocalesCallback(ALocaleID); end; constructor TdxSpellCheckerLanguages.Create; begin inherited; FTmpLanguages := Self; EnumSystemLocales(@InternalEnumLanguages, LCID_SUPPORTED); end; 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; function TdxSpellCheckerLanguages.IndexOf(ID: LCID): Integer; begin for Result := Low(FSysLangs) to High(FSysLangs) do if FSysLangs[Result].FLCID = ID then Exit; Result := -1; end; function TdxSpellCheckerLanguages.LocalesCallback(LocaleID: PChar): Integer; stdcall; var AID: LCID; AShortLangName: string; begin AID := StrToInt('$' + Copy(LocaleID, 5, 4)); AShortLangName := cxGetLocaleInfo(AID, LOCALE_SABBREVLANGNAME, ''); if AShortLangName <> '' then begin SetLength(FSysLangs, Length(FSysLangs) + 1); with FSysLangs[High(FSysLangs)] do begin FName := cxGetLocaleInfo(AID, LOCALE_SLANGUAGE, ''); FLCID := AID; FExt := AShortLangName; end; end; Result := 1; end; function TdxSpellCheckerLanguages.GetCount: integer; begin Result := High(FSysLangs) + 1; end; function TdxSpellCheckerLanguages.GetExt(Index: Integer): string; begin Result := FSysLangs[Index].FExt; end; function TdxSpellCheckerLanguages.GetID(Index: Integer): string; begin Result := HexDisplayPrefix + IntToHex(FSysLangs[Index].FLCID, 8); end; function TdxSpellCheckerLanguages.GetLocaleID(Index: Integer): LCID; begin Result := FSysLangs[Index].FLCID; end; function TdxSpellCheckerLanguages.GetName(Index: Integer): string; begin Result := FSysLangs[Index].FName; end; function TdxSpellCheckerLanguages.GetNameFromLocaleID(ID: LCID): string; var AIndex: Integer; begin Result := sdxSpellCheckerUnknown; AIndex := IndexOf(ID); if AIndex <> - 1 then Result := Name[AIndex]; if Result = '' then Result := sdxSpellCheckerUnknown; end; function TdxSpellCheckerLanguages.GetNameFromLCID(const ID: string): string; begin Result := NameFromLocaleID[StrToIntDef(ID, 0)]; end; initialization GetRegisteredDictionaryTypes.Register( TdxUserSpellCheckerDictionary, cxGetResourceString(@sdxSpellCheckerUserDictionary)); finalization FreeRegisteredDictionaryTypes; end.