Componentes.Terceros.DevExp.../internal/x.48/1/ExpressDataController/Sources/cxCustomData.pas

13666 lines
410 KiB
ObjectPascal
Raw Permalink Normal View History

{********************************************************************}
{ }
{ Developer Express Visual Component Library }
{ ExpressDataController }
{ }
{ 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 EXPRESSDATACONTROLLER 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 cxCustomData;
{$I cxVer.inc}
interface
uses
SysUtils, Classes, {$IFDEF DELPHI6}Variants, {$ENDIF}cxVariants,
cxDataUtils, cxDataStorage, cxFilter, dxCore;
const
cxNullEditingRecordIndex = -MaxInt;
type
TcxCustomDataControllerInfo = class;
TcxCustomDataController = class;
TcxCustomDataProvider = class;
TcxCustomDataField = class;
TcxCustomDataFieldList = class;
TcxCustomDataRelationList = class;
TcxDataSummaryItem = class;
TcxDataSummaryItems = class;
TcxDataSummaryGroup = class;
TcxDataSummaryGroups = class;
TcxDataSummaryGroupItemLinks = class;
TcxDataSummary = class;
EcxInvalidDataControllerOperation = class(EdxException);
{ TcxCustomDataSource }
TcxDataRecordHandle = Pointer;
TcxDataItemHandle = Pointer;
TcxCustomDataSource = class
private
FCurrentProvider: TcxCustomDataProvider;
FProvider: TcxCustomDataProvider;
FProviders: TList;
function GetDataController: TcxCustomDataController;
function GetProvider: TcxCustomDataProvider;
procedure AddProvider(AProvider: TcxCustomDataProvider);
procedure RemoveProvider(AProvider: TcxCustomDataProvider);
procedure RemoveFromProviders;
protected
function AppendRecord: TcxDataRecordHandle; virtual; // *
procedure CustomSort; virtual; // when IsCustomSorting = True
procedure DeleteRecord(ARecordHandle: TcxDataRecordHandle); virtual; // *
function GetDefaultItemID(AItemIndex: Integer): Integer; virtual;
function GetDetailHasChildren(ARecordIndex, ARelationIndex: Integer): Boolean; virtual;
function GetDisplayText(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): string; virtual;
function GetInfoForCompare(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle; var PValueBuffer: PAnsiChar): Boolean; virtual;
function GetItemHandle(AItemIndex: Integer): TcxDataItemHandle; virtual;
function GetRecordCount: Integer; virtual; // *
function GetRecordId(ARecordHandle: TcxDataRecordHandle): Variant; virtual; // Save Keys Required
function GetRecordHandle(ARecordIndex: Integer): TcxDataRecordHandle; virtual;
function GetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): Variant; virtual; // *
function InsertRecord(ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle; virtual; // *
function IsCustomSorting: Boolean; virtual;
function IsNativeCompare: Boolean; virtual;
function IsNativeCompareFunc: Boolean; virtual;
function IsRecordIdSupported: Boolean; virtual; // Save Keys Required
procedure LoadRecordHandles; virtual;
function NativeCompareFunc(ARecordHandle1, ARecordHandle2: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): Integer; virtual;
procedure SetRecordCount(ARecordCount: Integer); virtual;
procedure SetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle; const AValue: Variant); virtual; // *
property CurrentProvider: TcxCustomDataProvider read FCurrentProvider write FCurrentProvider;
// tree
function AddRecordHandle(ARecordHandle: TcxDataRecordHandle): Integer;
public
destructor Destroy; override;
procedure DataChanged; virtual;
function GetRecordHandleByIndex(ARecordIndex: Integer): TcxDataRecordHandle;
function GetRecordIndexByHandle(ARecordHandle: TcxDataRecordHandle): Integer;
property DataController: TcxCustomDataController read GetDataController;
property Provider: TcxCustomDataProvider read GetProvider;
end;
TcxCustomDataSourceClass = class of TcxCustomDataSource;
{ TcxCustomDataProvider }
TcxDataChange = (dcField, dcRecord, dcNew, dcDeleted, dcTotal);
TcxDataChangeInfo = record
Kind: TcxDataChange;
ItemIndex: Integer;
RecordIndex: Integer;
end;
TcxDataLayoutChange = (lcStructure, lcData);
TcxDataLayoutChanges = set of TcxDataLayoutChange;
TcxDataSummaryCountValues = array of Integer;
TcxDataSummaryValues = array of Variant;
TcxCustomDataProvider = class
private
FActiveChanging: Boolean;
FChanging: Boolean;
FCustomDataSource: TcxCustomDataSource;
FDataChangedLocked: Boolean;
FDataController: TcxCustomDataController;
FEditingRecordIndex: Integer;
FEditingRecordIndex1: Integer; // After
FEditingRecordIndex2: Integer; // Before
FInsertedRecordIndex: Integer;
FInUpdateData: Boolean;
FLocateCount: Integer;
FLockCount: Integer;
FModified: Boolean;
FRecreatedFieldsAfterFirst: Boolean;
FSavedRecordIndex: Integer;
procedure SetCustomDataSource(Value: TcxCustomDataSource);
protected
FInInserting: Boolean;
FInserting: Boolean;
FLoadAllNeeded: Boolean;
// Mode
procedure CustomSort; virtual; // when IsCustomSorting = True
function IsCustomDataSourceSupported: Boolean; virtual;
function IsCustomSorting: Boolean; virtual;
function IsGridMode: Boolean; virtual;
function IsDataSource: Boolean; virtual;
function IsOtherInsert: Boolean; virtual;
function IsRecordIdSupported: Boolean; virtual;
function IsSyncMode: Boolean; virtual;
// State
function IsActive: Boolean; virtual;
function IsActiveDataSet: Boolean; virtual;
function IsBOF: Boolean; virtual;
function IsChanging: Boolean; virtual;
function IsEditing: Boolean; virtual;
function IsEOF: Boolean; virtual;
function IsGridModeUpdating: Boolean; virtual;
function IsInserting: Boolean; virtual;
function IsModified: Boolean; virtual;
function IsUnboundColumnMode: Boolean; virtual;
// Navigation
procedure CorrectRecordIndex(ARecordIndex: Integer); virtual;
procedure First; virtual;
procedure Prev; virtual;
procedure Next; virtual;
procedure Last; virtual;
procedure MoveBy(ADistance: Integer); virtual;
procedure Scroll(ADistance: Integer); virtual;
procedure SavePos; virtual;
procedure RestorePos; virtual;
// Editing
function CanAppend: Boolean; virtual;
function CanDelete: Boolean; virtual;
function CanInitEditing(ARecordIndex: Integer): Boolean; virtual;
function CanInsert: Boolean; virtual;
function CanModify: Boolean; virtual;
procedure Append; virtual;
procedure Cancel; virtual;
procedure DoUpdateData; virtual;
procedure Delete; virtual;
procedure DeleteRecords(AList: TList); virtual;
procedure DeleteSelection; virtual;
procedure Edit; virtual;
function GetEditValue(ARecordIndex: Integer; AField: TcxCustomDataField; AEditValueSource: TcxDataEditValueSource): Variant; virtual;
procedure Insert; virtual;
procedure Post(AForcePost: Boolean = False); virtual;
procedure PostEditingData; virtual;
function SetEditValue(ARecordIndex: Integer; AField: TcxCustomDataField; const AValue: Variant; AEditValueSource: TcxDataEditValueSource): Boolean; virtual;
procedure BeginDeleting; virtual;
procedure EndDeleting; virtual;
procedure AssignItemValue(ARecordIndex: Integer; AField: TcxCustomDataField; const AValue: Variant); virtual;
procedure ClearSavedRecord;
procedure CreateSavedRecord(ARecordIndex: Integer);
procedure DoInitInsertingRecord(AInsertingRecordIndex: Integer; const AGroupValues: TcxDataSummaryValues); virtual;
procedure EditingRecord; virtual;
procedure InsertingRecord(AIsAppending: Boolean); virtual;
procedure ResetChanging; virtual;
procedure ResetEditing; virtual;
procedure ResetModified; virtual;
procedure SetChanging; virtual;
procedure SetEditing; virtual;
procedure SetModified; virtual;
// Lock Notify
procedure BeginLocate; virtual;
procedure EndLocate; virtual;
procedure Freeze; virtual;
procedure Unfreeze; virtual;
// Data
function AddRecordHandle(AData: Pointer): Integer;
function AppendRecord: Integer; virtual;
procedure DeleteRecord(ARecordIndex: Integer); virtual;
function GetDetailHasChildren(ARecordIndex, ARelationIndex: Integer): Boolean; virtual;
function GetDisplayText(ARecordIndex: Integer; AField: TcxCustomDataField): string; virtual;
// function GetRecordCount: Integer; virtual;
function GetExternalDataDisplayText(ARecordIndex: Integer; AField: TcxCustomDataField): string; virtual;
function GetExternalDataValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant; virtual;
function GetRecordId(ARecordIndex: Integer): Variant; virtual;
function GetRecordIndex: Integer; virtual;
function GetValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant; virtual;
function GetValueDefReaderClass: TcxValueDefReaderClass; virtual;
function InsertRecord(ARecordIndex: Integer): Integer; virtual;
procedure LoadDataBuffer; virtual;
procedure LoadRecordHandles;
function NativeCompare(ARecordIndex1, ARecordIndex2: Integer; AField: TcxCustomDataField): Integer; virtual;
function NativeCompareFunc(ARecordIndex1, ARecordIndex2: Integer; AField: TcxCustomDataField): Integer; virtual;
procedure SetDisplayText(ARecordIndex: Integer; AField: TcxCustomDataField; const Value: string); virtual;
procedure SetRecordCount(ARecordCount: Integer); virtual;
procedure SetValue(ARecordIndex: Integer; AField: TcxCustomDataField; const Value: Variant); virtual;
// Notification
procedure ActiveChanged(AActive: Boolean); virtual;
procedure DataChanged(ADataChange: TcxDataChange; AItemIndex, ARecordIndex: Integer); virtual;
procedure DataScrolled(ADistance: Integer); virtual;
procedure LayoutChanged(ADataLayoutChanges: TcxDataLayoutChanges); virtual;
property ActiveChanging: Boolean read FActiveChanging write FActiveChanging;
property CustomDataSource: TcxCustomDataSource read FCustomDataSource write SetCustomDataSource;
property DataController: TcxCustomDataController read FDataController;
property EditingRecordIndex: Integer read FEditingRecordIndex;
property InsertedRecordIndex: Integer read FInsertedRecordIndex;
property LocateCount: Integer read FLocateCount;
property LockCount: Integer read FLockCount;
property RecreatedFieldsAfterFirst: Boolean read FRecreatedFieldsAfterFirst write FRecreatedFieldsAfterFirst;
public
constructor Create(ADataController: TcxCustomDataController); virtual;
destructor Destroy; override;
end;
TcxCustomDataProviderClass = class of TcxCustomDataProvider;
{ TcxCustomDataField }
TcxCustomDataField = class
private
FFieldList: TcxCustomDataFieldList;
FIsInternal: Boolean;
FItem: TObject;
FLockRecreateData: Integer;
FNeedConversion: Boolean;
FNeedConversionFlag: Integer; // 0 - undefined; -1 - not needed; 1 - needed; 2 - SortByDisplayText
FPrepared: Boolean;
FReferenceField: TcxCustomDataField;
FTextStored: Boolean;
FValueDef: TcxValueDef;
FValueTypeClass: TcxValueTypeClass;
function GetDataController: TcxCustomDataController;
function GetIndex: Integer;
function GetNotifier: TComponent;
function GetTextStored: Boolean;
function GetValueDef: TcxValueDef;
function GetValueTypeClass: TcxValueTypeClass;
procedure SetIndex(Value: Integer);
procedure SetIsInternal(Value: Boolean);
procedure SetReferenceField(Value: TcxCustomDataField);
procedure SetTextStored(Value: Boolean);
procedure SetValueTypeClass(Value: TcxValueTypeClass);
protected
procedure BeginRecreateData;
procedure Changed;
procedure ClearData;
procedure CreateData;
procedure EndRecreateData;
procedure Reassign(ASource: TcxCustomDataField); virtual;
procedure RemoveNotification(AComponent: TComponent); virtual;
property FieldList: TcxCustomDataFieldList read FFieldList;
property NeedConversion: Boolean read FNeedConversion write FNeedConversion;
property Notifier: TComponent read GetNotifier;
property TextStored: Boolean read GetTextStored write SetTextStored;
property ValueDef: TcxValueDef read GetValueDef;
public
constructor Create(AFieldList: TcxCustomDataFieldList); virtual;
destructor Destroy; override;
function CanModify(AEditValueSource: TcxDataEditValueSource): Boolean; virtual;
function IsUnbound: Boolean; virtual;
function IsValueDefInternal: Boolean; virtual;
property DataController: TcxCustomDataController read GetDataController;
property Index: Integer read GetIndex write SetIndex;
property IsInternal: Boolean read FIsInternal write SetIsInternal;
property Item: TObject read FItem write FItem;
property ReferenceField: TcxCustomDataField read FReferenceField write SetReferenceField;
property ValueTypeClass: TcxValueTypeClass read GetValueTypeClass write SetValueTypeClass;
end;
TcxCustomDataFieldClass = class of TcxCustomDataField;
{ TcxCustomDataFieldList }
TcxCustomDataFieldList = class
private
FDataController: TcxCustomDataController;
FFieldDestroyed: Boolean;
FInternalCount: Integer;
FItems: TList;
function GetCount: Integer;
function GetItem(Index: Integer): TcxCustomDataField;
function GetItemCount: Integer;
protected
procedure Add(AField: TcxCustomDataField);
procedure Clear;
procedure RemoveField(AField: TcxCustomDataField);
procedure RemoveNotification(AComponent: TComponent); virtual;
procedure Update;
public
constructor Create(ADataController: TcxCustomDataController); virtual;
destructor Destroy; override;
function FieldByItem(AItem: TObject): TcxCustomDataField;
procedure ReassignFields(ADestroyedField: TcxCustomDataField);
property Count: Integer read GetCount;
property DataController: TcxCustomDataController read FDataController;
property ItemCount: Integer read GetItemCount;
property Items[Index: Integer]: TcxCustomDataField read GetItem; default;
end;
{ Master-Detail }
TcxDetailInfoObject = class
LinkObject: TObject;
HasChildren: Boolean;
HasChildrenAssigned: Boolean;
public
destructor Destroy; override;
end;
TcxDetailObject = class
private
FActiveRelationIndex: Integer;
FInfoObjects: TList;
FExpanded: Boolean;
function GetInfoObject(Index: Integer): TcxDetailInfoObject;
function GetInfoObjectCount: Integer;
function GetIsEmpty: Boolean;
function GetLinkObject(Index: Integer): TObject;
function GetLinkObjectCount: Integer;
procedure SetInfoObject(Index: Integer; Value: TcxDetailInfoObject);
procedure SetLinkObject(Index: Integer; Value: TObject);
protected
procedure CorrectCount(ARelations: TcxCustomDataRelationList);
public
constructor Create;
destructor Destroy; override;
procedure Clear;
function ClearHasChildrenFlag: Boolean;
procedure ClearInfoObject(AIndex: Integer);
property ActiveRelationIndex: Integer read FActiveRelationIndex write FActiveRelationIndex;
property Expanded: Boolean read FExpanded write FExpanded;
property InfoObjectCount: Integer read GetInfoObjectCount;
property InfoObjects[Index: Integer]: TcxDetailInfoObject read GetInfoObject write SetInfoObject;
property IsEmpty: Boolean read GetIsEmpty;
property LinkObjectCount: Integer read GetLinkObjectCount;
property LinkObjects[Index: Integer]: TObject read GetLinkObject write SetLinkObject;
end;
{ TcxCustomDataRelation }
TcxCustomDataRelation = class
private
FDetailDataController: TcxCustomDataController;
FItem: TObject;
FRelationList: TcxCustomDataRelationList;
function GetDataController: TcxCustomDataController;
function GetIndex: Integer;
protected
procedure Changed;
procedure RemoveDataField(ADataField: TcxCustomDataField); virtual;
public
constructor Create(ARelationList: TcxCustomDataRelationList; AItem: TObject); virtual;
destructor Destroy; override;
procedure Assign(ASource: TcxCustomDataRelation); virtual;
property DataController: TcxCustomDataController read GetDataController;
property DetailDataController: TcxCustomDataController read FDetailDataController;
property Index: Integer read GetIndex;
property Item: TObject read FItem;
property RelationList: TcxCustomDataRelationList read FRelationList;
end;
TcxCustomDataRelationClass = class of TcxCustomDataRelation;
{ TcxCustomDataRelationList }
TcxCustomDataRelationList = class
private
FDataController: TcxCustomDataController;
FDataField: TcxCustomDataField;
FItems: TList;
FLockCount: Integer;
procedure AddItem(AItem: TcxCustomDataRelation);
function GetCount: Integer;
function GetItem(Index: Integer): TcxCustomDataRelation;
procedure RemoveItem(AItem: TcxCustomDataRelation);
protected
procedure Changed(ARelation: TcxCustomDataRelation);
function GetValueAsDetailObject(ARecordIndex: Integer): TcxDetailObject;
procedure RemoveDataField(ADataField: TcxCustomDataField);
procedure Update(ARelation: TcxCustomDataRelation);
property DataController: TcxCustomDataController read FDataController;
property LockCount: Integer read FLockCount;
public
constructor Create(ADataController: TcxCustomDataController);
destructor Destroy; override;
function Add(AItem: TObject): TcxCustomDataRelation;
procedure Assign(ASource: TcxCustomDataRelationList);
procedure BeginUpdate;
procedure Clear;
function ClearDetailObject(ARecordIndex, ARelationIndex: Integer): Boolean;
procedure EndUpdate;
function FindByItem(AItem: TObject): TcxCustomDataRelation;
function GetDetailObject(ARecordIndex: Integer): TcxDetailObject;
function IsDetailObjectExist(ARecordIndex, ARelationIndex: Integer): Boolean;
function IsEmpty: Boolean;
procedure Move(ACurIndex, ANewIndex: Integer);
procedure RemoveByItem(AItem: TObject);
property Count: Integer read GetCount;
property Items[Index: Integer]: TcxCustomDataRelation read GetItem; default;
end;
{ TcxCustomDataController }
{ TcxSortingFieldList }
TcxDataSortOrder = (soNone, soAscending, soDescending);
TcxDataSortInfo = record
Field: TcxCustomDataField;
SortOrder: TcxDataSortOrder;
end;
PcxDataSortInfo = ^TcxDataSortInfo;
TcxNoParamsEvent = procedure of object;
TcxSortingFieldList = class
private
FItems: TList;
FOnChanged: TcxNoParamsEvent;
function GetCount: Integer;
function GetItem(Index: Integer): TcxDataSortInfo;
protected
procedure Add(AField: TcxCustomDataField; ASortOrder: TcxDataSortOrder);
procedure Changed;
procedure Delete(Index: Integer);
function Find(AField: TcxCustomDataField): Integer;
procedure Insert(Index: Integer; AField: TcxCustomDataField; ASortOrder: TcxDataSortOrder);
procedure Move(ACurIndex, ANewIndex: Integer);
procedure Remove(AField: TcxCustomDataField);
procedure SetSortOrder(Index: Integer; ASortOrder: TcxDataSortOrder);
public
constructor Create;
destructor Destroy; override;
procedure AppendFrom(AList: TcxSortingFieldList);
procedure ChangeSorting(AField: TcxCustomDataField; ASortOrder: TcxDataSortOrder); virtual;
procedure CheckField(AField: TcxCustomDataField);
procedure Clear;
function SortIndexByField(AField: TcxCustomDataField): Integer;
function SortOrderByField(AField: TcxCustomDataField): TcxDataSortOrder;
property Count: Integer read GetCount;
property Items[Index: Integer]: TcxDataSortInfo read GetItem; default;
property OnChanged: TcxNoParamsEvent read FOnChanged write FOnChanged;
end;
TcxGroupingFieldList = class(TcxSortingFieldList)
public
procedure ChangeGrouping(AField: TcxCustomDataField; AGroupIndex: Integer);
procedure ChangeSorting(AField: TcxCustomDataField; ASortOrder: TcxDataSortOrder); override;
function GroupIndexByField(AField: TcxCustomDataField): Integer;
procedure UpdateSorting(ASortingFieldList: TcxSortingFieldList);
end;
{ TcxDataGroups }
TcxDataGroupInfo = class
RowIndex: Integer;
Expanded: Boolean;
Level: Integer;
BeginRecordListIndex: Integer;
EndRecordListIndex: Integer;
SummaryValues: Variant;
end;
TcxGroupsRowInfo = record
Expanded: Boolean;
Level: Integer;
RecordListIndex: Integer;
end;
TcxDataGroups = class
private
FFields: TList;
FItems: TList;
function GetField(Index: Integer): TcxCustomDataField;
function GetFieldCount: Integer;
function GetItem(Index: Integer): TcxDataGroupInfo;
function GetItemCount: Integer;
function GetRowCount: Integer;
function GetRowInfo(ARowIndex: Integer): TcxGroupsRowInfo;
function GetTopVisibleItem(ARowIndex: Integer; var ACurIndex: Integer): TcxDataGroupInfo;
protected
procedure Add(const ADataGroupInfo: TcxDataGroupInfo);
procedure AddField(AField: TcxCustomDataField);
function AddEmpty: TcxDataGroupInfo;
procedure Clear;
function Find(ARowIndex: Integer; var AItem: TcxDataGroupInfo): Integer;
function FindByIndex(ARecordListIndex, ALevel: Integer): Integer;
function IsVisible(AIndex: Integer): Boolean;
function MakeVisible(AIndex: Integer; AExpand: Boolean): Boolean;
property FieldCount: Integer read GetFieldCount;
property Fields[Index: Integer]: TcxCustomDataField read GetField;
public
constructor Create;
destructor Destroy; override;
procedure ChangeExpanding(ARowIndex: Integer; AExpanded, ARecursive: Boolean);
procedure FullExpanding(AExpanded: Boolean);
function GetChildCount(AIndex: Integer): Integer;
function GetChildIndex(AParentIndex, AIndex: Integer): Integer;
function GetChildRecordListIndex(AParentIndex, AIndex: Integer): Integer;
function GetDataRecordListIndex(AInfo: TcxDataGroupInfo): Integer;
function GetFirstLevelCount: Integer;
function GetFirstLevelIndex(AIndex: Integer): Integer;
function GetIndexByRowIndex(ARowIndex: Integer): Integer;
function GetIndexByRowIndexLevel(ARowIndex, ALevel: Integer): Integer;
function GetLevel(AIndex: Integer): Integer;
function GetParentIndex(AChildIndex: Integer): Integer;
procedure Rebuild;
procedure SetItem(AIndex: Integer; AItem: TcxDataGroupInfo);
property Count: Integer read GetItemCount;
property Items[Index: Integer]: TcxDataGroupInfo read GetItem; default;
property LevelCount: Integer read GetFieldCount;
property RowInfo[RowIndex: Integer]: TcxGroupsRowInfo read GetRowInfo;
property RowCount: Integer read GetRowCount;
end;
{ TcxCustomDataControllerInfo }
TcxDataControllerInfoChange = (dcicLoad, dcicGrouping, dcicSorting,
dcicFocusedRow, dcicView, dcicExpanding, dcicResetFocusedRow, dcicSelection,
dcicSummary, dcicFocusedRecord);
TcxDataControllerInfoChanges = set of TcxDataControllerInfoChange;
TcxRowInfo = record
Expanded: Boolean;
Level: Integer;
RecordIndex: Integer;
end;
TcxDataFocusingInfo = class
private
FChangedFlag: Boolean;
FDataControllerInfo: TcxCustomDataControllerInfo;
FLevel: Integer;
FPrevNewItemRowFocused: Boolean;
FPrevRecordIndex: Integer;
FRecordIndex: Integer;
FRowIndex: Integer;
protected
procedure Assign(AFocusingInfo: TcxDataFocusingInfo);
procedure Clear;
function IsEqual(AFocusingInfo: TcxDataFocusingInfo): Boolean;
procedure ResetPos;
procedure SavePos;
property Level: Integer read FLevel write FLevel;
property RecordIndex: Integer read FRecordIndex write FRecordIndex;
property RowIndex: Integer read FRowIndex write FRowIndex;
public
constructor Create(ADataControllerInfo: TcxCustomDataControllerInfo);
end;
TcxDataExpandingInfoState = (eisExpanded, eisSelected);
TcxDataExpandingInfoStateSet = set of TcxDataExpandingInfoState;
TcxDataExpandingInfo = record
Level: Integer;
Value: Variant;
State: TcxDataExpandingInfoStateSet;
end;
PcxDataExpandingInfo = ^TcxDataExpandingInfo;
TcxDataExpandingInfos = class
private
FFields: TList;
FItems: TList;
FSaveStates: TcxDataExpandingInfoStateSet;
function Find(AField: TcxCustomDataField): Integer;
function GetCount: Integer;
function GetEmpty: Boolean;
function GetField(Index: Integer): TcxCustomDataField;
function GetFieldCount: Integer;
function GetItem(Index: Integer): PcxDataExpandingInfo;
protected
procedure AddField(AField: TcxCustomDataField);
procedure AddItem(ALevel: Integer; const AValue: Variant; AState: TcxDataExpandingInfoStateSet);
procedure CheckField(AField: TcxCustomDataField);
procedure Clear;
procedure ClearFields;
procedure ClearValues;
public
constructor Create;
destructor Destroy; override;
property Count: Integer read GetCount;
property Empty: Boolean read GetEmpty;
property FieldCount: Integer read GetFieldCount;
property Fields[Index: Integer]: TcxCustomDataField read GetField;
property Items[Index: Integer]: PcxDataExpandingInfo read GetItem; default;
property SaveStates: TcxDataExpandingInfoStateSet read FSaveStates write FSaveStates;
end;
TcxDataSelectionInfo = record
Level: Integer;
RecordIndex: Integer; // if Level = -1 then RecordIndex is GroupIndex (in DataGroups)
RowIndex: Integer;
end;
PcxDataSelectionInfo = ^TcxDataSelectionInfo;
TcxDataSelection = class
private
FAnchorRowIndex: Integer;
FDataController: TcxCustomDataController;
FItems: TList;
FFields: TList;
FOnChanged: TcxNoParamsEvent;
function GetCount: Integer;
function GetField(Index: Integer): TcxCustomDataField;
function GetFieldCount: Integer;
function GetItem(Index: Integer): PcxDataSelectionInfo;
protected
procedure Changed;
procedure CheckAfterFiltering;
procedure ClearAnchor; virtual;
procedure InternalAdd(AIndex, ARowIndex, ARecordIndex, ALevel: Integer); virtual;
procedure InternalClear; virtual;
procedure InternalDelete(AIndex: Integer); virtual;
procedure SetInternalCount(ACount: Integer); virtual;
public
constructor Create(ADataController: TcxCustomDataController); virtual;
destructor Destroy; override;
procedure Add(AIndex, ARowIndex, ARecordIndex, ALevel: Integer); virtual;
procedure AddField(AField: TcxCustomDataField);
procedure Clear;
procedure ClearAll;
procedure ClearFields;
procedure Delete(AIndex: Integer);
function FindByGroupIndex(AGroupIndex: Integer): Integer;
function FindByRecordIndex(ARecordIndex: Integer): Integer;
function FindByRowIndex(ARowIndex: Integer; var AIndex: Integer): Boolean; virtual;
function IsRecordSelected(ARecordIndex: Integer): Boolean;
function IsRowSelected(ARowIndex: Integer): Boolean;
procedure Sort;
property Count: Integer read GetCount;
property DataController: TcxCustomDataController read FDataController;
property FieldCount: Integer read GetFieldCount;
property Fields[Index: Integer]: TcxCustomDataField read GetField;
property Items[Index: Integer]: PcxDataSelectionInfo read GetItem; default;
property AnchorRowIndex: Integer read FAnchorRowIndex;
property OnChanged: TcxNoParamsEvent read FOnChanged write FOnChanged;
end;
TcxDataSelectionClass = class of TcxDataSelection;
TcxCustomDataControllerInfo = class
private
FChanges: TcxDataControllerInfoChanges;
FDataController: TcxCustomDataController;
FDataGroups: TcxDataGroups;
FExpandingInfo: TcxDataExpandingInfos;
FExpandingFlag: Boolean;
FFocusingFlag: Boolean;
FFocusingInfo: TcxDataFocusingInfo;
FGroupingFieldList: TcxGroupingFieldList;
FInCanFocusedRowChanging: Boolean;
FInfoCalculation: Boolean;
FLockCount: Integer;
FPrevFocusingInfo: TcxDataFocusingInfo;
FRecordList: TList;
FSelection: TcxDataSelection;
FSortingFieldList: TcxSortingFieldList;
FTotalSortingFieldList: TcxSortingFieldList;
procedure CheckAfterCollapsing;
procedure CheckExpanding;
procedure CheckFocusing;
procedure CheckFocusingAfterFilter;
procedure CheckInfo;
procedure ClearInfo;
function CompareRecords(ARecord1, ARecord2: Pointer): Integer;
function GetFocusedRecordIndex: Integer;
function GetFocusedRowIndex: Integer;
function GetNewItemRowFocusingChanged: Boolean;
function GetPrevFocusedRecordIndex: Integer;
function GetPrevFocusedRowIndex: Integer;
procedure SaveInfo;
procedure SetFocusedRowIndex(Value: Integer);
protected
function CanFocusedRowChanging(ARowIndex: Integer): Boolean;
procedure CorrectFocusedRow(ARowIndex: Integer);
procedure DoChangeFocusedRow(AValue: Integer; AIsInternal: Boolean);
procedure DoFilter;
procedure DoGrouping;
procedure DoLoad;
procedure DoSort;
procedure DoBeginSummary;
procedure DoEndSummary;
function FindDataGroup(ARecordListIndex: Integer): Integer;
function FindFocusedRow(ANearest: Boolean): Integer;
procedure ForwardChanges;
function GetInternalRecordCount: Integer;
function GetInternalRecordListIndex(ARecordIndex: Integer): Integer;
function IsAlwaysExpanded: Boolean;
function IsGrouped: Boolean;
function IsSummaryForSelectedRecords: Boolean;
function LocateGroupByLevel(AGroupIndex, ALevel: Integer): Integer;
function LocateDetail(AGroupIndex, ARecordListIndex: Integer; AMakeVisible: Boolean): Integer;
procedure PrepareSorting;
procedure RecalcSelection;
procedure ResetFocusing;
procedure SaveExpanding(ASaveStates: TcxDataExpandingInfoStateSet);
procedure TruncateSelection; virtual;
procedure UnprepareSorting;
procedure Update; virtual;
// Sorting By Summary
procedure DoSortBySummary;
function IsSortingBySummary: Boolean;
property SortingFieldList: TcxSortingFieldList read FSortingFieldList;
property TotalSortingFieldList: TcxSortingFieldList read FTotalSortingFieldList;
public
constructor Create(ADataController: TcxCustomDataController); virtual;
destructor Destroy; override;
procedure BeginUpdate;
procedure EndUpdate;
procedure CheckChanges;
procedure CheckRowIndex(ARowIndex: Integer);
function GetInternalRecordIndex(ARecordListIndex: Integer): Integer;
procedure Refresh;
procedure RefreshFocused;
procedure RefreshSummary(ARedrawOnly: Boolean);
procedure RefreshView;
// Notify
procedure ExpandingChanged;
procedure FocusedRecordChanged(AChangedFlag: Boolean);
procedure GroupingChanged;
procedure SelectionChanged;
procedure SortingChanged;
// Structure
procedure RemoveField(AField: TcxCustomDataField);
procedure UpdateField(AField: TcxCustomDataField);
// Sorting
procedure ChangeSortIndex(AField: TcxCustomDataField; ASortIndex: Integer);
procedure ChangeSorting(AField: TcxCustomDataField; ASortOrder: TcxDataSortOrder);
procedure ClearSorting(AKeepGroupedItems: Boolean);
// Grouping
procedure ChangeExpanding(ARowIndex: Integer; AExpanded, ARecursive: Boolean);
procedure ChangeGrouping(AField: TcxCustomDataField; AGroupIndex: Integer);
procedure ClearGrouping;
function CompareGroupRecords(ARecordIndex1, ARecordIndex2, AIndex: Integer): Integer;
procedure FullCollapse;
procedure FullExpand;
property DataGroups: TcxDataGroups read FDataGroups;
property GroupingFieldList: TcxGroupingFieldList read FGroupingFieldList;
// View Data
function GetNearestRowIndex(ARowIndex: Integer): Integer;
function GetRowCount: Integer;
function GetRowIndexByRecordIndex(ARecordIndex: Integer; AMakeVisible: Boolean): Integer;
function GetRowInfo(ARowIndex: Integer): TcxRowInfo;
// Selection
procedure ChangeRowSelection(ARowIndex: Integer; ASelection: Boolean);
procedure ClearSelection;
procedure ClearSelectionAnchor;
function GetSelectedCount: Integer;
function GetSelectedRowIndex(Index: Integer): Integer;
function IsRowSelected(ARowIndex: Integer): Boolean;
property Selection: TcxDataSelection read FSelection;
// Navigation
property FocusedRecordIndex: Integer read GetFocusedRecordIndex;
property FocusedRowIndex: Integer read GetFocusedRowIndex write SetFocusedRowIndex;
property NewItemRowFocusingChanged: Boolean read GetNewItemRowFocusingChanged;
property PrevFocusedRecordIndex: Integer read GetPrevFocusedRecordIndex;
property PrevFocusedRowIndex: Integer read GetPrevFocusedRowIndex;
property DataController: TcxCustomDataController read FDataController;
property LockCount: Integer read FLockCount;
end;
{ Filter }
TcxDataFilterCriteriaItem = class(TcxFilterCriteriaItem)
private
function GetDataController: TcxCustomDataController;
function GetField: TcxCustomDataField;
protected
function GetDataValue(AData: TObject): Variant; override;
function GetFieldCaption: string; override;
function GetFieldName: string; override;
function GetItemLink: TObject; override;
procedure SetItemLink(Value: TObject); override;
public
property DataController: TcxCustomDataController read GetDataController;
property Field: TcxCustomDataField read GetField;
end;
TcxDataFilterValueList = class(TcxFilterValueList)
private
function GetDataController: TcxCustomDataController;
public
procedure Load(AItemIndex: Integer; AInitSortByDisplayText: Boolean = True;
AUseFilteredValues: Boolean = False); virtual;
property DataController: TcxCustomDataController read GetDataController;
end;
TcxDataFilterGetValueListEvent = procedure(Sender: TcxFilterCriteria;
AItemIndex: Integer; AValueList: TcxDataFilterValueList) of object;
TcxDataFilterCriteria = class(TcxFilterCriteria)
private
FActive: Boolean;
FDataController: TcxCustomDataController;
FDestroying: Boolean;
FOnGetValueList: TcxDataFilterGetValueListEvent;
procedure SetActive(Value: Boolean);
protected
procedure DefineProperties(Filer: TFiler); override;
function DoFilterRecord(ARecordIndex: Integer): Boolean;
function GetIDByItemLink(AItemLink: TObject): Integer; override;
function GetNameByItemLink(AItemLink: TObject): string; override;
function GetItemClass: TcxFilterCriteriaItemClass; override;
function GetItemLinkByID(AID: Integer): TObject; override;
function GetItemLinkByName(const AName: string): TObject; override;
function GetValueListClass: TcxFilterValueListClass; override;
function IsDestroying: Boolean;
function IsInternal: Boolean;
function IsLoading: Boolean;
procedure Update; override;
property Destroying: Boolean read FDestroying;
public
constructor Create(ADataController: TcxCustomDataController); virtual;
destructor Destroy; override;
procedure Assign(Source: TPersistent; AIgnoreItemNames: Boolean = False); override;
procedure AssignEvents(Source: TPersistent); override;
function CreateValueList: TcxDataFilterValueList; virtual;
function FindItemByItemLink(AItemLink: TObject): TcxFilterCriteriaItem; override;
function IsFiltering: Boolean; virtual;
procedure RemoveItemByField(AField: TcxCustomDataField);
property DataController: TcxCustomDataController read FDataController;
published
property Active: Boolean read FActive write SetActive default False;
property OnGetValueList: TcxDataFilterGetValueListEvent read FOnGetValueList write FOnGetValueList;
end;
TcxDataFilterCriteriaClass = class of TcxDataFilterCriteria;
{ Filters }
TcxDataFilterList = class(TList)
private
function GetItem(Index: Integer): TcxDataFilterCriteria;
protected
procedure RemoveItemByField(AField: TcxCustomDataField);
public
procedure Clear; override;
property Items[Index: Integer]: TcxDataFilterCriteria read GetItem; default;
end;
{ Groups }
TcxDataGroupIndex = Integer;
TcxDataControllerGroups = class
private
FDataController: TcxCustomDataController;
function GetChildCount(DataGroupIndex: TcxDataGroupIndex): Integer;
function GetChildDataGroupIndex(ParentDataGroupIndex: TcxDataGroupIndex; ChildIndex: Integer): TcxDataGroupIndex;
function GetChildRecordIndex(ParentDataGroupIndex: TcxDataGroupIndex; ChildIndex: Integer): Integer;
function GetDataControllerInfo: TcxCustomDataControllerInfo;
function GetDataGroupIndexByRowIndex(RowIndex: Integer): TcxDataGroupIndex;
function GetDataGroups: TcxDataGroups;
function GetFieldGroupIndex(AField: TcxCustomDataField): Integer;
function GetGroupingItemCount: Integer;
function GetGroupingItemIndex(Index: Integer): Integer;
function GetGroupDisplayText(ADataGroupIndex: TcxDataGroupIndex): string;
function GetGroupValue(ADataGroupIndex: TcxDataGroupIndex): Variant;
function GetItemGroupIndex(AItemIndex: Integer): Integer;
function GetLevel(ADataGroupIndex: TcxDataGroupIndex): Integer;
function GetParentDataGroupIndex(ChildDataGroupIndex: TcxDataGroupIndex): TcxDataGroupIndex;
protected
function GetGroupRecordIndex(ADataGroupIndex: TcxDataGroupIndex): Integer;
property DataControllerInfo: TcxCustomDataControllerInfo read GetDataControllerInfo;
property DataGroups: TcxDataGroups read GetDataGroups;
property LevelCount: Integer read GetGroupingItemCount;
public
constructor Create(ADataController: TcxCustomDataController);
procedure ChangeExpanding(ARowIndex: Integer; AExpanded, ARecursive: Boolean);
procedure ChangeGrouping(AItemIndex, AGroupIndex: Integer);
procedure ClearGrouping;
procedure FullCollapse;
procedure FullExpand;
function GetDataGroupIndexByGroupValue(AParentDataGroupIndex: TcxDataGroupIndex;
const AGroupValue: Variant): TcxDataGroupIndex;
function HasAsParent(ARowIndex, AParentRowIndex: Integer): Boolean;
procedure LoadRecordIndexes(AList: TList; ADataGroupIndex: TcxDataGroupIndex);
procedure LoadRecordIndexesByRowIndex(AList: TList; ARowIndex: Integer);
property DataController: TcxCustomDataController read FDataController;
// Grouping
property FieldGroupIndex[AField: TcxCustomDataField]: Integer read GetFieldGroupIndex;
property GroupingItemCount: Integer read GetGroupingItemCount;
property GroupingItemIndex[Index: Integer]: Integer read GetGroupingItemIndex;
property ItemGroupIndex[ItemIndex: Integer]: Integer read GetItemGroupIndex;
// Navigation
property DataGroupIndexByRowIndex[RowIndex: Integer]: TcxDataGroupIndex read GetDataGroupIndexByRowIndex;
property Level[ADataGroupIndex: TcxDataGroupIndex]: Integer read GetLevel;
property ChildCount[DataGroupIndex: TcxDataGroupIndex]: Integer read GetChildCount; // for Level = 0 DataGroupIndex = -1
property ChildDataGroupIndex[ParentDataGroupIndex: TcxDataGroupIndex; ChildIndex: Integer]: TcxDataGroupIndex read GetChildDataGroupIndex;
property ChildRecordIndex[ParentDataGroupIndex: TcxDataGroupIndex; ChildIndex: Integer]: Integer read GetChildRecordIndex;
property ParentDataGroupIndex[ChildDataGroupIndex: TcxDataGroupIndex]: TcxDataGroupIndex read GetParentDataGroupIndex;
// Values
property GroupDisplayTexts[ADataGroupIndex: TcxDataGroupIndex]: string read GetGroupDisplayText;
property GroupValues[ADataGroupIndex: TcxDataGroupIndex]: Variant read GetGroupValue;
end;
{ Summary }
TcxSummaryKind = (skNone, skSum, skMin, skMax, skCount, skAverage);
TcxSummaryKinds = set of TcxSummaryKind;
TcxSummaryValueType = (svtFloat, svtCurrency, svtDate);
TcxCustomDataSummaryItem = class(TCollectionItem)
private
FField: TcxCustomDataField;
FFormat: string;
FKind: TcxSummaryKind;
function GetItemLink: TObject;
procedure SetFormat(const Value: string);
procedure SetItemLink(Value: TObject);
procedure SetKind(Value: TcxSummaryKind);
protected
procedure AssignValues(ASource: TcxCustomDataSummaryItem); virtual;
function GetDataController: TcxCustomDataController; virtual; abstract;
function GetValueFormat(AValueType: TcxSummaryValueType; const AValue: Variant;
AIsFooter: Boolean): string; virtual;
function GetValueType(AVarType: TVarType): TcxSummaryValueType;
function IsCurrency(AVarType: TVarType): Boolean; virtual;
procedure ItemLinkChanging(AField: TcxCustomDataField); virtual;
property Format: string read FFormat write SetFormat;
property Kind: TcxSummaryKind read FKind write SetKind default skNone;
public
procedure Assign(Source: TPersistent); override;
procedure BeginUpdate;
procedure EndUpdate;
function FormatValue(const AValue: Variant; AIsFooter: Boolean): string; virtual;
property DataController: TcxCustomDataController read GetDataController;
property ItemLink: TObject read GetItemLink write SetItemLink;
property Field: TcxCustomDataField read FField;
end;
TcxSummaryOption = (soNullIgnore, soSelectedRecords, soMultipleSelectedRecords);
TcxSummaryOptions = set of TcxSummaryOption;
TcxSummaryPosition = (spGroup, spFooter);
TcxSummaryProperty = (spFormat, spKind, spSorted);
TcxSummaryProperties = set of TcxSummaryProperty;
TcxDataSummaryItemGetTextEvent = procedure(Sender: TcxDataSummaryItem;
const AValue: Variant; AIsFooter: Boolean; var AText: string) of object;
TcxDataSummaryItem = class(TcxCustomDataSummaryItem)
private
FSorted: Boolean;
FTag: Longint;
FOnGetText: TcxDataSummaryItemGetTextEvent;
function GetPosition: TcxSummaryPosition;
function GetSummaryItems: TcxDataSummaryItems;
procedure SetPosition(Value: TcxSummaryPosition);
procedure SetSorted(Value: Boolean);
protected
FPosition: TcxSummaryPosition;
procedure AssignValues(Source: TcxCustomDataSummaryItem); override;
function GetDataController: TcxCustomDataController; override;
function GetValueFormat(AValueType: TcxSummaryValueType; const AValue: Variant;
AIsFooter: Boolean): string; override;
function IsDataBinded: Boolean; virtual;
function IsPositionStored: Boolean; virtual;
public
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
function DataField: TcxCustomDataField; virtual;
function FormatValue(const AValue: Variant; AIsFooter: Boolean): string; override;
property DataController: TcxCustomDataController read GetDataController;
property SummaryItems: TcxDataSummaryItems read GetSummaryItems;
property Sorted: Boolean read FSorted write SetSorted default False;
published
property Format;
property Kind;
property Position: TcxSummaryPosition read GetPosition write SetPosition stored IsPositionStored;
property Tag: Longint read FTag write FTag default 0;
property OnGetText: TcxDataSummaryItemGetTextEvent read FOnGetText write FOnGetText;
end;
TcxDataSummaryItemClass = class of TcxDataSummaryItem;
TcxSummaryEventArguments = record
RecordIndex: Integer;
SummaryItem: TcxDataSummaryItem;
end;
TcxSummaryEventOutArguments = record
Value: Variant;
SummaryValue: Variant;
CountValue: Integer;
Done: Boolean;
end;
TcxSummaryEvent = procedure(ASender: TcxDataSummaryItems;
Arguments: TcxSummaryEventArguments;
var OutArguments: TcxSummaryEventOutArguments) of object;
TcxDataSummaryItems = class(TcxCollection)
private
FSummary: TcxDataSummary;
FOnSummary: TcxSummaryEvent;
function GetDataController: TcxCustomDataController;
function GetItem(Index: Integer): TcxDataSummaryItem;
procedure SetItem(Index: Integer; Value: TcxDataSummaryItem);
protected
procedure BeforeSortingChange(AItem: TcxDataSummaryItem; AValue: Boolean); virtual;
procedure ChangedView;
function GetItemPosition(AItem: TcxDataSummaryItem): TcxSummaryPosition; virtual;
function GetOwner: TPersistent; override;
function IndexOfField(AField: TcxCustomDataField): Integer;
function IsPositionStored(AItem: TcxDataSummaryItem): Boolean; virtual;
function ItemOfField(AField: TcxCustomDataField): TcxDataSummaryItem;
procedure Update(Item: TCollectionItem); override;
// simple summary routines
function AddDataItem(AItemIndex: Integer; APosition: TcxSummaryPosition): TcxDataSummaryItem;
procedure CheckItemEmpty(AItem: TcxDataSummaryItem);
procedure GetDataItemProperties(AItemIndex: Integer; APosition: TcxSummaryPosition;
AProperties: TcxSummaryProperties; var AFormat: string; var AKind: TcxSummaryKind;
var ASorted: Boolean);
function IsPropertiesEmpty(AProperties: TcxSummaryProperties; const AFormat: string;
AKind: TcxSummaryKind; ASorted: Boolean): Boolean; virtual;
procedure SetDataItemProperties(AItemIndex: Integer; APosition: TcxSummaryPosition;
AProperties: TcxSummaryProperties; const AFormat: string; AKind: TcxSummaryKind;
ASorted: Boolean);
procedure SetItemProperties(AItem: TcxDataSummaryItem; APosition: TcxSummaryPosition;
AProperties: TcxSummaryProperties; const AFormat: string; AKind: TcxSummaryKind;
ASorted: Boolean);
public
constructor Create(ASummary: TcxDataSummary; AItemClass: TcxDataSummaryItemClass); virtual;
function Add: TcxDataSummaryItem; overload;
function Add(AItemLink: TObject; APosition: TcxSummaryPosition;
AKind: TcxSummaryKind; const AFormat: string = ''): TcxDataSummaryItem; overload;
procedure Assign(Source: TPersistent); override;
procedure AssignEvents(Source: TPersistent); virtual;
function DefaultFormat(AValueType: TcxSummaryValueType; ASummaryKind: TcxSummaryKind; AIsFooter: Boolean): string; virtual;
procedure DeleteItems(AItemLink: TObject; APosition: TcxSummaryPosition);
function FindByTag(ATag: Longint): TcxDataSummaryItem;
function GetGroupText(const ASummaryValues: Variant): string; virtual;
function IndexOf(AItem: TcxDataSummaryItem): Integer;
function IndexOfItemLink(AItemLink: TObject): Integer;
function ItemOfItemLink(AItemLink: TObject): TcxDataSummaryItem;
// simple summary
function GetDataItem(AItemIndex: Integer; APosition: TcxSummaryPosition;
ACheckKind: Boolean = False; AKind: TcxSummaryKind = skNone): TcxDataSummaryItem; virtual;
function GetDataItemFormat(AItemIndex: Integer; APosition: TcxSummaryPosition): string;
function GetDataItemKind(AItemIndex: Integer; APosition: TcxSummaryPosition): TcxSummaryKind;
function GetDataItemSorted(AItemIndex: Integer; APosition: TcxSummaryPosition): Boolean;
procedure SetDataItemFormat(AItemIndex: Integer; APosition: TcxSummaryPosition; const Value: string);
procedure SetDataItemKind(AItemIndex: Integer; APosition: TcxSummaryPosition; Value: TcxSummaryKind);
procedure SetDataItemSorted(AItemIndex: Integer; APosition: TcxSummaryPosition; Value: Boolean);
property DataController: TcxCustomDataController read GetDataController;
property Items[Index: Integer]: TcxDataSummaryItem read GetItem write SetItem; default;
property Summary: TcxDataSummary read FSummary;
published
property OnSummary: TcxSummaryEvent read FOnSummary write FOnSummary;
end;
TcxDataFooterSummaryItems = class(TcxDataSummaryItems)
protected
function GetItemPosition(AItem: TcxDataSummaryItem): TcxSummaryPosition; override;
function IsPositionStored(AItem: TcxDataSummaryItem): Boolean; override;
end;
TcxDataGroupSummaryItems = class(TcxDataSummaryItems)
private
FBeginText: string;
FEndText: string;
FSeparator: string;
FOwner: TPersistent;
function IsBeginTextStored: Boolean;
function IsEndTextStored: Boolean;
function IsSeparatorStored: Boolean;
procedure SetBeginText(const Value: string);
procedure SetEndText(const Value: string);
procedure SetSeparator(const Value: string);
protected
procedure BeforeSortingChange(AItem: TcxDataSummaryItem; AValue: Boolean); override;
function GetOwner: TPersistent; override;
public
constructor Create(ASummary: TcxDataSummary; AItemClass: TcxDataSummaryItemClass); override;
procedure Assign(Source: TPersistent); override;
function GetGroupText(const ASummaryValues: Variant): string; override;
function SortedSummaryItem: TcxDataSummaryItem;
published
property BeginText: string read FBeginText write SetBeginText stored IsBeginTextStored;
property EndText: string read FEndText write SetEndText stored IsEndTextStored;
property Separator: string read FSeparator write SetSeparator stored IsSeparatorStored;
end;
TcxDataSummaryGroupItemLink = class(TcxCustomDataSummaryItem)
private
function GetSummaryGroupItemLinks: TcxDataSummaryGroupItemLinks;
protected
function GetDataController: TcxCustomDataController; override;
procedure ItemLinkChanging(AField: TcxCustomDataField); override;
public
property SummaryGroupItemLinks: TcxDataSummaryGroupItemLinks read GetSummaryGroupItemLinks;
end;
TcxDataSummaryGroupItemLinkClass = class of TcxDataSummaryGroupItemLink;
TcxDataSummaryGroupItemLinks = class(TcxCollection)
private
FSummaryGroup: TcxDataSummaryGroup;
function GetItem(Index: Integer): TcxDataSummaryGroupItemLink;
procedure SetItem(Index: Integer; Value: TcxDataSummaryGroupItemLink);
protected
function GetOwner: TPersistent; override;
function IndexOfField(AField: TcxCustomDataField): Integer;
function ItemOfField(AField: TcxCustomDataField): TcxDataSummaryGroupItemLink;
procedure Update(Item: TCollectionItem); override;
public
constructor Create(ASummaryGroup: TcxDataSummaryGroup; AItemClass: TcxDataSummaryGroupItemLinkClass); virtual;
function Add: TcxDataSummaryGroupItemLink;
function IndexOfItemLink(AItemLink: TObject): Integer;
function ItemOfItemLink(AItemLink: TObject): TcxDataSummaryGroupItemLink;
property Items[Index: Integer]: TcxDataSummaryGroupItemLink read GetItem write SetItem; default;
property SummaryGroup: TcxDataSummaryGroup read FSummaryGroup;
end;
TcxDataSummaryGroup = class(TCollectionItem)
private
FItemLinks: TcxDataSummaryGroupItemLinks;
FSummaryItems: TcxDataGroupSummaryItems;
function GetItemLinks: TcxDataSummaryGroupItemLinks;
function GetSummaryGroups: TcxDataSummaryGroups;
procedure SetItemLinks(Value: TcxDataSummaryGroupItemLinks);
procedure SetSummaryItems(Value: TcxDataGroupSummaryItems);
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
property SummaryGroups: TcxDataSummaryGroups read GetSummaryGroups;
published
property Links: TcxDataSummaryGroupItemLinks read GetItemLinks write SetItemLinks;
property SummaryItems: TcxDataGroupSummaryItems read FSummaryItems write SetSummaryItems;
end;
TcxDataSummaryGroups = class(TcxCollection)
private
FSummary: TcxDataSummary;
function GetItem(Index: Integer): TcxDataSummaryGroup;
procedure SetItem(Index: Integer; Value: TcxDataSummaryGroup);
protected
function GetOwner: TPersistent; override;
procedure Update(Item: TCollectionItem); override;
public
constructor Create(ASummary: TcxDataSummary);
function Add: TcxDataSummaryGroup;
function FindByItemLink(AItemLink: TObject): TcxDataSummaryGroup;
property Summary: TcxDataSummary read FSummary;
property Items[Index: Integer]: TcxDataSummaryGroup read GetItem write SetItem; default;
end;
TcxAfterSummaryEvent = procedure(ASender: TcxDataSummary) of object;
TcxDataSummary = class(TPersistent)
private
FDataController: TcxCustomDataController;
FDefaultGroupSummaryItems: TcxDataGroupSummaryItems;
FDestroying: Boolean;
FFooterSummaryItems: TcxDataFooterSummaryItems;
FFooterSummaryValues: Variant; // array of Variant
FInAfterSummary: Boolean;
FLockCount: Integer;
FOptions: TcxSummaryOptions;
FSetCustomSummary: Boolean;
FSummaryGroups: TcxDataSummaryGroups;
FOnAfterSummary: TcxAfterSummaryEvent;
function GetFooterSummaryValue(Index: Integer): Variant;
function GetFooterSummaryText(Index: Integer): string;
function GetGroupSummaryItems(Level: Integer): TcxDataGroupSummaryItems;
function GetGroupSummaryText(RowIndex: Integer): string;
function GetGroupSummaryDisplayValue(RowIndex, Level, Index: Integer): Variant;
function GetGroupSummaryValue(DataGroupIndex: TcxDataGroupIndex; Index: Integer): Variant;
function GetGroupFooterIndexOfItemLink(Level: Integer; ItemLink: TObject): Integer;
function GetGroupFooterSummaryText(RowIndex, Level, Index: Integer): string;
function GetOptions: TcxSummaryOptions;
procedure SetDefaultGroupSummaryItems(Value: TcxDataGroupSummaryItems);
procedure SetFooterSummaryItems(Value: TcxDataFooterSummaryItems);
procedure SetFooterSummaryValue(Index: Integer; Value: Variant);
procedure SetGroupSummaryDisplayValue(RowIndex, Level, Index: Integer; const Value: Variant);
procedure SetGroupSummaryValue(DataGroupIndex: TcxDataGroupIndex; Index: Integer; const Value: Variant);
procedure SetOptions(Value: TcxSummaryOptions);
procedure SetSummaryGroups(Value: TcxDataSummaryGroups);
protected
procedure BeginCalculateSummary(ASummaryItems: TcxDataSummaryItems;
var ACountValues: TcxDataSummaryCountValues; var ASummaryValues: TcxDataSummaryValues); virtual;
procedure CalculateSummary(ASummaryItems: TcxDataSummaryItems;
ABeginIndex, AEndIndex: Integer; var ACountValues: TcxDataSummaryCountValues;
var ASummaryValues: TcxDataSummaryValues; var SummaryValues: Variant); virtual;
procedure Changed(ARedrawOnly: Boolean); virtual;
procedure DoAfterSummary; virtual;
procedure DoFinishSummaryValue(ASummaryItem: TcxDataSummaryItem;
var SummaryValue: Variant; var CountValue: Integer); virtual;
procedure DoSummaryValue(ASummaryItem: TcxDataSummaryItem; ARecordIndex: Integer;
var SummaryValue: Variant; var CountValue: Integer); virtual;
procedure EndCalculateSummary(ASummaryItems: TcxDataSummaryItems;
var ACountValues: TcxDataSummaryCountValues; var ASummaryValues: TcxDataSummaryValues;
var SummaryValues: Variant); virtual;
function GetGroupSummaryValues(ADataGroupIndex: TcxDataGroupIndex): PVariant;
function GetOwner: TPersistent; override;
function GetRecordIndex(ARecordListIndex: Integer): Integer; virtual;
function GetSummaryGroupItemLinkClass: TcxDataSummaryGroupItemLinkClass; virtual;
function GetSummaryItemClass: TcxDataSummaryItemClass; virtual;
function IsValidSummaryValuesIndex(const ASummaryValues: Variant; AIndex: Integer): Boolean; virtual;
procedure Update(ARedrawOnly: Boolean); virtual;
property Destroying: Boolean read FDestroying;
property LockCount: Integer read FLockCount;
public
constructor Create(ADataController: TcxCustomDataController); virtual;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure AssignEvents(Source: TPersistent); virtual;
procedure BeginUpdate;
procedure BeginCalculate; virtual;
procedure EndCalculate; virtual;
procedure Calculate; virtual;
procedure CalculateFooterSummary; virtual;
procedure CalculateGroupSummary; virtual;
procedure CancelUpdate;
procedure EndUpdate;
function GetGroupSummaryInfo(ARowIndex: Integer; var ASummaryItems: TcxDataSummaryItems;
var ASummaryValues: PVariant): Boolean;
function GetPatternSummaryItems(APatternSummary: TcxDataSummary; ASummaryItems: TcxDataSummaryItems): TcxDataSummaryItems;
procedure Recalculate;
procedure RemoveItemByField(AField: TcxCustomDataField);
property DataController: TcxCustomDataController read FDataController;
property FooterSummaryValues[Index: Integer]: Variant read GetFooterSummaryValue write SetFooterSummaryValue;
property FooterSummaryTexts[Index: Integer]: string read GetFooterSummaryText;
property GroupFooterIndexOfItemLink[Level: Integer; ItemLink: TObject]: Integer read GetGroupFooterIndexOfItemLink;
property GroupSummaryItems[Level: Integer]: TcxDataGroupSummaryItems read GetGroupSummaryItems;
// only for View
property GroupFooterSummaryTexts[RowIndex, Level, Index: Integer]: string read GetGroupFooterSummaryText;
property GroupSummaryText[RowIndex: Integer]: string read GetGroupSummaryText;
property GroupSummaryDisplayValues[RowIndex, Level, Index: Integer]: Variant read GetGroupSummaryDisplayValue write SetGroupSummaryDisplayValue;
// summary group values
property GroupSummaryValues[DataGroupIndex: TcxDataGroupIndex; Index: Integer]: Variant read GetGroupSummaryValue write SetGroupSummaryValue;
published
property DefaultGroupSummaryItems: TcxDataGroupSummaryItems read FDefaultGroupSummaryItems write SetDefaultGroupSummaryItems;
property FooterSummaryItems: TcxDataFooterSummaryItems read FFooterSummaryItems write SetFooterSummaryItems;
property SummaryGroups: TcxDataSummaryGroups read FSummaryGroups write SetSummaryGroups;
property Options: TcxSummaryOptions read GetOptions write SetOptions default [];
property OnAfterSummary: TcxAfterSummaryEvent read FOnAfterSummary write FOnAfterSummary;
end;
TcxDataSummaryClass = class of TcxDataSummary;
{ Incremental Search }
TcxDataControllerSearch = class
private
FDataController: TcxCustomDataController;
FLocked: Boolean;
function GetItemIndex: Integer;
function GetSearching: Boolean;
function GetSearchText: string;
procedure SetItemIndex(const Value: Integer);
protected
procedure DoFocusedRecord(AFilteredRecordIndex: Integer); virtual;
function DoSearch(AStartFilteredRecordIndex, AEndFilteredRecordIndex: Integer;
const ASubText: string; AForward: Boolean): Integer; virtual;
public
constructor Create(ADataController: TcxCustomDataController); virtual;
procedure Cancel;
function Locate(AItemIndex: Integer; const ASubText: string): Boolean; virtual;
function LocateNext(AForward: Boolean): Boolean; virtual;
procedure Lock; virtual;
procedure Unlock; virtual;
property DataController: TcxCustomDataController read FDataController;
property ItemIndex: Integer read GetItemIndex write SetItemIndex;
property Locked: Boolean read FLocked;
property Searching: Boolean read GetSearching;
property SearchText: string read GetSearchText;
end;
TcxDataControllerSearchClass = class of TcxDataControllerSearch;
{ TcxCustomDataController }
// Changing Notification
TcxDataControllerChange = (dccLayout, dccData, dccFocus, dccSelection,
dccDetail, dccSummary, dccSearch, dccSorting, dccGrouping, dccSyncMasterPos,
dccBookmark, dccUpdateRecord);
TcxDataControllerChanges = set of TcxDataControllerChange;
TcxUpdateControlInfo = class
end;
TcxUpdateControlInfoClass = class of TcxUpdateControlInfo;
TcxFocusedRecordChangedInfo = class(TcxUpdateControlInfo)
private
FFocusedRecordIndex: Integer;
FPrevFocusedRecordIndex: Integer;
FFocusedRowIndex: Integer;
FPrevFocusedRowIndex: Integer;
FNewItemRowFocusingChanged: Boolean;
public
constructor Create(APrevFocusedRecordIndex, AFocusedRecordIndex, APrevFocusedRowIndex, AFocusedRowIndex: Integer;
ANewItemRowFocusingChanged: Boolean);
property FocusedRecordIndex: Integer read FFocusedRecordIndex;
property PrevFocusedRecordIndex: Integer read FPrevFocusedRecordIndex;
property FocusedRowIndex: Integer read FFocusedRowIndex;
property PrevFocusedRowIndex: Integer read FPrevFocusedRowIndex;
property NewItemRowFocusingChanged: Boolean read FNewItemRowFocusingChanged;
end;
TcxFocusedRowChangedInfo = class(TcxUpdateControlInfo)
private
FFocusedRowIndex: Integer;
FPrevFocusedRowIndex: Integer;
public
constructor Create(APrevFocusedRowIndex, AFocusedRowIndex: Integer);
property FocusedRowIndex: Integer read FFocusedRowIndex;
property PrevFocusedRowIndex: Integer read FPrevFocusedRowIndex;
end;
TcxGroupingChangingInfo = class(TcxUpdateControlInfo);
TcxLayoutChangedInfo = class(TcxUpdateControlInfo)
{ RowCount has not been changed }
end;
TcxUpdateRecordInfo = class(TcxUpdateControlInfo) { Record is changed }
private
FRecordIndex: Integer;
public
constructor Create(ARecordIndex: Integer);
property RecordIndex: Integer read FRecordIndex;
end;
TcxDataChangedInfo = class(TcxUpdateControlInfo)
{ RowCount has been changed; Grouping/Expanding changed }
Kind: TcxDataChange;
ItemIndex: Integer; // Kind = dcField
RecordIndex: Integer; // Kind = dcRecord
end;
TcxBookmarkChangedInfo = class(TcxUpdateControlInfo)
end;
TcxSelectionChangedInfo = class(TcxUpdateControlInfo)
private
FRowIndexes: TList;
function GetCount: Integer;
function GetRowIndex(Index: Integer): Integer;
public
constructor Create;
constructor CreateEx(ARowIndex1, ARowIndex2: Integer);
destructor Destroy; override;
property Count: Integer read GetCount;
property RowIndexes[Index: Integer]: Integer read GetRowIndex;
end;
TcxSearchChangedInfo = class(TcxUpdateControlInfo)
end;
TcxDataSelectionChangedInfo = record
SelectedCount: Integer;
RowIndex: Integer;
end;
TcxUpdateControlEvent = procedure(AInfo: TcxUpdateControlInfo) of object;
TcxDataControllerLocateObject = class
private
FDataController: TcxCustomDataController;
FInternalRecordIndex: Integer;
protected
property DataController: TcxCustomDataController read FDataController;
property InternalRecordIndex: Integer read FInternalRecordIndex;
public
constructor Create(ADataController: TcxCustomDataController); virtual;
destructor Destroy; override;
procedure Clear; virtual;
function FindRecordIndex: Integer; virtual; abstract;
procedure ReadData(AValueDefReader: TcxValueDefReader);
end;
TcxDataControllerKeyLocateObject = class(TcxDataControllerLocateObject)
private
FFields: TList;
public
constructor Create(ADataController: TcxCustomDataController); override;
destructor Destroy; override;
procedure AddField(AField: TcxCustomDataField);
procedure ClearFields;
function FindRecordIndex: Integer; override;
property FieldList: TList read FFields;
end;
TcxDataControllerGroupLocateObject = class(TcxDataControllerLocateObject)
private
FIsGroupDataSorted: Boolean;
FLevel: Integer;
public
constructor Create(ADataController: TcxCustomDataController); override;
function FindRecordIndex: Integer; override;
property Level: Integer read FLevel write FLevel;
end;
TcxValueDefUnboundReader = class(TcxValueDefReader)
private
FItems: TList;
FValues: TList;
public
constructor Create; override;
destructor Destroy; override;
procedure Clear;
function GetValue(AValueDef: TcxValueDef): Variant; override;
function IsInternal(AValueDef: TcxValueDef): Boolean; override;
procedure SetValue(AValueDef: TcxValueDef; const AValue: Variant);
procedure Truncate(AItemCount: Integer);
end;
TcxValueDefRecordReader = class(TcxValueDefReader)
private
FDataController: TcxCustomDataController;
FRecordIndex: Integer;
public
constructor Create(ADataController: TcxCustomDataController; ARecordIndex: Integer); reintroduce;
function GetDisplayText(AValueDef: TcxValueDef): string; override;
function GetValue(AValueDef: TcxValueDef): Variant; override;
function IsInternal(AValueDef: TcxValueDef): Boolean; override;
end;
TcxDataControllerComparisonMode = (dccmSorting, dccmGrouping, dccmOther);
// Detail Mode
TcxDataControllerDetailMode = (dcdmNone, dcdmPattern, dcdmClone);
TcxDataControllerEachDetailProc = procedure(ADataController: TcxCustomDataController) of object;
TcxDataControllerEachRowProc = procedure(ARowIndex: Integer; ARowInfo: TcxRowInfo) of object;
TcxDataControllerNotifier = class(TComponent) // because TcxCustomDataController is TPersistent
private
FDataController: TcxCustomDataController;
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
constructor Create(AOwner: TComponent; ADataController: TcxCustomDataController); reintroduce;
property DataController: TcxCustomDataController read FDataController;
end;
TcxSortingBySummaryEngineClass = class of TcxSortingBySummaryEngine;
TcxSortingBySummaryEngine = class
private
FDataControllerInfo: TcxCustomDataControllerInfo;
function GetDataController: TcxCustomDataController;
protected
property DataController: TcxCustomDataController read GetDataController;
public
constructor Create(ADataControllerInfo: TcxCustomDataControllerInfo); virtual;
procedure Sort; virtual; abstract;
property DataControllerInfo: TcxCustomDataControllerInfo read FDataControllerInfo;
end;
TcxDataControllerCompareEvent = procedure(ADataController: TcxCustomDataController;
ARecordIndex1, ARecordIndex2, AItemIndex: Integer; const V1, V2: Variant;
var Compare: Integer) of object;
TcxDataRecordChangedEvent = procedure(ADataController: TcxCustomDataController;
ARecordIndex, AItemIndex: Integer) of object;
TcxDataControllerNotifyEvent = procedure(ADataController: TcxCustomDataController) of object;
TcxDataDetailExpandingEvent = procedure(ADataController: TcxCustomDataController;
ARecordIndex: Integer; var AAllow: Boolean) of object;
TcxDataDetailExpandedEvent = procedure(ADataController: TcxCustomDataController;
ARecordIndex: Integer) of object;
TcxDataRecordNotifyEvent = TcxDataDetailExpandedEvent;
TcxDataControllerOption = (dcoAnsiSort, dcoCaseInsensitive, dcoAssignGroupingValues,
dcoAssignMasterDetailKeys, dcoSaveExpanding, dcoSortByDisplayText,
dcoFocusTopRowAfterSorting, dcoGroupsAlwaysExpanded, dcoImmediatePost,
dcoInsertOnNewItemRowFocusing);
TcxDataControllerOptions = set of TcxDataControllerOption;
TcxDataControllerEditKind = (dceInsert, dceEdit, dceChanging, dceModified);
TcxDataControllerEditState = set of TcxDataControllerEditKind;
TcxDataControllerEditOperation = (dceoAppend, dceoDelete, dceoEdit, dceoInsert,
dceoShowEdit);
TcxDataControllerEditOperations = set of TcxDataControllerEditOperation;
TcxDataListenerLink = class
Ref: TcxCustomDataController;
end;
TcxDataFilterRecordEvent = procedure(ADataController: TcxCustomDataController;
ARecordIndex: Integer; var Accept: Boolean) of object;
TcxCustomDataController = class(TPersistent, IUnknown)
private
FActive: Boolean;
FAfterSummaryFlag: Boolean;
FBookmarkRecordIndex: Integer;
FDataChangedFlag: Boolean;
FDataChangedLockCount: Integer;
// FDataChangeInfo: TcxDataChangeInfo;
FChanges: TcxDataControllerChanges;
FCheckFocusingAfterFilterNeeded: Boolean;
FCreatingDataController: TcxCustomDataController;
FCreatingLinkObject: Boolean;
FDataChangedListeners: TList;
FDataChangeRefCount: Integer;
FDataControllerInfo: TcxCustomDataControllerInfo;
FDataStorage: TcxDataStorage;
FDestroying: Boolean;
FGroupingChanged: Boolean;
FLoadedStorage: Boolean;
FGroups: TcxDataControllerGroups;
FFields: TcxCustomDataFieldList;
FFilter: TcxDataFilterCriteria;
FFilterChanged: Boolean;
FFilters: TcxDataFilterList;
FFocusedSelected: Boolean;
FIncrementalFilterField: TcxCustomDataField;
FIncrementalFilterText: string;
FIncrementalSearchField: TcxCustomDataField;
FIncrementalSearching: Boolean;
FIncrementalSearchText: string;
FInCancel: Boolean;
FInLoadStorage: Boolean;
FInNotifyControl: Boolean;
FInOnSortingChanged: Boolean;
FInternalFindRecord: Boolean;
FIsDetailExpanding: Boolean;
FIsPattern: Boolean;
FIsPatternSave: Boolean;
FInSetCustomDataSource: Boolean;
FInSmartLoad: Boolean;
FListenerLinks: TList;
FLockGridModeNotifyCount: Integer;
FLockUpdateFieldsCount: Integer;
FUpdateFieldsFlag: Boolean;
FNewItemRecordIndex: Integer;
FNewItemRowFocused: Boolean;
FMasterRecordIndex: Integer;
FMasterRelation: TcxCustomDataRelation;
FMultiSelect: Boolean;
FNotifier: TcxDataControllerNotifier;
FOptions: TcxDataControllerOptions;
FOwner: TComponent;
FPrevSelectionChangedInfo: TcxDataSelectionChangedInfo;
FProvider: TcxCustomDataProvider;
FRecordHandlesField: TcxCustomDataField;
FRecreatingLinkObject: Boolean;
FRelations: TcxCustomDataRelationList;
FSaveObject: TObject;
FSaveObjectLockCount: Integer;
FSearch: TcxDataControllerSearch;
FSelectionChangedInfo: TcxDataSelectionChangedInfo;
FSortingBySummaryChanged: Boolean;
FSortingBySummaryDataItemField: TcxCustomDataField;
FSortingChanged: Boolean;
FStructureChanged: Boolean;
FStructureRecreated: Boolean;
FSummary: TcxDataSummary;
FUpdateItems: Boolean;
FUseNewItemRowForEditing: Boolean;
FOnAfterCancel: TcxDataControllerNotifyEvent;
FOnAfterDelete: TcxDataControllerNotifyEvent;
FOnAfterInsert: TcxDataControllerNotifyEvent;
FOnAfterPost: TcxDataControllerNotifyEvent;
FOnBeforeCancel: TcxDataControllerNotifyEvent;
FOnBeforeDelete: TcxDataRecordNotifyEvent;
FOnBeforeInsert: TcxDataControllerNotifyEvent;
FOnBeforePost: TcxDataControllerNotifyEvent;
FOnFilterRecord: TcxDataFilterRecordEvent;
FOnNewRecord: TcxDataRecordNotifyEvent;
FOnCompare: TcxDataControllerCompareEvent;
FOnDataChanged: TNotifyEvent;
FOnDetailCollapsing: TcxDataDetailExpandingEvent;
FOnDetailCollapsed: TcxDataDetailExpandedEvent;
FOnDetailExpanding: TcxDataDetailExpandingEvent;
FOnDetailExpanded: TcxDataDetailExpandedEvent;
FOnGroupingChanged: TNotifyEvent;
FOnRecordChanged: TcxDataRecordChangedEvent;
FOnSortingChanged: TNotifyEvent;
FOnUpdateControl: TcxUpdateControlEvent;
function GetCustomDataSource: TcxCustomDataSource;
function GetDetailMode: TcxDataControllerDetailMode;
function GetFilteredIndexByRecordIndex(Index: Integer): Integer;
function GetFilteredRecordCount: Integer;
function GetFilteredRecordIndex(Index: Integer): Integer;
function GetIsEditing: Boolean;
function GetIsPattern: Boolean;
function GetIsRowInfoValid: Boolean;
function GetLockCount: Integer;
function GetNewItemRowFocused: Boolean;
function GetOptions: TcxDataControllerOptions;
function GetRelations: TcxCustomDataRelationList;
function GetSortingBySummaryDataItemIndex: Integer;
procedure SetCustomDataSource(Value: TcxCustomDataSource);
procedure SetIsPattern(Value: Boolean);
procedure SetFilter(Value: TcxDataFilterCriteria);
procedure SetFocusedRowIndex(Value: Integer);
procedure SetMultiSelect(Value: Boolean);
procedure SetNewItemRowFocused(Value: Boolean);
procedure SetOnFilterRecord(Value: TcxDataFilterRecordEvent);
procedure SetOptions(Value: TcxDataControllerOptions);
procedure SetSummary(Value: TcxDataSummary);
procedure SetUseNewItemRowForEditing(Value: Boolean);
procedure SetSortingBySummaryDataItemIndex(Value: Integer);
procedure Update;
// Notify
procedure BeforeGroupingNotification;
procedure BookmarkNotification;
procedure DataNotification;
procedure DoGroupingChanged;
procedure DoSortingBySummaryChanged;
procedure DoSortingChanged;
procedure FocusedNotification;
procedure LayoutNotification;
procedure UpdateRecordNotification;
procedure SearchNotification;
procedure SelectionNotification;
procedure NotifyControl(AUpdateControlInfo: TcxUpdateControlInfo);
procedure NotifyListenerLinks;
procedure ResetDataChangeInfo;
protected
FDataChangeInfo: TcxDataChangeInfo;
FInDeleteSelection: Boolean;
FInFocusDetails: Boolean;
FNearestRecordIndex: Integer;
// IUnknown
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
// Based
function AddInternalField: TcxCustomDataField; virtual;
function AddField: TcxCustomDataField; virtual;
function AppendInSmartLoad: Integer; virtual;
procedure BeforeSorting; virtual;
function CalcEditingRecordIndex: Integer; virtual;
function CanChangeDetailExpanding(ARecordIndex: Integer; AExpanded: Boolean): Boolean; virtual;
function CanFocusRecord(ARecordIndex: Integer): Boolean; virtual;
function CanSelectRow(ARowIndex: Integer): Boolean; virtual;
procedure CheckChanges;
procedure Change(AChanges: TcxDataControllerChanges);
procedure CheckBookmarkValid(ADeletedRecordIndex: Integer); virtual;
procedure CheckDataSetCurrent; virtual;
procedure CheckEditingRecordIndex(ARecordIndex: Integer); virtual;
procedure CheckInternalRecordRange(ARecordIndex: Integer);
procedure CheckItemRange(AItemIndex: Integer);
function CheckDetailsBrowseMode: Boolean; virtual;
function CheckMasterBrowseMode: Boolean; virtual;
procedure CheckMode;
procedure CheckNearestFocusRow;
procedure CheckRange(ARecordIndex, AItemIndex: Integer);
procedure CheckRecordRange(ARecordIndex: Integer);
procedure CheckSelectedCount(ADeletedRecordIndex: Integer); // TODO: rename CheckRecordsAfterDelete
procedure ClearDataChangedListeners;
procedure ClearDetailsMasterRelation(ARelation: TcxCustomDataRelation);
procedure CopyRecord(AFromRecordIndex, AToRecordIndex: Integer); virtual;
procedure CorrectAfterInsert(ARecordIndex: Integer); virtual;
procedure CorrectAfterDelete(ARecordIndex: Integer); virtual;
procedure CorrectPrevSelectionChangedInfo;
procedure DeleteInSmartLoad(ARecordIndex: Integer); virtual;
procedure Unlocked; virtual;
procedure DoAfterCancel; virtual;
procedure DoAfterDelete; virtual;
procedure DoAfterInsert; virtual;
procedure DoAfterPost; virtual;
procedure DoBeforeCancel; virtual;
procedure DoBeforeDelete(ARecordIndex: Integer); virtual;
procedure DoBeforeInsert; virtual;
procedure DoBeforePost; virtual;
function DoFilterRecord(ARecordIndex: Integer): Boolean; virtual;
function IsFilterRecord: Boolean; virtual;
procedure DoNewRecord(ARecordIndex: Integer); virtual;
procedure DoDataChanged; virtual;
procedure DoDetailExpanding(ARecordIndex: Integer; var AAllow: Boolean); virtual;
procedure DoDetailExpanded(ARecordIndex: Integer); virtual;
procedure DoDetailCollapsing(ARecordIndex: Integer; var AAllow: Boolean); virtual;
procedure DoDetailCollapsed(ARecordIndex: Integer); virtual;
function DoIncrementalFilterRecord(ARecordIndex: Integer): Boolean; virtual;
procedure DoReadRecord(ARecordIndex: Integer); virtual;
function DoSearchInGridMode(const ASubText: string; AForward, ANext: Boolean): Boolean; virtual;
function FindItemByInternalID(AID: Integer): TObject; virtual;
function FindItemByName(const AName: string): TObject; virtual;
function FindProperItemLink(AItemLink: TObject): TObject; virtual;
function GetActiveRecordIndex: Integer; virtual; // GridMode
function GetDataProviderClass: TcxCustomDataProviderClass; virtual;
function GetDataSelectionClass: TcxDataSelectionClass; virtual;
function GetEditOperations: TcxDataControllerEditOperations; virtual;
function GetEditState: TcxDataControllerEditState; virtual;
function GetDefaultActiveRelationIndex: Integer; virtual;
function GetFieldClass: TcxCustomDataFieldClass; virtual;
function GetFilterCriteriaClass: TcxDataFilterCriteriaClass; virtual;
function GetInternalDisplayText(ARecordIndex: Integer; AField: TcxCustomDataField): string; virtual;
function GetItemID(AItem: TObject): Integer; virtual;
function GetItemName(AItem: TObject): string; virtual;
function GetInternalRecordId(ARecordIndex: Integer; AFieldList: TList): Variant; virtual;
function GetInternalValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant; virtual;
procedure GetKeyFields(AList: TList); virtual;
function GetLastRecordIndex: Integer;
function GetRecordIndex: Integer; virtual;
function GetRelationClass: TcxCustomDataRelationClass; virtual;
function GetSearchClass: TcxDataControllerSearchClass; virtual;
function GetStoredDisplayText(ARecordIndex: Integer; AField: TcxCustomDataField): string;
function GetStoredValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant;
function GetSummaryClass: TcxDataSummaryClass; virtual;
function GetSummaryItemClass: TcxDataSummaryItemClass; virtual;
function GetSummaryGroupItemLinkClass: TcxDataSummaryGroupItemLinkClass; virtual;
function InternalCheckBookmark(ADeletedRecordIndex: Integer): Boolean; virtual;
procedure InternalClearBookmark; virtual;
procedure InternalGotoBookmark; virtual;
function InternalSaveBookmark: Boolean; virtual;
function IsDataBound: Boolean;
function IsDataChangedListenersExist: Boolean;
function IsDataField(AField: TcxCustomDataField): Boolean; virtual;
function IsDestroying: Boolean; virtual;
function IsFocusedSelectedMode: Boolean; virtual;
function IsImmediatePost: Boolean; virtual;
function IsKeyNavigation: Boolean; virtual;
function IsLoading: Boolean; virtual;
function IsProviderDataSource: Boolean; virtual; // Data Aware / Unbound
function IsProviderMode: Boolean; virtual;
function IsRecordID: Boolean;
function IsSmartLoad: Boolean; virtual;
function IsSmartRefresh: Boolean; virtual;
function IsUnboundMode: Boolean; virtual;
procedure LoadStorage; virtual;
function IsNewItemRecordIndex(ARecordIndex: Integer): Boolean;
function LockOnAfterSummary: Boolean; virtual;
procedure NotifyDataChangedListeners;
procedure NotifyDataControllers; virtual;
procedure PrepareField(AField: TcxCustomDataField); virtual;
procedure ProviderValueDefSetProc(AValueDef: TcxValueDef; AFromRecordIndex, AToRecordIndex: Integer; AValueDefReader: TcxValueDefReader); virtual;
procedure RemoveNotification(AComponent: TComponent); virtual;
procedure ResetMasterHasChildrenFlag;
procedure ResetNewItemRowFocused;
procedure RestructData; virtual;
procedure SetStoredValue(ARecordIndex: Integer; AField: TcxCustomDataField; const Value: Variant);
function SyncDetailsFocusWithMaster: Boolean; virtual;
procedure SyncMasterPos; virtual;
procedure UpdateFields; virtual;
procedure UpdateFocused; virtual;
procedure UpdateRelations(ARelation: TcxCustomDataRelation); virtual;
procedure UpdateUseRecordIDState;
procedure UpdateStorage(AUpdateFields: Boolean);
function UseRecordID: Boolean; virtual;
// Notification for Self
class function AddListenerLink(ADataController: TcxCustomDataController): TcxDataListenerLink;
class procedure RemoveListenerLink(ALink: TcxDataListenerLink);
// Notification from DataStorage
procedure DataStorageClearInternalRecords(Sender: TObject); virtual;
// Notification from Fields
procedure RemoveField(ADataField: TcxCustomDataField); virtual;
// Notification from Filter
procedure FilterChanged; virtual;
procedure SummaryChanged(ARedrawOnly: Boolean); virtual;
// Notification from Provider
procedure ActiveChanged(AActive: Boolean); virtual;
procedure DataChanged(ADataChange: TcxDataChange; AItemIndex, ARecordIndex: Integer); virtual;
procedure DataScrolled(ADistance: Integer); virtual;
procedure LayoutChanged(ADataLayoutChanges: TcxDataLayoutChanges); virtual;
// Notification for Grid
procedure CustomDataSourceChanged; virtual;
procedure DoBeforeFocusedRowChange(ARowIndex: Integer); virtual;
procedure DoValueTypeClassChanged(AItemIndex: Integer); virtual;
procedure UpdateControl(AInfo: TcxUpdateControlInfo); virtual;
// methods for Grid
// function GetDisplayText(ARecordIndex, AItemIndex: Integer): string; virtual;
function GetIncrementalSearchText(ARecordIndex, AItemIndex: Integer): string; virtual;
function GetFilterDisplayText(ARecordIndex, AItemIndex: Integer): string; virtual;
// Compare
function CompareByField(ARecordIndex1, ARecordIndex2: Integer;
AField: TcxCustomDataField; AMode: TcxDataControllerComparisonMode): Integer; virtual;
function CompareEqualRecords(ARecordIndex1, ARecordIndex2: Integer): Integer; virtual;
function CompareRecords(ARecordIndex1, ARecordIndex2: Integer;
ASortInfo: TcxDataSortInfo; AMode: TcxDataControllerComparisonMode = dccmOther): Integer; virtual;
function FindGroupRecord(ABufferRecordIndex: Integer; AGroupItemCount: Integer;
AIsGroupDataSorted: Boolean): Integer;
function FindRecordByFields(ABufferRecordIndex: Integer; AFields: TList): Integer;
function GetComparedValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant; virtual;
function IsConversionNeededForCompare(AField: TcxCustomDataField): Boolean; virtual;
function IsFilterItemSortByDisplayText(AItemIndex: Integer): Boolean; virtual;
function IsStringConversionNeeded(AField: TcxCustomDataField): Boolean; virtual;
function IsSortByDisplayTextNeeded(AField: TcxCustomDataField): Boolean; virtual;
function NativeCompare(ARecordIndex1, ARecordIndex2: Integer; AField: TcxCustomDataField): Integer;
// Internal Data
procedure DeleteFocusedRecord;
procedure DeleteRecords(AList: TList);
// Smart Load
procedure BeginSmartLoad; virtual;
procedure EndSmartLoad; virtual;
function LoadRecord(AData: Pointer): Integer; virtual;
// Locate
procedure BeginReadRecord; virtual;
procedure EndReadRecord; virtual;
// Sorting By Summary
function GetSortingBySummaryEngineClass: TcxSortingBySummaryEngineClass; virtual;
property DataStorage: TcxDataStorage read FDataStorage;
property Fields: TcxCustomDataFieldList read FFields;
property Notifier: TcxDataControllerNotifier read FNotifier;
property Provider: TcxCustomDataProvider read FProvider;
property LockUpdateFieldsCount: Integer read FLockUpdateFieldsCount;
public
constructor Create(AOwner: TComponent); virtual;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure BeforeDestruction; override;
procedure Destroying; // for CLR
function GetOwner: TPersistent; override;
procedure BeginUpdate;
procedure EndUpdate;
procedure BeginFullUpdate; virtual;
procedure EndFullUpdate; virtual;
procedure BeginUpdateFields;
procedure EndUpdateFields;
procedure SaveKeys;
procedure RestoreKeys;
// internal for Grid
function CreateFilter: TcxDataFilterCriteria; virtual;
// Notification from Grid
function AddItem(AItem: TObject): TcxCustomDataField; virtual;
function ItemPropertiesChanged(AItemIndex: Integer): Boolean; virtual;
procedure Loaded; virtual;
procedure RemoveItem(AItem: TObject); virtual;
procedure UpdateItemIndexes; virtual;
procedure UpdateItems(AUpdateFields: Boolean);
function ExecuteAction(Action: TBasicAction): Boolean; virtual;
function UpdateAction(Action: TBasicAction): Boolean; virtual;
// Data Changed notify
procedure AddDataChangedListener(AInstance: TObject; ADataChangedEvent: TNotifyEvent);
procedure RemoveDataChangedListener(AInstance: TObject; ADataChangedEvent: TNotifyEvent);
procedure AddDataChangeRefCount;
procedure RemoveDataChangeRefCount;
function DataChangedNotifyLocked: Boolean; virtual;
function IsDataLoading: Boolean;
procedure LockDataChangedNotify;
procedure UnlockDataChangedNotify;
procedure LockGridModeNotify;
procedure UnlockGridModeNotify;
// Structure
procedure ChangeNeedConversion(AItemIndex: Integer; ANeedConversion: Boolean); virtual;
procedure ChangeTextStored(AItemIndex: Integer; ATextStored: Boolean); virtual;
procedure ChangeValueTypeClass(AItemIndex: Integer; AValueTypeClass: TcxValueTypeClass); virtual;
function GetItem(Index: Integer): TObject; virtual; abstract;
function GetItemCount: Integer; virtual;
function GetItemNeedConversion(AItemIndex: Integer): Boolean; virtual;
function GetItemTextStored(AItemIndex: Integer): Boolean; virtual;
function GetItemValueTypeClass(AItemIndex: Integer): TcxValueTypeClass;
function IsDisplayFormatDefined(AItemIndex: Integer; AIgnoreSimpleCurrency: Boolean): Boolean; virtual;
// Data
function AppendRecord: Integer;
procedure DeleteRecord(ARecordIndex: Integer);
function GetGroupValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant; virtual;
procedure GetGroupValues(ARecordIndex: Integer; var AValues: TcxDataSummaryValues); virtual;
function GetDisplayText(ARecordIndex, AItemIndex: Integer): string; virtual;
function GetRecordCount: Integer; virtual;
function GetRecordId(ARecordIndex: Integer): Variant; virtual;
function GetValue(ARecordIndex, AItemIndex: Integer): Variant; virtual;
function InsertRecord(ARecordIndex: Integer): Integer;
procedure Refresh; virtual;
procedure SetDisplayText(ARecordIndex, AItemIndex: Integer; const Value: string); virtual;
procedure SetRecordCount(Value: Integer); virtual;
procedure SetValue(ARecordIndex, AItemIndex: Integer; const Value: Variant); virtual;
procedure SortByDisplayTextChanged;
// Data Editing
procedure Append; virtual;
procedure Cancel; virtual;
function CanInitEditing(AItemIndex: Integer): Boolean; virtual;
procedure CheckBrowseMode; virtual;
procedure DeleteFocused; virtual;
procedure DeleteSelection; virtual;
procedure Edit; virtual;
procedure FocusControl(AItemIndex: Integer; var Done: Boolean); virtual;
function GetEditValue(AItemIndex: Integer; AEditValueSource: TcxDataEditValueSource): Variant; virtual;
function GetItemValueSource(AItemIndex: Integer): TcxDataEditValueSource; virtual;
procedure Insert; virtual;
procedure Post(AForcePost: Boolean = False); virtual;
procedure PostEditingData; virtual;
procedure RefreshExternalData; virtual;
function SetEditValue(AItemIndex: Integer; const AValue: Variant; AEditValueSource: TcxDataEditValueSource): Boolean; virtual;
procedure UpdateData; virtual;
// Data New Item Row
function GetEditingRecordIndex: Integer; virtual;
function GetNewItemRecordIndex: Integer; virtual;
// Data Save/Load
procedure LoadFromStream(AStream: TStream);
procedure SaveToStream(AStream: TStream);
// Master-Detail: Relations
function GetMasterDataController: TcxCustomDataController; virtual;
function GetMasterRecordIndex: Integer; virtual;
function GetMasterRelation: TcxCustomDataRelation; virtual;
function GetPatternDataController: TcxCustomDataController;
function GetRootDataController: TcxCustomDataController;
function IsDetailMode: Boolean; virtual;
// Master-Detail: Grid override
function CreateDetailLinkObject(ARelation: TcxCustomDataRelation; ARecordIndex: Integer): TObject; virtual;
procedure FocusDetails(ARecordIndex: Integer);
function GetDetailDataControllerByLinkObject(ALinkObject: TObject): TcxCustomDataController; virtual;
// Master-Detail: Grid Notifications
procedure ResetRelationByItem(AItem: TObject); virtual;
procedure SetMasterMode(AMasterRelation: TcxCustomDataRelation; AIsPattern: Boolean);
procedure SetMasterRelation(AMasterRelation: TcxCustomDataRelation; AMasterRecordIndex: Integer); virtual;
// Master-Detail: View Data
procedure ChangeDetailActiveRelationIndex(ARecordIndex: Integer; ARelationIndex: Integer);
function ChangeDetailExpanding(ARecordIndex: Integer; AExpanded: Boolean): Boolean;
procedure ClearDetailLinkObject(ARecordIndex: Integer; ARelationIndex: Integer);
procedure ClearDetails;
procedure CollapseDetails;
procedure ForEachDetail(AMasterRelation: TcxCustomDataRelation; AProc: TcxDataControllerEachDetailProc);
function GetDetailActiveRelationIndex(ARecordIndex: Integer): Integer;
function GetDetailDataController(ARecordIndex: Integer; ARelationIndex: Integer): TcxCustomDataController;
function GetDetailExpanding(ARecordIndex: Integer): Boolean;
function GetDetailHasChildren(ARecordIndex, ARelationIndex: Integer): Boolean;
function GetDetailLinkObject(ARecordIndex: Integer; ARelationIndex: Integer): TObject;
function IsDetailDataControllerExist(ARecordIndex: Integer; ARelationIndex: Integer): Boolean;
procedure ResetHasChildrenFlag;
// View Data
procedure ForEachRow(ASelectedRows: Boolean; AProc: TcxDataControllerEachRowProc); virtual;
function GetNearestRowIndex(ARowIndex: Integer): Integer;
function GetRowCount: Integer;
function GetRowIndexByRecordIndex(ARecordIndex: Integer; AMakeVisible: Boolean): Integer;
function GetRowInfo(ARowIndex: Integer): TcxRowInfo;
function GetRowDisplayText(const ARowInfo: TcxRowInfo; var AItemIndex: Integer): string;
function GetRowValue(const ARowInfo: TcxRowInfo; AItemIndex: Integer): Variant;
procedure MakeRecordVisible(ARecordIndex: Integer);
// Export in GridMode
function FocusSelectedRow(ASelectedIndex: Integer): Boolean; virtual;
procedure RestoreDataSetPos; virtual;
procedure SaveDataSetPos; virtual;
// Navigation
procedure ChangeFocusedRecordIndex(ARecordIndex: Integer);
function ChangeFocusedRowIndex(ARowIndex: Integer): Boolean;
procedure CheckFocusedRow;
function GetFocusedRecordIndex: Integer;
function GetFocusedRowIndex: Integer;
procedure GotoFirst;
procedure GotoLast;
procedure GotoNext;
procedure GotoPrev;
function IsBOF: Boolean;
function IsEOF: Boolean;
function IsGridMode: Boolean;
procedure MoveBy(ADistance: Integer);
procedure Scroll(ADistance: Integer);
procedure SetFocus; virtual;
// Bookmark
procedure ClearBookmark;
procedure GotoBookmark;
function IsBookmarkAvailable: Boolean; virtual;
function IsBookmarkRow(ARowIndex: Integer): Boolean; virtual;
procedure SaveBookmark;
// Filtering
function GetFilterDataValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant; virtual;
function GetFilterItemFieldCaption(AItem: TObject): string; virtual;
function GetFilterItemFieldName(AItem: TObject): string; virtual;
// Search
function ChangeIncrementalFilterText(const AText: string): Boolean;
function FindRecordIndexByText(AStartRecordIndex, AItemIndex: Integer;
const AText: string; APartialCompare, ACircular, AForward: Boolean): Integer; virtual;
function GetIncrementalFilterText: string; virtual;
function IsIncrementalFilter: Boolean; virtual;
procedure ResetIncrementalFilter; virtual;
function SetIncrementalFilter(AItemIndex: Integer; const AText: string): Integer; virtual;
// Sorting
procedure ChangeItemSortingIndex(AItemIndex: Integer; ASortingIndex: Integer);
procedure ChangeSorting(AItemIndex: Integer; ASortOrder: TcxDataSortOrder);
procedure ClearSorting(AKeepGroupedItems: Boolean);
function GetItemSortByDisplayText(AItemIndex: Integer; ASortByDisplayText: Boolean): Boolean; virtual;
function GetItemSortOrder(AItemIndex: Integer): TcxDataSortOrder;
function GetItemSortingIndex(AItemIndex: Integer): Integer;
function GetSortingItemCount: Integer;
function GetSortingItemIndex(Index: Integer): Integer;
// MultiSelect
procedure ChangeRowSelection(ARowIndex: Integer; ASelection: Boolean); virtual;
procedure CheckFocusedSelected;
procedure ClearSelection;
procedure ClearSelectionAnchor; virtual;
function GetRowId(ARowIndex: Integer): Variant; virtual;
function GetSelectedCount: Integer;
function GetSelectedRowIndex(Index: Integer): Integer; virtual; // Visible Order
function GetSelectionAnchorRowIndex: Integer; virtual;
function IsRowSelected(ARowIndex: Integer): Boolean;
function IsSelectionAnchorExist: Boolean; virtual;
procedure SelectAll; virtual;
procedure SelectFromAnchor(ARowIndex: Integer; AKeepSelection: Boolean); virtual;
procedure SelectRows(AStartRowIndex, AEndRowIndex: Integer);
procedure SetSelectionAnchor(ARowIndex: Integer); virtual;
procedure SyncSelected(ASelected: Boolean); // not MultiSelect only
procedure SyncSelectionFocusedRecord; virtual;
property Active: Boolean read FActive;
property CustomDataSource: TcxCustomDataSource read GetCustomDataSource write SetCustomDataSource;
property DataControllerInfo: TcxCustomDataControllerInfo read FDataControllerInfo;
property DetailMode: TcxDataControllerDetailMode read GetDetailMode;
property DisplayTexts[RecordIndex, ItemIndex: Integer]: string read GetDisplayText write SetDisplayText;
property Groups: TcxDataControllerGroups read FGroups;
property EditingRecordIndex: Integer read GetEditingRecordIndex;
property EditOperations: TcxDataControllerEditOperations read GetEditOperations;
property EditState: TcxDataControllerEditState read GetEditState;
property Filter: TcxDataFilterCriteria read FFilter write SetFilter;
property FilteredIndexByRecordIndex[Index: Integer]: Integer read GetFilteredIndexByRecordIndex;
property FilteredRecordCount: Integer read GetFilteredRecordCount;
property FilteredRecordIndex[Index: Integer]: Integer read GetFilteredRecordIndex;
property FocusedRecordIndex: Integer read GetFocusedRecordIndex write ChangeFocusedRecordIndex;
property FocusedRowIndex: Integer read GetFocusedRowIndex write SetFocusedRowIndex;
property IsCreatingLinkObject: Boolean read FCreatingLinkObject;
property IsDetailExpanding: Boolean read FIsDetailExpanding;
property IsEditing: Boolean read GetIsEditing;
property IsPattern: Boolean read GetIsPattern write SetIsPattern;
property IsRowInfoValid: Boolean read GetIsRowInfoValid;
property IsUpdatingItems: Boolean read FUpdateItems;
property ItemCount: Integer read GetItemCount;
property LockCount: Integer read GetLockCount;
property LockGridModeNotifyCount: Integer read FLockGridModeNotifyCount;
property MultiSelect: Boolean read FMultiSelect write SetMultiSelect;
property NewItemRecordIndex: Integer read GetNewItemRecordIndex;
property NewItemRowFocused: Boolean read GetNewItemRowFocused write SetNewItemRowFocused;
property Options: TcxDataControllerOptions read GetOptions write SetOptions default [dcoAssignGroupingValues, dcoAssignMasterDetailKeys, dcoSaveExpanding];
property RecordCount: Integer read GetRecordCount write SetRecordCount;
property Relations: TcxCustomDataRelationList read GetRelations;
property RowCount: Integer read GetRowCount;
property Search: TcxDataControllerSearch read FSearch;
property Summary: TcxDataSummary read FSummary write SetSummary;
property UseNewItemRowForEditing: Boolean read FUseNewItemRowForEditing write SetUseNewItemRowForEditing;
property Values[RecordIndex, ItemIndex: Integer]: Variant read GetValue write SetValue;
// Sorting By Summary
property SortingBySummaryDataItemIndex: Integer read GetSortingBySummaryDataItemIndex write SetSortingBySummaryDataItemIndex;
property OnAfterCancel: TcxDataControllerNotifyEvent read FOnAfterCancel write FOnAfterCancel;
property OnAfterDelete: TcxDataControllerNotifyEvent read FOnAfterDelete write FOnAfterDelete;
property OnAfterInsert: TcxDataControllerNotifyEvent read FOnAfterInsert write FOnAfterInsert;
property OnAfterPost: TcxDataControllerNotifyEvent read FOnAfterPost write FOnAfterPost;
property OnBeforeCancel: TcxDataControllerNotifyEvent read FOnBeforeCancel write FOnBeforeCancel;
property OnBeforeDelete: TcxDataRecordNotifyEvent read FOnBeforeDelete write FOnBeforeDelete;
property OnBeforeInsert: TcxDataControllerNotifyEvent read FOnBeforeInsert write FOnBeforeInsert;
property OnBeforePost: TcxDataControllerNotifyEvent read FOnBeforePost write FOnBeforePost;
property OnFilterRecord: TcxDataFilterRecordEvent read FOnFilterRecord write SetOnFilterRecord;
property OnNewRecord: TcxDataRecordNotifyEvent read FOnNewRecord write FOnNewRecord;
property OnCompare: TcxDataControllerCompareEvent read FOnCompare write FOnCompare;
property OnDataChanged: TNotifyEvent read FOnDataChanged write FOnDataChanged;
property OnDetailCollapsing: TcxDataDetailExpandingEvent read FOnDetailCollapsing write FOnDetailCollapsing;
property OnDetailCollapsed: TcxDataDetailExpandedEvent read FOnDetailCollapsed write FOnDetailCollapsed;
property OnDetailExpanding: TcxDataDetailExpandingEvent read FOnDetailExpanding write FOnDetailExpanding;
property OnDetailExpanded: TcxDataDetailExpandedEvent read FOnDetailExpanded write FOnDetailExpanded;
property OnGroupingChanged: TNotifyEvent read FOnGroupingChanged write FOnGroupingChanged;
property OnRecordChanged: TcxDataRecordChangedEvent read FOnRecordChanged write FOnRecordChanged;
property OnSortingChanged: TNotifyEvent read FOnSortingChanged write FOnSortingChanged;
property OnUpdateControl: TcxUpdateControlEvent read FOnUpdateControl write FOnUpdateControl;
end;
TcxCustomDataControllerClass = class of TcxCustomDataController;
TcxCustomDataHelper = class
public
class function GetValueDef(AField: TcxCustomDataField): TcxValueDef;
class procedure SetTextStored(AField: TcxCustomDataField; ATextStored: Boolean);
end;
TCompareItems = function (AItem1, AItem2: Pointer): Integer of object;
procedure QuickSortList(ASortList: PPointerList;
L, R: Integer; ACompareFunc: TCompareItems);
procedure InvalidOperation(const S: string);
function cxDataGetDataSummaryValueDefaultFormat(AValueType: TcxSummaryValueType;
ASummaryKind: TcxSummaryKind; AIsFooter: Boolean): string;
const
cxDataFilterVersion: Byte = 3;
implementation
uses
{$IFDEF DELPHI9}
Windows,
{$ENDIF}
cxDataConsts, cxFilterConsts;
const
stDataControllerSignature = 'DataController1';
stBeginText = '(';
stEndText = ')';
stSeparator = ',';
NullRecordHandle: TcxDataRecordHandle = TcxDataRecordHandle(-1);
{$IFNDEF DELPHI6}
// HRESULT value definitions from windows.pas
// Success codes
S_OK = $00000000;
S_FALSE = $00000001;
// No such interface supported
E_NOINTERFACE = HRESULT($80004002);
{$ENDIF}
type
TNotifyEventItem = class
Instance: TObject;
Event: TNotifyEvent;
end;
{ Keys Storage }
TcxSaveObject = class;
TcxKeyInfo = class
Key: Variant;
end;
TcxSelectedKeyInfo = class(TcxKeyInfo)
SelectedIndex: Integer;
end;
TcxDetailKeyInfo = class(TcxKeyInfo)
ActiveRelationIndex: Integer;
SubDetail: TcxSaveObject;
end;
TcxKeys = class
private
FDataController: TcxCustomDataController;
FKeyFields: TList;
FKeys: TList;
procedure SetDataController(Value: TcxCustomDataController);
protected
procedure DoRestore; virtual;
procedure DoSave; virtual;
function Find(ARecordIndex: Integer; var AIndex: Integer): Boolean;
procedure FreeKeyInfo(AKeyInfo: TObject); virtual;
property DataController: TcxCustomDataController read FDataController write SetDataController;
property Keys: TList read FKeys;
public
constructor Create;
destructor Destroy; override;
procedure Clear; virtual;
procedure Delete(AIndex: Integer);
procedure Restore(ADataController: TcxCustomDataController);
procedure Save(ADataController: TcxCustomDataController);
end;
TcxDetailKeys = class(TcxKeys)
protected
procedure DoRestore; override;
procedure DoSave; override;
procedure FreeKeyInfo(AKeyInfo: TObject); override;
end;
TcxSelectedKeys = class(TcxKeys)
protected
procedure DoRestore; override;
procedure DoSave; override;
procedure FreeKeyInfo(AKeyInfo: TObject); override;
end;
TcxSaveObject = class
private
FDetailKeys: TcxDetailKeys;
FSelectedKeys: TcxSelectedKeys;
public
constructor Create;
destructor Destroy; override;
procedure Restore(ADataController: TcxCustomDataController);
procedure Save(ADataController: TcxCustomDataController);
end;
function CompareByLinkObject(AItem1, AItem2: Pointer): Integer;
begin
Result := TcxCustomDataField(TcxValueDef(AItem1).LinkObject).Index -
TcxCustomDataField(TcxValueDef(AItem2).LinkObject).Index;
end;
procedure InvalidOperation(const S: string);
begin
raise EcxInvalidDataControllerOperation.Create(S);
end;
function cxDataGetDataSummaryValueDefaultFormat(AValueType: TcxSummaryValueType;
ASummaryKind: TcxSummaryKind; AIsFooter: Boolean): string;
const
ACurrencyFormats: array [Boolean, skSum..skAverage] of string = (
('SUM=%s;SUM=-%s', 'MIN=%s;MIN=-%s', 'MAX=%s;MAX=-%s', 'COUNT=0', 'AVG=%s;AVG=-%s'),
('%s;-%s', 'MIN=%s;MIN=-%s', 'MAX=%s;MAX=-%s', '0', 'AVG=%s;AVG=-%s'));
ADateFormats: array [Boolean, skSum..skAverage] of string = (
('SUM=', 'MIN=', 'MAX=', 'COUNT=0', 'AVG='),
('', 'MIN=', 'MAX=', '0', 'AVG='));
AFloatFormats: array [Boolean, skSum..skAverage] of string = (
('SUM=0.00;SUM=-0.00', 'MIN=0.00;MIN=-0.00', 'MAX=0.00;MAX=-0.00', 'COUNT=0', 'AVG=0.00;AVG=-0.00'),
('0.00;-0.00', 'MIN=0.00;MIN=-0.00', 'MAX=0.00;MAX=-0.00', '0', 'AVG=0.00;AVG=-0.00'));
var
S: string;
begin
Result := '';
if ASummaryKind = skNone then Exit;
case AValueType of
svtCurrency:
begin
S := DefaultCurrencyDisplayFormat;
Result := Format(ACurrencyFormats[AIsFooter, ASummaryKind], [S, S]);
end;
svtDate:
Result := ADateFormats[AIsFooter, ASummaryKind];
else
Result := AFloatFormats[AIsFooter, ASummaryKind];
end;
end;
function CompareSelectionList(AItem1, AItem2: Pointer): Integer;
begin
Result := PcxDataSelectionInfo(AItem1).RowIndex - PcxDataSelectionInfo(AItem2).RowIndex;
end;
function CompareIntegers(AItem1, AItem2: Pointer): Integer;
begin
Result := Integer(AItem1) - Integer(AItem2);
end;
function CompareByKey(AItem1, AItem2: Pointer): Integer;
begin
Result := VarCompare(TcxKeyInfo(AItem1).Key, TcxKeyInfo(AItem2).Key);
end;
procedure QuickSortList(ASortList: PPointerList;
L, R: Integer; ACompareFunc: TCompareItems);
var
I, J: Integer;
P, T: Pointer;
begin
repeat
I := L;
J := R;
P := ASortList[(L + R) shr 1];
repeat
while ACompareFunc(ASortList[I], P) < 0 do
Inc(I);
while ACompareFunc(ASortList[J], P) > 0 do
Dec(J);
if I <= J then
begin
T := ASortList[I];
ASortList[I] := ASortList[J];
ASortList[J] := T;
Inc(I);
Dec(J);
end;
until I > J;
if L < J then
QuickSortList(ASortList, L, J, ACompareFunc);
L := I;
until I >= R;
end;
function IsValueDefInternal(AValueDef: TcxValueDef): Boolean;
begin
Result := (AValueDef.LinkObject is TcxCustomDataField) and
TcxCustomDataField(AValueDef.LinkObject).IsValueDefInternal;
end;
procedure ListSetCount(AList: TList; ANewCount: Integer);
begin
AList.Count := ANewCount;
end;
{ TcxKeys }
constructor TcxKeys.Create;
begin
inherited Create;
FKeyFields := TList.Create;
FKeys := TList.Create;
end;
destructor TcxKeys.Destroy;
begin
Clear;
FKeyFields.Free;
FKeys.Free;
inherited Destroy;
end;
procedure TcxKeys.Clear;
var
I: Integer;
begin
for I := 0 to Keys.Count - 1 do
FreeKeyInfo(Keys[I]);
Keys.Clear;
end;
procedure TcxKeys.Delete(AIndex: Integer);
begin
FreeKeyInfo(Keys[AIndex]);
Keys.Delete(AIndex);
end;
procedure TcxKeys.Restore(ADataController: TcxCustomDataController);
begin
DataController := ADataController;
if Keys.Count > 0 then // !!! TODO
begin
DoRestore;
Clear;
end;
end;
procedure TcxKeys.Save(ADataController: TcxCustomDataController);
begin
Clear;
DataController := ADataController;
DoSave;
Keys.Sort(CompareByKey);
end;
procedure TcxKeys.DoRestore;
begin
end;
procedure TcxKeys.DoSave;
begin
end;
function TcxKeys.Find(ARecordIndex: Integer; var AIndex: Integer): Boolean;
var
I: Integer;
AKeyInfo: TcxKeyInfo;
begin
Result := False;
for I := 0 to Keys.Count - 1 do
begin
AKeyInfo := TcxKeyInfo(Keys[I]);
if VarEquals(DataController.GetInternalRecordId(ARecordIndex, FKeyFields), AKeyInfo.Key) then
begin
AIndex := I;
Result := True;
Break;
end;
end;
end;
procedure TcxKeys.FreeKeyInfo(AKeyInfo: TObject);
begin
AKeyInfo.Free;
end;
procedure TcxKeys.SetDataController(Value: TcxCustomDataController);
begin
FDataController := Value;
FKeyFields.Clear;
DataController.GetKeyFields(FKeyFields);
end;
{ TcxDetailKeys }
procedure TcxDetailKeys.DoRestore;
var
I, J: Integer;
ADetailKeyInfo: TcxDetailKeyInfo;
ADetailLinkObject: TObject;
ADataController: TcxCustomDataController;
begin
for I := 0 to DataController.RecordCount - 1 do
begin
if Find(I, J) then
begin
ADetailKeyInfo := TcxDetailKeyInfo(Keys[J]);
DataController.ChangeDetailExpanding(I, True);
DataController.ChangeDetailActiveRelationIndex(I, ADetailKeyInfo.ActiveRelationIndex);
if ADetailKeyInfo.SubDetail <> nil then
begin
if DataController.GetDetailExpanding(I) and
(DataController.GetDetailActiveRelationIndex(I) = ADetailKeyInfo.ActiveRelationIndex) then
begin
// WARNING: GetDetailDataController->nil !
ADetailLinkObject := DataController.GetDetailLinkObject(I, ADetailKeyInfo.ActiveRelationIndex);
if ADetailLinkObject <> nil then
begin
ADataController := DataController.GetDetailDataControllerByLinkObject(ADetailLinkObject);
if ADataController.InheritsFrom(DataController.ClassType) then //if ADataController is TcxDBDataController then
ADetailKeyInfo.SubDetail.Restore(ADataController);
end;
end;
end;
end;
end;
end;
procedure TcxDetailKeys.DoSave;
var
I: Integer;
ADetailKeyInfo: TcxDetailKeyInfo;
ADetailLinkObject: TObject;
ADataController: TcxCustomDataController;
begin
for I := 0 to DataController.RecordCount - 1 do
begin
if DataController.GetDetailExpanding(I) then
begin
ADetailKeyInfo := TcxDetailKeyInfo.Create;
Keys.Add(ADetailKeyInfo);
ADetailKeyInfo.Key := DataController.GetInternalRecordId(I, FKeyFields);
ADetailKeyInfo.ActiveRelationIndex := DataController.GetDetailActiveRelationIndex(I);
ADetailKeyInfo.SubDetail := TcxSaveObject.Create;
// WARNING: GetDetailDataController->nil !
ADetailLinkObject := DataController.GetDetailLinkObject(I, ADetailKeyInfo.ActiveRelationIndex);
if ADetailLinkObject <> nil then
begin
ADataController := DataController.GetDetailDataControllerByLinkObject(ADetailLinkObject);
if ADataController.InheritsFrom(DataController.ClassType) then //if ADataController is TcxDBDataController then
ADetailKeyInfo.SubDetail.Save(ADataController);
end;
end;
end;
end;
procedure TcxDetailKeys.FreeKeyInfo(AKeyInfo: TObject);
begin
if TcxDetailKeyInfo(AKeyInfo).SubDetail <> nil then
TcxDetailKeyInfo(AKeyInfo).SubDetail.Free;
AKeyInfo.Free;
end;
{ TcxSelectedKeys }
procedure TcxSelectedKeys.DoRestore;
var
I, J: Integer;
ASelection: TcxDataSelection;
ASelectedKeyInfo: TcxSelectedKeyInfo;
begin
ASelection := DataController.DataControllerInfo.Selection;
if ASelection.Count = 0 then Exit;
// TODO: Level check <> -1
for I := 0 to DataController.RecordCount - 1 do
begin
if Find(I, J) then
begin
ASelectedKeyInfo := TcxSelectedKeyInfo(Keys[J]);
if ASelectedKeyInfo.SelectedIndex < ASelection.Count then
ASelection[ASelectedKeyInfo.SelectedIndex].RecordIndex := I;
Delete(J);
end;
end;
for I := 0 to Keys.Count - 1 do
begin
ASelectedKeyInfo := TcxSelectedKeyInfo(Keys[I]);
if (0 <= ASelectedKeyInfo.SelectedIndex) and (ASelectedKeyInfo.SelectedIndex < ASelection.Count) then
ASelection[ASelectedKeyInfo.SelectedIndex].RecordIndex := -1;
end;
for I := ASelection.Count - 1 downto 0 do
if ASelection[I].RecordIndex = -1 then
ASelection.Delete(I);
DataController.DataControllerInfo.ExpandingChanged;
end;
procedure TcxSelectedKeys.DoSave;
var
ASelection: TcxDataSelection;
I: Integer;
PSelectionInfo: PcxDataSelectionInfo;
ASelectedKeyInfo: TcxSelectedKeyInfo;
begin
ASelection := DataController.DataControllerInfo.Selection;
for I := 0 to ASelection.Count - 1 do
begin
PSelectionInfo := ASelection[I];
if (PSelectionInfo.Level <> -1) and
(PSelectionInfo.RecordIndex < DataController.RecordCount) then
begin
ASelectedKeyInfo := TcxSelectedKeyInfo.Create;
Keys.Add(ASelectedKeyInfo);
ASelectedKeyInfo.SelectedIndex := I;
ASelectedKeyInfo.Key := DataController.GetInternalRecordId(PSelectionInfo.RecordIndex, FKeyFields);
end;
end;
DataController.DataControllerInfo.SaveExpanding([eisExpanded, eisSelected]);
end;
procedure TcxSelectedKeys.FreeKeyInfo(AKeyInfo: TObject);
begin
AKeyInfo.Free;
end;
{ TcxSaveObject }
constructor TcxSaveObject.Create;
begin
inherited Create;
FDetailKeys := TcxDetailKeys.Create;
FSelectedKeys := TcxSelectedKeys.Create;
end;
destructor TcxSaveObject.Destroy;
begin
FSelectedKeys.Free;
FDetailKeys.Free;
inherited Destroy;
end;
procedure TcxSaveObject.Restore(ADataController: TcxCustomDataController);
begin
if ADataController.IsCreatingLinkObject then Exit;
if ADataController.IsKeyNavigation then
begin
FDetailKeys.Restore(ADataController);
FSelectedKeys.Restore(ADataController);
end
else
ADataController.CheckSelectedCount(-1);
end;
procedure TcxSaveObject.Save(ADataController: TcxCustomDataController);
begin
if ADataController.IsCreatingLinkObject or
ADataController.IsDestroying then Exit;
if ADataController.IsKeyNavigation then
begin
FDetailKeys.Save(ADataController);
FSelectedKeys.Save(ADataController);
end;
end;
{ TcxCustomDataSource }
destructor TcxCustomDataSource.Destroy;
begin
RemoveFromProviders;
FProviders.Free;
FProviders := nil;
// if Assigned(FProvider) then
// FProvider.CustomDataSource := nil;
inherited Destroy;
end;
procedure TcxCustomDataSource.DataChanged;
begin
if Provider = nil then Exit;
Provider.DataController.BeginFullUpdate;
try
Provider.DataChanged(dcTotal, -1, -1);
finally
Provider.DataController.EndFullUpdate;
end;
end;
function TcxCustomDataSource.GetRecordHandleByIndex(ARecordIndex: Integer): TcxDataRecordHandle;
begin
Result := TcxDataRecordHandle(Integer(DataController.GetInternalValue(ARecordIndex, DataController.FRecordHandlesField)));
end;
function TcxCustomDataSource.GetRecordIndexByHandle(ARecordHandle: TcxDataRecordHandle): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to DataController.RecordCount - 1 do
if GetRecordHandleByIndex(I) = ARecordHandle then
begin
Result := I;
Break;
end;
end;
procedure TcxCustomDataSource.CustomSort;
begin
end;
function TcxCustomDataSource.AppendRecord: TcxDataRecordHandle;
begin
Result := NullRecordHandle;
// IMPL: Add + Data Notify
end;
procedure TcxCustomDataSource.DeleteRecord(ARecordHandle: TcxDataRecordHandle);
begin
// IMPL: Delete + Data Notify
end;
function TcxCustomDataSource.GetDefaultItemID(AItemIndex: Integer): Integer;
begin
Result := DataController.GetItemID(DataController.GetItem(AItemIndex));
end;
function TcxCustomDataSource.GetDetailHasChildren(ARecordIndex, ARelationIndex: Integer): Boolean;
begin
Result := False;
end;
function TcxCustomDataSource.GetDisplayText(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): string;
begin
try
Result := VarToStr(GetValue(ARecordHandle, AItemHandle));
except
on EVariantError do
Result := '';
end;
end;
function TcxCustomDataSource.GetInfoForCompare(ARecordHandle: TcxDataRecordHandle;
AItemHandle: TcxDataItemHandle; var PValueBuffer: PAnsiChar): Boolean;
begin
InvalidOperation(cxSDataCustomDataSourceInvalidCompare);
Result := False; // i.e. IS NULL
end;
function TcxCustomDataSource.GetItemHandle(AItemIndex: Integer): TcxDataItemHandle;
begin
Result := TcxDataItemHandle(AItemIndex);
end;
function TcxCustomDataSource.GetRecordCount: Integer;
begin
Result := 0;
end;
function TcxCustomDataSource.GetRecordId(ARecordHandle: TcxDataRecordHandle): Variant;
begin
Result := Null;
end;
function TcxCustomDataSource.GetRecordHandle(ARecordIndex: Integer): TcxDataRecordHandle;
begin
Result := TcxDataRecordHandle(ARecordIndex);
end;
function TcxCustomDataSource.GetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): Variant;
begin
Result := Null;
end;
function TcxCustomDataSource.InsertRecord(ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle;
begin
// IMPL: Insert + Data Notify
Result := AppendRecord;
end;
function TcxCustomDataSource.IsCustomSorting: Boolean;
begin
Result := False;
end;
function TcxCustomDataSource.IsNativeCompare: Boolean;
begin
Result := False;
end;
function TcxCustomDataSource.IsNativeCompareFunc: Boolean;
begin
Result := False;
end;
function TcxCustomDataSource.IsRecordIdSupported: Boolean;
begin
Result := False;
end;
procedure TcxCustomDataSource.LoadRecordHandles;
var
I: Integer;
begin
DataController.DataStorage.Clear(Provider.FDataChangedLocked);
DataController.DataStorage.RecordCount := GetRecordCount;
for I := 0 to DataController.{DataStorage.}RecordCount - 1 do
DataController.SetStoredValue(I, DataController.FRecordHandlesField, Integer(GetRecordHandle(I)));
end;
function TcxCustomDataSource.NativeCompareFunc(ARecordHandle1, ARecordHandle2: TcxDataRecordHandle;
AItemHandle: TcxDataItemHandle): Integer;
begin
Result := 0;
end;
procedure TcxCustomDataSource.SetRecordCount(ARecordCount: Integer);
begin
// IMPL: change record count + Data Notify
end;
procedure TcxCustomDataSource.SetValue(ARecordHandle: TcxDataRecordHandle;
AItemHandle: TcxDataItemHandle; const AValue: Variant);
begin
// IMPL: change Value + Data Notify
end;
function TcxCustomDataSource.AddRecordHandle(ARecordHandle: TcxDataRecordHandle): Integer;
begin
Result := DataController.DataStorage.AppendRecord;
DataController.SetStoredValue(Result, DataController.FRecordHandlesField, Integer(ARecordHandle));
end;
function TcxCustomDataSource.GetDataController: TcxCustomDataController;
begin
Result := Provider.DataController;
end;
function TcxCustomDataSource.GetProvider: TcxCustomDataProvider;
begin
if FCurrentProvider <> nil then
Result := FCurrentProvider
else
Result := FProvider;
end;
procedure TcxCustomDataSource.AddProvider(AProvider: TcxCustomDataProvider);
begin
if FProviders = nil then
FProviders := TList.Create;
FProviders.Add(AProvider);
end;
procedure TcxCustomDataSource.RemoveProvider(AProvider: TcxCustomDataProvider);
begin
if FProviders = nil then Exit;
FProviders.Remove(AProvider);
if CurrentProvider = AProvider then
CurrentProvider := nil;
end;
procedure TcxCustomDataSource.RemoveFromProviders;
var
I: Integer;
begin
if FProviders = nil then Exit;
for I := FProviders.Count - 1 downto 0 do
if TcxCustomDataProvider(FProviders[I]).CustomDataSource = Self then
TcxCustomDataProvider(FProviders[I]).CustomDataSource := nil;
end;
{ TcxCustomDataProvider }
constructor TcxCustomDataProvider.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FDataController := ADataController;
FEditingRecordIndex := cxNullEditingRecordIndex;
FEditingRecordIndex1 := cxNullEditingRecordIndex;
FEditingRecordIndex2 := cxNullEditingRecordIndex;
FInsertedRecordIndex := -1;
end;
destructor TcxCustomDataProvider.Destroy;
begin
ClearSavedRecord;
CustomDataSource := nil;
inherited Destroy;
end;
// Mode
procedure TcxCustomDataProvider.CustomSort;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
CustomDataSource.CustomSort;
end;
end;
function TcxCustomDataProvider.IsCustomDataSourceSupported: Boolean;
begin
Result := True;
end;
function TcxCustomDataProvider.IsCustomSorting: Boolean;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
Result := CustomDataSource.IsCustomSorting;
end
else
Result := False;
end;
function TcxCustomDataProvider.IsGridMode: Boolean;
begin
Result := False;
end;
function TcxCustomDataProvider.IsDataSource: Boolean;
begin
Result := False;
end;
function TcxCustomDataProvider.IsOtherInsert: Boolean;
begin
Result := False;
end;
function TcxCustomDataProvider.IsRecordIdSupported: Boolean;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
Result := CustomDataSource.IsRecordIdSupported;
end
else
Result := False;
end;
function TcxCustomDataProvider.IsSyncMode: Boolean;
begin
Result := False;
end;
// State
function TcxCustomDataProvider.IsActive: Boolean;
begin
Result := CustomDataSource <> nil;
end;
function TcxCustomDataProvider.IsActiveDataSet: Boolean;
begin
Result := False;
end;
function TcxCustomDataProvider.IsBOF: Boolean;
begin
Result := True;
end;
function TcxCustomDataProvider.IsChanging: Boolean;
begin
Result := FChanging;
end;
function TcxCustomDataProvider.IsEditing: Boolean;
begin
Result := FEditingRecordIndex <> cxNullEditingRecordIndex;
end;
function TcxCustomDataProvider.IsEOF: Boolean;
begin
Result := True;
end;
function TcxCustomDataProvider.IsGridModeUpdating: Boolean;
begin
Result := False;
end;
function TcxCustomDataProvider.IsInserting: Boolean;
begin
Result := IsEditing and FInserting;
end;
function TcxCustomDataProvider.IsModified: Boolean;
begin
Result := FModified;
end;
function TcxCustomDataProvider.IsUnboundColumnMode: Boolean;
begin
Result := not CanModify; // !!!
end;
// Navigation
procedure TcxCustomDataProvider.CorrectRecordIndex(ARecordIndex: Integer);
begin
end;
procedure TcxCustomDataProvider.First;
begin
FRecreatedFieldsAfterFirst := False;
end;
procedure TcxCustomDataProvider.Prev;
begin
end;
procedure TcxCustomDataProvider.Next;
begin
end;
procedure TcxCustomDataProvider.Last;
begin
end;
procedure TcxCustomDataProvider.MoveBy(ADistance: Integer);
begin
end;
procedure TcxCustomDataProvider.Scroll(ADistance: Integer);
begin
end;
procedure TcxCustomDataProvider.SavePos;
begin
end;
procedure TcxCustomDataProvider.RestorePos;
begin
end;
// Editing
function TcxCustomDataProvider.CanAppend: Boolean;
begin
Result := CanModify;
end;
function TcxCustomDataProvider.CanInsert: Boolean;
begin
Result := CanModify;
end;
function TcxCustomDataProvider.CanDelete: Boolean;
begin
Result := DataController.FilteredRecordCount > 0;
end;
function TcxCustomDataProvider.CanInitEditing(ARecordIndex: Integer): Boolean;
begin
DataController.CheckRecordRange(ARecordIndex);
if not IsEditing then
SetEditing;
if IsEditing then
SetChanging;
Result := True;
end;
function TcxCustomDataProvider.CanModify: Boolean;
begin
Result := True;
end;
procedure TcxCustomDataProvider.Append;
begin
InsertingRecord(True);
end;
procedure TcxCustomDataProvider.Cancel;
var
ARecordIndex: Integer;
begin
DataController.DoBeforeCancel;
DataController.BeginUpdate;
try
if (FEditingRecordIndex <> cxNullEditingRecordIndex) and
((FEditingRecordIndex < DataController.RecordCount) or
(FEditingRecordIndex = DataController.FNewItemRecordIndex)) then
begin
if IsInserting then
begin
DataController.FInSmartLoad := DataController.IsSmartLoad;
try
ARecordIndex := DataController.GetFocusedRecordIndex;
DataController.DeleteRecord(FEditingRecordIndex);
if not DataController.FInCancel and
(ARecordIndex >= DataController.RecordCount) then
DataController.ChangeFocusedRecordIndex(ARecordIndex - 1);
finally
DataController.FInSmartLoad := False;
end;
end
else
if FSavedRecordIndex < 0 then
begin
DataController.CopyRecord(FSavedRecordIndex, FEditingRecordIndex);
end;
end;
ResetEditing;
finally
DataController.EndUpdate;
end;
DataController.DoAfterCancel;
end;
procedure TcxCustomDataProvider.DoUpdateData;
begin
if FInUpdateData then Exit;
FInUpdateData := True;
try
if IsChanging then
DataController.UpdateData;
finally
FInUpdateData := False;
end;
end;
procedure TcxCustomDataProvider.Delete;
var
ARecordIndex: Integer;
begin
ARecordIndex := DataController.DataControllerInfo.FocusedRecordIndex;
if ARecordIndex <> -1 then
begin
DataController.DoBeforeDelete(ARecordIndex);
if DataController.IsProviderMode then
DataController.SaveKeys;
try
DataController.DeleteRecord(ARecordIndex);
DataController.CheckNearestFocusRow;
finally
if DataController.IsProviderMode then
DataController.RestoreKeys;
end;
DataController.DoAfterDelete;
end;
end;
procedure TcxCustomDataProvider.DeleteRecords(AList: TList);
var
I, ARecordIndex: Integer;
begin
if DataController.IsProviderMode then
DataController.SaveKeys;
try
DataController.BeginFullUpdate;
try
for I := AList.Count - 1 downto 0 do
begin
ARecordIndex := Integer(AList[I]);
DataController.DoBeforeDelete(ARecordIndex);
DataController.DeleteRecord(ARecordIndex);
DataController.DoAfterDelete;
end;
if DataController.FInDeleteSelection then
DataController.ClearSelection;
finally
DataController.EndFullUpdate;
end;
// if DataController.LockCount = 0 then
DataController.CheckNearestFocusRow;
finally
if DataController.IsProviderMode then
DataController.RestoreKeys;
end;
end;
procedure TcxCustomDataProvider.DeleteSelection;
var
AList: TList;
I, ARowIndex: Integer;
ARowInfo: TcxRowInfo;
begin
AList := TList.Create;
try
for I := 0 to DataController.GetSelectedCount - 1 do
begin
ARowIndex := DataController.GetSelectedRowIndex(I);
ARowInfo := DataController.GetRowInfo(ARowIndex);
if ARowInfo.Level < DataController.Groups.LevelCount then // It's Group Row
DataController.Groups.LoadRecordIndexesByRowIndex(AList, ARowIndex)
else
AList.Add(Pointer(ARowInfo.RecordIndex));
end;
// Delete Records
DataController.FInDeleteSelection := True;
try
DataController.DeleteRecords(AList);
finally
DataController.FInDeleteSelection := False;
end;
// DataController.ClearSelection;
finally
AList.Free;
end;
end;
procedure TcxCustomDataProvider.Edit;
begin
SetEditing;
end;
function TcxCustomDataProvider.GetEditValue(ARecordIndex: Integer; AField: TcxCustomDataField;
AEditValueSource: TcxDataEditValueSource): Variant;
begin
if ARecordIndex <> cxNullEditingRecordIndex then
Result := DataController.GetInternalValue(ARecordIndex, AField)
else
begin
Result := Null;
InvalidOperation(cxSDataRecordIndexError);
end;
end;
procedure TcxCustomDataProvider.Insert;
begin
InsertingRecord(False);
end;
procedure TcxCustomDataProvider.Post(AForcePost: Boolean);
var
ARecordIndex: Integer;
APostFlag: Boolean;
begin
DataController.BeginFullUpdate;
try
ARecordIndex := -1;
APostFlag := False;
DoUpdateData;
if IsInserting and not IsModified and not AForcePost then
Cancel
else
begin
APostFlag := True;
DataController.DoBeforePost;
ARecordIndex := FEditingRecordIndex;
if IsInserting and DataController.UseNewItemRowForEditing and
(FEditingRecordIndex <> cxNullEditingRecordIndex) then
begin
FDataChangedLocked := CustomDataSource <> nil;
try
FInsertedRecordIndex := DataController.AppendRecord;
ARecordIndex := FInsertedRecordIndex;
finally
FDataChangedLocked := False;
end;
DataController.CopyRecord(FEditingRecordIndex, FInsertedRecordIndex);
end;
ResetEditing;
end;
DataController.DataChanged(dcRecord, -1, ARecordIndex);
if APostFlag then
DataController.DoAfterPost;
finally
DataController.EndFullUpdate;
end;
end;
procedure TcxCustomDataProvider.PostEditingData;
begin
DoUpdateData;
ResetChanging;
end;
function TcxCustomDataProvider.SetEditValue(ARecordIndex: Integer; AField: TcxCustomDataField;
const AValue: Variant; AEditValueSource: TcxDataEditValueSource): Boolean;
begin
if (AEditValueSource = evsText) and (VarToStr(AValue) = '') then
DataController.SetValue(ARecordIndex, AField.Index, Null)
else
DataController.SetValue(ARecordIndex, AField.Index, AValue);
SetModified;
Result := True;
end;
procedure TcxCustomDataProvider.BeginDeleting;
var
ARowIndex: Integer;
begin
if not IsGridMode and (DataController.FocusedRowIndex <> -1) then
begin
ARowIndex := DataController.FocusedRowIndex;
if not DataController.MultiSelect or
(DataController.IsRowSelected(ARowIndex) or (DataController.GetSelectedCount = 0)) then
ARowIndex := DataController.GetNearestRowIndex(ARowIndex);
if ARowIndex <> -1 then
DataController.FNearestRecordIndex := DataController.GetRowInfo(ARowIndex).RecordIndex;
end;
end;
procedure TcxCustomDataProvider.EndDeleting;
begin
DataController.FNearestRecordIndex := -1;
end;
procedure TcxCustomDataProvider.AssignItemValue(ARecordIndex: Integer;
AField: TcxCustomDataField; const AValue: Variant);
begin
DataController.Values[ARecordIndex, AField.Index] := AValue;
end;
procedure TcxCustomDataProvider.ClearSavedRecord;
begin
if FSavedRecordIndex < 0 then
begin
FDataController.DataStorage.DeleteRecord(FSavedRecordIndex);
FSavedRecordIndex := 0;
end;
end;
procedure TcxCustomDataProvider.CreateSavedRecord(ARecordIndex: Integer);
begin
if FSavedRecordIndex = 0 then
FSavedRecordIndex := FDataController.DataStorage.AddInternalRecord;
FDataController.CopyRecord(ARecordIndex, FSavedRecordIndex);
end;
procedure TcxCustomDataProvider.DoInitInsertingRecord(AInsertingRecordIndex: Integer; const AGroupValues: TcxDataSummaryValues);
var
I: Integer;
begin
if (DataController.Groups.GroupingItemCount > 0) and
(dcoAssignGroupingValues in DataController.Options) then
begin
for I := 0 to DataController.Groups.GroupingItemCount - 1 do
AssignItemValue(AInsertingRecordIndex,
DataController.Fields[DataController.Groups.GroupingItemIndex[I]],
AGroupValues[I]);
end;
DataController.DoNewRecord(AInsertingRecordIndex);
end;
procedure TcxCustomDataProvider.EditingRecord;
var
ARowIndex: Integer;
begin
if DataController.UseNewItemRowForEditing and DataController.NewItemRowFocused and
not IsInserting then
begin
Insert;
Exit;
end;
FEditingRecordIndex := DataController.CalcEditingRecordIndex;
if FEditingRecordIndex >= 0 then
begin
FEditingRecordIndex1 := cxNullEditingRecordIndex;
FEditingRecordIndex2 := cxNullEditingRecordIndex;
ARowIndex := DataController.GetFocusedRowIndex;
if ARowIndex <> -1 then
begin
if ARowIndex < (DataController.GetRowCount - 1) then
begin
ARowIndex := ARowIndex + 1;
FEditingRecordIndex2 := DataController.GetRowInfo(ARowIndex).RecordIndex;
end
else
begin
ARowIndex := ARowIndex - 1;
if 0 <= ARowIndex then
FEditingRecordIndex1 := DataController.GetRowInfo(ARowIndex).RecordIndex
else
FEditingRecordIndex1 := DataController.GetRowInfo(DataController.GetFocusedRowIndex).RecordIndex;
end;
end;
end;
if FEditingRecordIndex = cxNullEditingRecordIndex then Exit;
if (not IsDataSource or DataController.IsSmartRefresh) and not IsInserting then // !!!
CreateSavedRecord(FEditingRecordIndex);
DataController.Change([dccUpdateRecord]);
end;
procedure TcxCustomDataProvider.InsertingRecord(AIsAppending: Boolean);
var
ARowIndex, ARecordIndex: Integer;
AGroupValues: TcxDataSummaryValues;
begin
// TODO: proc
if not IsDataSource and IsInserting and not IsChanging and not IsModified then Exit;
if IsEditing and (IsChanging or IsModified) and not IsDataSource then
begin
if IsChanging then
PostEditingData;
Post;
end;
//
DataController.DoBeforeInsert;
DataController.BeginUpdate;
try
if DataController.UseNewItemRowForEditing then
begin
ARecordIndex := DataController.GetFocusedRecordIndex;
if (ARecordIndex < 0) and (DataController.GetRowCount > 0) then
ARecordIndex := DataController.GetRowInfo(0).RecordIndex;
FEditingRecordIndex := DataController.NewItemRecordIndex;
DataController.NewItemRowFocused := True;
DataController.GetGroupValues(ARecordIndex, AGroupValues);
end
else
begin
if not AIsAppending then
DataController.ClearSelection; // TODO: ?
if IsGridMode then
begin
ARecordIndex := DataController.GetActiveRecordIndex; //DataController.GetFocusedRecordIndex;
DataController.GetGroupValues(ARecordIndex, AGroupValues);
FEditingRecordIndex := ARecordIndex;
end
else
begin
if AIsAppending then
begin
ARecordIndex := DataController.GetLastRecordIndex;
DataController.GetGroupValues(ARecordIndex, AGroupValues);
FEditingRecordIndex := DataController.AppendRecord;
FEditingRecordIndex1 := ARecordIndex;
end
else
begin
ARowIndex := DataController.GetFocusedRowIndex;
if ARowIndex <> -1 then
ARecordIndex := DataController.GetRowInfo(ARowIndex).RecordIndex
else
ARecordIndex := 0;
DataController.GetGroupValues(ARecordIndex, AGroupValues);
FEditingRecordIndex := DataController.InsertRecord(ARecordIndex);
FEditingRecordIndex2 := ARecordIndex;
if (FEditingRecordIndex <= ARecordIndex) and
(FEditingRecordIndex2 < DataController.RecordCount - 1) then
Inc(FEditingRecordIndex2);
end;
end;
end;
DoInitInsertingRecord(FEditingRecordIndex, AGroupValues);
ResetChanging;
FInserting := True;
finally
DataController.EndUpdate;
end;
DataController.ChangeFocusedRecordIndex(FEditingRecordIndex);
DataController.DoAfterInsert;
end;
procedure TcxCustomDataProvider.ResetChanging;
begin
FChanging := False;
end;
procedure TcxCustomDataProvider.ResetEditing;
begin
if (FEditingRecordIndex <> cxNullEditingRecordIndex) and
(FEditingRecordIndex = DataController.FNewItemRecordIndex) and
(DataController.FNewItemRecordIndex < 0{is real new item row}) then
FDataController.DataStorage.DeleteRecord(FEditingRecordIndex);
FEditingRecordIndex := cxNullEditingRecordIndex;
FEditingRecordIndex1 := cxNullEditingRecordIndex;
FEditingRecordIndex2 := cxNullEditingRecordIndex;
FInserting := False;
ResetChanging;
ResetModified;
ClearSavedRecord;
end;
procedure TcxCustomDataProvider.ResetModified;
begin
FModified := False;
end;
procedure TcxCustomDataProvider.SetChanging;
begin
FChanging := True;
end;
procedure TcxCustomDataProvider.SetEditing;
begin
EditingRecord;
end;
procedure TcxCustomDataProvider.SetModified;
begin
FModified := True;
end;
// Lock Notify
procedure TcxCustomDataProvider.BeginLocate;
begin
Inc(FLocateCount);
end;
procedure TcxCustomDataProvider.EndLocate;
begin
Dec(FLocateCount);
end;
procedure TcxCustomDataProvider.Freeze;
begin
Inc(FLockCount);
end;
procedure TcxCustomDataProvider.Unfreeze;
begin
Dec(FLockCount);
end;
// Data
function TcxCustomDataProvider.AddRecordHandle(AData: Pointer): Integer;
begin
Result := -1;
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
Result := CustomDataSource.AddRecordHandle(AData);
end;
end;
function TcxCustomDataProvider.AppendRecord: Integer;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
Result := CustomDataSource.GetRecordIndexByHandle(CustomDataSource.AppendRecord);
end
else
Result := -1;
end;
procedure TcxCustomDataProvider.DeleteRecord(ARecordIndex: Integer);
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
CustomDataSource.DeleteRecord(CustomDataSource.GetRecordHandleByIndex(ARecordIndex));
end;
end;
function TcxCustomDataProvider.GetDetailHasChildren(ARecordIndex, ARelationIndex: Integer): Boolean;
begin
Result := False;
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
Result := CustomDataSource.GetDetailHasChildren(ARecordIndex, ARelationIndex);
end;
end;
function TcxCustomDataProvider.GetDisplayText(ARecordIndex: Integer; AField: TcxCustomDataField): string;
var
ARecordHandle: TcxDataRecordHandle;
AItemHandle: TcxDataItemHandle;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
ARecordHandle := CustomDataSource.GetRecordHandleByIndex(ARecordIndex);
AItemHandle := CustomDataSource.GetItemHandle(AField.Index);
Result := CustomDataSource.GetDisplayText(ARecordHandle, AItemHandle);
end
else
Result := '';
end;
{function TcxCustomDataProvider.GetRecordCount: Integer;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
Result := CustomDataSource.GetRecordCount;
end
else
Result := 0;
end;}
function TcxCustomDataProvider.GetExternalDataDisplayText(ARecordIndex: Integer;
AField: TcxCustomDataField): string;
begin
Result := '';
end;
function TcxCustomDataProvider.GetExternalDataValue(ARecordIndex: Integer;
AField: TcxCustomDataField): Variant;
begin
Result := Null;
end;
function TcxCustomDataProvider.GetRecordId(ARecordIndex: Integer): Variant;
var
ARecordHandle: TcxDataRecordHandle;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
ARecordHandle := CustomDataSource.GetRecordHandleByIndex(ARecordIndex);
Result := CustomDataSource.GetRecordId(ARecordHandle);
end
else
Result := Null;
end;
function TcxCustomDataProvider.GetRecordIndex: Integer;
begin
Result := -1;
end;
function TcxCustomDataProvider.GetValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant;
var
ARecordHandle: TcxDataRecordHandle;
AItemHandle: TcxDataItemHandle;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
ARecordHandle := CustomDataSource.GetRecordHandleByIndex(ARecordIndex);
AItemHandle := CustomDataSource.GetItemHandle(AField.Index);
Result := CustomDataSource.GetValue(ARecordHandle, AItemHandle);
end
else
Result := Null;
end;
function TcxCustomDataProvider.GetValueDefReaderClass: TcxValueDefReaderClass;
begin
Result := TcxValueDefReader;
end;
function TcxCustomDataProvider.InsertRecord(ARecordIndex: Integer): Integer;
var
ARecordHandle: TcxDataRecordHandle;
begin
Result := -1;
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
if CustomDataSource.GetRecordCount = 0 then
ARecordHandle := CustomDataSource.AppendRecord
else
ARecordHandle := CustomDataSource.InsertRecord(CustomDataSource.GetRecordHandleByIndex(ARecordIndex));
Result := CustomDataSource.GetRecordIndexByHandle(ARecordHandle);
end;
end;
procedure TcxCustomDataProvider.LoadDataBuffer;
begin
end;
procedure TcxCustomDataProvider.LoadRecordHandles;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
CustomDataSource.LoadRecordHandles;
end;
end;
function TcxCustomDataProvider.NativeCompare(ARecordIndex1, ARecordIndex2: Integer;
AField: TcxCustomDataField): Integer;
procedure GetCompareInfo(ARecordIndex: Integer; var IsNull: Boolean; var P: PAnsiChar);
var
ARecordHandle: TcxDataRecordHandle;
AItemHandle: TcxDataItemHandle;
begin
if ARecordIndex <= -1 then // internal record
IsNull := DataController.DataStorage.GetCompareInfo(ARecordIndex, AField.ValueDef, P)
else
begin
ARecordHandle := CustomDataSource.GetRecordHandleByIndex(ARecordIndex);
AItemHandle := CustomDataSource.GetItemHandle(AField.Index);
IsNull := not CustomDataSource.GetInfoForCompare(ARecordHandle, AItemHandle, P);
end;
end;
var
AIsNull1, AIsNull2: Boolean;
P1, P2: PAnsiChar;
begin
if (ARecordIndex1 <= -1) and (ARecordIndex2 <= -1) then
Result := DataController.DataStorage.CompareRecords(ARecordIndex1, ARecordIndex2, AField.ValueDef)
else
begin
CustomDataSource.CurrentProvider := Self;
GetCompareInfo(ARecordIndex1, AIsNull1, P1);
GetCompareInfo(ARecordIndex2, AIsNull2, P2);
Result := AField.ValueDef.CompareValues(AIsNull1, AIsNull2, P1, P2);
end;
end;
function TcxCustomDataProvider.NativeCompareFunc(ARecordIndex1, ARecordIndex2: Integer;
AField: TcxCustomDataField): Integer;
begin
CustomDataSource.CurrentProvider := Self;
with CustomDataSource do
Result := NativeCompareFunc(GetRecordHandleByIndex(ARecordIndex1),
GetRecordHandleByIndex(ARecordIndex2), GetItemHandle(AField.Index));
end;
procedure TcxCustomDataProvider.SetDisplayText(ARecordIndex: Integer;
AField: TcxCustomDataField; const Value: string);
begin
end;
procedure TcxCustomDataProvider.SetRecordCount(ARecordCount: Integer);
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
CustomDataSource.SetRecordCount(ARecordCount);
end;
end;
procedure TcxCustomDataProvider.SetValue(ARecordIndex: Integer;
AField: TcxCustomDataField; const Value: Variant);
var
ARecordHandle: TcxDataRecordHandle;
AItemHandle: TcxDataItemHandle;
begin
if CustomDataSource <> nil then
begin
CustomDataSource.CurrentProvider := Self;
ARecordHandle := CustomDataSource.GetRecordHandleByIndex(ARecordIndex);
AItemHandle := CustomDataSource.GetItemHandle(AField.Index);
CustomDataSource.SetValue(ARecordHandle, AItemHandle, Value);
end;
end;
// Notification
procedure TcxCustomDataProvider.ActiveChanged(AActive: Boolean);
begin
DataController.ActiveChanged(AActive);
ResetEditing;
end;
procedure TcxCustomDataProvider.DataChanged(ADataChange: TcxDataChange;
AItemIndex, ARecordIndex: Integer);
begin
if (LockCount = 0) {and (LocateCount = 0) }then
DataController.DataChanged(ADataChange, AItemIndex, ARecordIndex);
if FDataChangedLocked then
ResetChanging
else
ResetEditing;
end;
procedure TcxCustomDataProvider.DataScrolled(ADistance: Integer);
begin
DataController.DataScrolled(ADistance);
ResetChanging;
end;
procedure TcxCustomDataProvider.LayoutChanged(ADataLayoutChanges: TcxDataLayoutChanges);
begin
DataController.LayoutChanged(ADataLayoutChanges);
end;
procedure TcxCustomDataProvider.SetCustomDataSource(Value: TcxCustomDataSource);
begin
if not IsCustomDataSourceSupported then Exit;
if FCustomDataSource <> Value then
begin
if FCustomDataSource <> nil then
FCustomDataSource.RemoveProvider(Self);
FCustomDataSource := Value;
if FCustomDataSource <> nil then
FCustomDataSource.AddProvider(Self);
DataController.FInSetCustomDataSource := True;
try
if not DataController.FDestroying then
DataController.RestructData;
finally
DataController.FInSetCustomDataSource := False;
end;
end;
end;
{ TcxCustomDataField }
constructor TcxCustomDataField.Create(AFieldList: TcxCustomDataFieldList);
begin
inherited Create;
FFieldList := AFieldList;
end;
destructor TcxCustomDataField.Destroy;
begin
FieldList.RemoveField(Self);
ClearData;
inherited Destroy;
end;
function TcxCustomDataField.CanModify(AEditValueSource: TcxDataEditValueSource): Boolean;
begin
Result := Assigned(ValueDef)
or DataController.IsProviderMode {don't support cancel for ValueDef = nil};
end;
function TcxCustomDataField.IsUnbound: Boolean;
begin
Result := False;
end;
function TcxCustomDataField.IsValueDefInternal: Boolean;
begin
Result := IsInternal;
end;
procedure TcxCustomDataField.BeginRecreateData;
begin
if FLockRecreateData = 0 then
ClearData;
Inc(FLockRecreateData);
end;
procedure TcxCustomDataField.Changed;
begin
if (FieldList.DataController.Provider.LockCount = 0) then
FieldList.DataController.Provider.LayoutChanged([lcStructure]);
end;
procedure TcxCustomDataField.ClearData;
begin
if Assigned(FReferenceField) then Exit;
if Assigned(FValueDef) then
begin
FValueDef.Free;
FValueDef := nil;
end;
end;
procedure TcxCustomDataField.CreateData;
var
ADataStorage: TcxDataStorage;
begin
if Assigned(FReferenceField) or (FValueTypeClass = nil) then Exit;
ClearData;
ADataStorage := FieldList.DataController.DataStorage;
FValueDef := ADataStorage.ValueDefs.Add(FValueTypeClass, IsInternal, TextStored, Self);
end;
procedure TcxCustomDataField.EndRecreateData;
begin
Dec(FLockRecreateData);
if FLockRecreateData = 0 then
CreateData;
end;
procedure TcxCustomDataField.Reassign(ASource: TcxCustomDataField);
begin
FReferenceField := nil;
FValueDef := ASource.FValueDef;
if FValueDef <> nil then
FValueDef.LinkObject := Self;
FValueTypeClass := ASource.FValueTypeClass;
ASource.FValueDef := nil;
ASource.FValueTypeClass := nil;
end;
procedure TcxCustomDataField.RemoveNotification(AComponent: TComponent);
begin
end;
function TcxCustomDataField.GetDataController: TcxCustomDataController;
begin
Result := FieldList.DataController;
end;
function TcxCustomDataField.GetIndex: Integer;
begin
Result := FieldList.FItems.IndexOf(Self);
end;
function TcxCustomDataField.GetNotifier: TComponent;
begin
Result := FieldList.DataController.Notifier;
end;
function TcxCustomDataField.GetTextStored: Boolean;
begin
if Assigned(FReferenceField) then
Result := FReferenceField.TextStored
else
Result := FTextStored;
end;
function TcxCustomDataField.GetValueDef: TcxValueDef;
begin
if Assigned(FReferenceField) then
Result := FReferenceField.ValueDef
else
Result := FValueDef;
end;
function TcxCustomDataField.GetValueTypeClass: TcxValueTypeClass;
begin
if Assigned(FReferenceField) then
Result := FReferenceField.ValueTypeClass
else
Result := FValueTypeClass;
end;
procedure TcxCustomDataField.SetIndex(Value: Integer);
var
ACurIndex: Integer;
begin
ACurIndex := Index;
if ACurIndex <> Value then
FieldList.FItems.Move(ACurIndex, Value);
end;
procedure TcxCustomDataField.SetIsInternal(Value: Boolean);
begin
if FIsInternal <> Value then
begin
FIsInternal := Value;
FieldList.Update;
end;
end;
procedure TcxCustomDataField.SetReferenceField(Value: TcxCustomDataField);
begin
if FReferenceField <> Value then
begin
if Value <> nil then
begin
while Value.FReferenceField <> nil do
Value := Value.FReferenceField;
end;
if Value <> Self then
begin
if FReferenceField = nil then
ValueTypeClass := nil;
FReferenceField := Value;
end;
end;
end;
procedure TcxCustomDataField.SetTextStored(Value: Boolean);
begin
if Assigned(FReferenceField) then Exit;
if FTextStored <> Value then
begin
BeginRecreateData;
try
FTextStored := Value;
finally
EndRecreateData;
end;
end;
end;
procedure TcxCustomDataField.SetValueTypeClass(Value: TcxValueTypeClass);
begin
if Assigned(FReferenceField) then Exit;
if FValueTypeClass <> Value then
begin
BeginRecreateData;
try
FValueTypeClass := Value;
finally
EndRecreateData;
end;
end;
end;
{ TcxCustomDataFieldList }
constructor TcxCustomDataFieldList.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FItems := TList.Create;
FDataController := ADataController;
end;
destructor TcxCustomDataFieldList.Destroy;
begin
Clear;
FItems.Free;
inherited Destroy;
end;
function TcxCustomDataFieldList.FieldByItem(AItem: TObject): TcxCustomDataField;
var
I: Integer;
begin
Result := nil;
for I := 0 to Count - 1 do
if Items[I].Item = AItem then
begin
Result := Items[I];
Break;
end;
end;
procedure TcxCustomDataFieldList.ReassignFields(ADestroyedField: TcxCustomDataField);
var
I: Integer;
ACurrentField, ANewReferenceField: TcxCustomDataField;
begin
ANewReferenceField := nil;
for I := 0 to Count - 1 do
begin
ACurrentField := Items[I];
if ACurrentField.ReferenceField = ADestroyedField then
begin
if ANewReferenceField = nil then
begin
ANewReferenceField := ACurrentField;
ACurrentField.Reassign(ADestroyedField);
end
else
ACurrentField.ReferenceField := ANewReferenceField;
end;
end;
end;
procedure TcxCustomDataFieldList.Add(AField: TcxCustomDataField);
begin
if not AField.IsInternal then
FItems.Insert(ItemCount, AField) // Before Internal Fields
else
FItems.Add(AField);
Update;
end;
procedure TcxCustomDataFieldList.Clear;
begin
while FItems.Count > 0 do
TcxCustomDataField(FItems.Last).Free;
end;
procedure TcxCustomDataFieldList.RemoveField(AField: TcxCustomDataField);
var
ASavedField: TcxCustomDataField;
begin
FFieldDestroyed := True;
ASavedField := AField;
// DataController.RemoveField(AField);
ReassignFields(AField);
FItems.Remove(AField);
DataController.RemoveField(ASavedField);
Update;
end;
procedure TcxCustomDataFieldList.RemoveNotification(AComponent: TComponent);
var
I: Integer;
begin
I := 0;
while I < Count do
begin
Items[I].RemoveNotification(AComponent);
Inc(I);
end;
end;
procedure TcxCustomDataFieldList.Update;
var
I: Integer;
begin
FInternalCount := 0;
for I := 0 to Count - 1 do
if Items[I].IsInternal then
Inc(FInternalCount);
end;
function TcxCustomDataFieldList.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TcxCustomDataFieldList.GetItem(Index: Integer): TcxCustomDataField;
begin
Result := TcxCustomDataField(FItems[Index]);
end;
function TcxCustomDataFieldList.GetItemCount: Integer;
begin
Result := FItems.Count - FInternalCount;
end;
{ TcxDetailInfoObject }
destructor TcxDetailInfoObject.Destroy;
begin
LinkObject.Free;
inherited Destroy;;
end;
{ TcxDetailObject }
constructor TcxDetailObject.Create;
begin
inherited Create;
FActiveRelationIndex := -1;
FInfoObjects := TList.Create;
end;
destructor TcxDetailObject.Destroy;
begin
Clear;
FInfoObjects.Free;
inherited Destroy;
end;
procedure TcxDetailObject.Clear;
var
I: Integer;
AObject: TObject;
begin
for I := 0 to FInfoObjects.Count - 1 do
begin
AObject := TObject(FInfoObjects[I]);
FInfoObjects[I] := nil;
AObject.Free;
end;
FInfoObjects.Clear;
end;
function TcxDetailObject.ClearHasChildrenFlag: Boolean;
var
I: Integer;
AInfoObject: TcxDetailInfoObject;
begin
Result := False;
for I := 0 to FInfoObjects.Count - 1 do
begin
AInfoObject := TcxDetailInfoObject(FInfoObjects[I]);
if AInfoObject <> nil then
begin
if not Result and AInfoObject.HasChildrenAssigned then
Result := True;
AInfoObject.HasChildrenAssigned := False;
end;
end;
end;
procedure TcxDetailObject.ClearInfoObject(AIndex: Integer);
begin
TObject(FInfoObjects[AIndex]).Free;
FInfoObjects[AIndex] := nil;
end;
procedure TcxDetailObject.CorrectCount(ARelations: TcxCustomDataRelationList);
var
I: Integer;
begin
if FInfoObjects.Count < ARelations.Count then
ListSetCount(FInfoObjects, ARelations.Count);
if FInfoObjects.Count > 0 then
begin
if FActiveRelationIndex < 0 then
FActiveRelationIndex := 0;
if FActiveRelationIndex >= ARelations.Count then // !!!
FActiveRelationIndex := ARelations.Count - 1;
// Check Hidden Levels
if ARelations[FActiveRelationIndex].DetailDataController = nil then
for I := 0 to ARelations.Count - 1 do
if ARelations[I].DetailDataController <> nil then
begin
FActiveRelationIndex := I;
Break;
end;
end
else
FActiveRelationIndex := -1;
end;
function TcxDetailObject.GetInfoObject(Index: Integer): TcxDetailInfoObject;
begin
if (0 <= Index) and (Index < FInfoObjects.Count) then
Result := TcxDetailInfoObject(FInfoObjects[Index])
else
Result := nil;
end;
function TcxDetailObject.GetInfoObjectCount: Integer;
begin
if FInfoObjects <> nil then
Result := FInfoObjects.Count
else
Result := 0;
end;
function TcxDetailObject.GetIsEmpty: Boolean;
var
I: Integer;
begin
Result := True;
for I := 0 to LinkObjectCount - 1 do
if LinkObjects[I] <> nil then
begin
Result := False;
Break;
end;
end;
function TcxDetailObject.GetLinkObject(Index: Integer): TObject;
var
AInfoObject: TcxDetailInfoObject;
begin
Result := nil;
if (0 <= Index) and (Index < InfoObjectCount) then
begin
AInfoObject := InfoObjects[Index];
if AInfoObject <> nil then
Result := AInfoObject.LinkObject;
end;
end;
function TcxDetailObject.GetLinkObjectCount: Integer;
begin
Result := InfoObjectCount;
end;
procedure TcxDetailObject.SetInfoObject(Index: Integer; Value: TcxDetailInfoObject);
begin
if Index >= FInfoObjects.Count then
ListSetCount(FInfoObjects, Index + 1);
FInfoObjects[Index] := Value;
end;
procedure TcxDetailObject.SetLinkObject(Index: Integer; Value: TObject);
begin
if Index >= FInfoObjects.Count then
ListSetCount(FInfoObjects, Index + 1);
if FInfoObjects[Index] = nil then
FInfoObjects[Index] := TcxDetailInfoObject.Create;
TcxDetailInfoObject(FInfoObjects[Index]).LinkObject := Value;
end;
{ TcxCustomDataRelation }
constructor TcxCustomDataRelation.Create(ARelationList: TcxCustomDataRelationList;
AItem: TObject);
begin
inherited Create;
FItem := AItem;
ARelationList.AddItem(Self);
end;
destructor TcxCustomDataRelation.Destroy;
begin
if FDetailDataController <> nil then
begin
FDetailDataController.SetMasterMode(nil, FDetailDataController.IsPattern);
FDetailDataController := nil;
end;
FRelationList.RemoveItem(Self);
inherited Destroy;
end;
procedure TcxCustomDataRelation.Assign(ASource: TcxCustomDataRelation);
begin
end;
procedure TcxCustomDataRelation.Changed;
begin
FRelationList.Changed(Self);
end;
procedure TcxCustomDataRelation.RemoveDataField(ADataField: TcxCustomDataField);
begin
end;
function TcxCustomDataRelation.GetDataController: TcxCustomDataController;
begin
Result := FRelationList.DataController;
end;
function TcxCustomDataRelation.GetIndex: Integer;
begin
Result := FRelationList.FItems.IndexOf(Self);
end;
{ TcxCustomDataRelationList }
constructor TcxCustomDataRelationList.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FItems := TList.Create;
FDataController := ADataController;
end;
destructor TcxCustomDataRelationList.Destroy;
begin
Clear;
FItems.Free;
inherited Destroy;
end;
function TcxCustomDataRelationList.Add(AItem: TObject): TcxCustomDataRelation;
begin
Result := DataController.GetRelationClass.Create(Self, AItem);
end;
procedure TcxCustomDataRelationList.Assign(ASource: TcxCustomDataRelationList);
var
I: Integer;
begin
BeginUpdate;
try
Clear;
for I := 0 to ASource.Count - 1 do
Add(ASource[I].Item).Assign(ASource[I]);
finally
EndUpdate;
end;
end;
procedure TcxCustomDataRelationList.BeginUpdate;
begin
Inc(FLockCount);
end;
procedure TcxCustomDataRelationList.Clear;
var
I: Integer;
begin
BeginUpdate;
try
for I := Count - 1 downto 0 do
Items[I].Free;
finally
EndUpdate;
end;
end;
function TcxCustomDataRelationList.ClearDetailObject(ARecordIndex, ARelationIndex: Integer): Boolean;
var
ADetailObject: TcxDetailObject;
begin
Result := False;
DataController.CheckRecordRange(ARecordIndex);
if Assigned(FDataField) then
begin
ADetailObject := GetValueAsDetailObject(ARecordIndex);
if Assigned(ADetailObject) then
begin
if ARelationIndex = -1 then
ADetailObject.Clear
else
ADetailObject.ClearInfoObject(ARelationIndex);
Result := True;
end;
end;
end;
procedure TcxCustomDataRelationList.EndUpdate;
begin
Dec(FLockCount);
Changed(nil);
end;
function TcxCustomDataRelationList.FindByItem(AItem: TObject): TcxCustomDataRelation;
var
I: Integer;
begin
Result := nil;
for I := 0 to Count - 1 do
begin
if Items[I].Item = AItem then
begin
Result := Items[I];
Break;
end;
end;
end;
function TcxCustomDataRelationList.GetDetailObject(ARecordIndex: Integer): TcxDetailObject;
procedure ResetMasterRelations(ADetailObject: TcxDetailObject);
var
I: Integer;
ADetailLinkObject: TObject;
begin
for I := 0 to ADetailObject.LinkObjectCount - 1 do
begin
ADetailLinkObject := ADetailObject.LinkObjects[I];
if ADetailLinkObject <> nil then
DataController.GetDetailDataControllerByLinkObject(ADetailLinkObject).FMasterRelation := nil;
end;
end;
begin
DataController.CheckRecordRange(ARecordIndex);
if Assigned(FDataField) then
begin
Result := GetValueAsDetailObject(ARecordIndex);
if IsEmpty then
begin
if Result <> nil then
begin
ResetMasterRelations(Result);
Result.Free;
Result := nil;
DataController.SetStoredValue(ARecordIndex, FDataField,
Integer(Result));
end;
Exit;
end;
if Result = nil then
begin
Result := TcxDetailObject.Create;
Result.ActiveRelationIndex := DataController.GetDefaultActiveRelationIndex;
DataController.SetStoredValue(ARecordIndex, FDataField,
Integer(Result));
end;
Result.CorrectCount(Self);
end
else
Result := nil;
end;
function TcxCustomDataRelationList.IsDetailObjectExist(ARecordIndex, ARelationIndex: Integer): Boolean;
var
ADetailObject: TcxDetailObject;
begin
DataController.CheckRecordRange(ARecordIndex);
Result := False;
if Assigned(FDataField) then
begin
ADetailObject := GetValueAsDetailObject(ARecordIndex);
Result := Assigned(ADetailObject) and
(((ARelationIndex = -1) and not ADetailObject.IsEmpty) or
((ARelationIndex <> -1) and (ADetailObject.LinkObjects[ARelationIndex] <> nil)));
end;
end;
function TcxCustomDataRelationList.IsEmpty: Boolean;
var
APatternRelations: TcxCustomDataRelationList;
I: Integer;
begin
Result := True;
if DataController.GetPatternDataController <> nil then
begin
APatternRelations := DataController.GetPatternDataController.Relations;
for I := 0 to APatternRelations.Count - 1 do
if APatternRelations[I].DetailDataController <> nil then
begin
Result := False;
Break;
end;
end;
end;
procedure TcxCustomDataRelationList.Move(ACurIndex, ANewIndex: Integer);
var
ARelation: TcxCustomDataRelation;
begin
if ACurIndex <> ANewIndex then
begin
ARelation := TcxCustomDataRelation(FItems[ACurIndex]);
FItems.Delete(ACurIndex);
FItems.Insert(ANewIndex, ARelation);
Changed(nil);
end;
end;
procedure TcxCustomDataRelationList.RemoveByItem(AItem: TObject);
var
ARelation: TcxCustomDataRelation;
begin
ARelation := FindByItem(AItem);
if ARelation <> nil then
ARelation.Free;
end;
procedure TcxCustomDataRelationList.Changed(ARelation: TcxCustomDataRelation);
begin
if LockCount = 0 then
Update(ARelation);
end;
function TcxCustomDataRelationList.GetValueAsDetailObject(ARecordIndex: Integer): TcxDetailObject;
var
AValue: Variant;
begin
AValue := DataController.GetInternalValue(ARecordIndex, FDataField);
if VarIsNull(AValue) then
Result := nil
else
Result := TcxDetailObject(Integer(AValue));
end;
procedure TcxCustomDataRelationList.RemoveDataField(ADataField: TcxCustomDataField);
var
I: Integer;
begin
if ADataField = FDataField then
FDataField := nil;
for I := 0 to Count - 1 do
Items[I].RemoveDataField(ADataField);
end;
procedure TcxCustomDataRelationList.Update(ARelation: TcxCustomDataRelation);
begin
DataController.BeginUpdate;
try
if Count = 0 then
FDataField.Free // FDataField <- nil (in RemoveDataField method)
else
if FDataField <> nil then
begin
DataController.ClearDetails;
end
else
begin
FDataField := DataController.AddInternalField;
FDataField.ValueTypeClass := TcxObjectValueType;
end;
DataController.UpdateRelations(ARelation);
finally
DataController.EndUpdate;
end;
end;
procedure TcxCustomDataRelationList.AddItem(AItem: TcxCustomDataRelation);
begin
FItems.Add(AItem);
AItem.FRelationList := Self;
Changed(AItem);
end;
function TcxCustomDataRelationList.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TcxCustomDataRelationList.GetItem(Index: Integer): TcxCustomDataRelation;
begin
Result := TcxCustomDataRelation(FItems[Index]);
end;
procedure TcxCustomDataRelationList.RemoveItem(AItem: TcxCustomDataRelation);
begin
DataController.ClearDetailsMasterRelation(AItem);
FItems.Remove(AItem);
AItem.FRelationList := nil;
Changed(AItem);
end;
{ TcxSortingFieldList }
constructor TcxSortingFieldList.Create;
begin
inherited Create;
FItems := TList.Create;
end;
destructor TcxSortingFieldList.Destroy;
begin
Clear;
FItems.Free;
inherited Destroy;
end;
procedure TcxSortingFieldList.AppendFrom(AList: TcxSortingFieldList);
var
I: Integer;
begin
for I := 0 to AList.Count - 1 do
if Find(AList[I].Field) = -1 then
Add(AList[I].Field, AList[I].SortOrder);
end;
procedure TcxSortingFieldList.ChangeSorting(AField: TcxCustomDataField; ASortOrder: TcxDataSortOrder);
var
AIndex: Integer;
begin
AIndex := Find(AField);
if AIndex = -1 then
begin
if ASortOrder <> soNone then
Add(AField, ASortOrder);
end
else
if ASortOrder = soNone then
Delete(AIndex)
else
SetSortOrder(AIndex, ASortOrder);
end;
procedure TcxSortingFieldList.CheckField(AField: TcxCustomDataField);
begin
if Find(AField) <> -1 then
Changed;
end;
procedure TcxSortingFieldList.Clear;
begin
while Count > 0 do
Delete(0);
end;
function TcxSortingFieldList.SortIndexByField(AField: TcxCustomDataField): Integer;
begin
Result := Find(AField);
end;
function TcxSortingFieldList.SortOrderByField(AField: TcxCustomDataField): TcxDataSortOrder;
var
AIndex: Integer;
begin
AIndex := SortIndexByField(AField);
if AIndex <> -1 then
Result := Items[AIndex].SortOrder
else
Result := soNone;
end;
procedure TcxSortingFieldList.Add(AField: TcxCustomDataField; ASortOrder: TcxDataSortOrder);
var
PInfo: PcxDataSortInfo;
begin
New(PInfo);
PInfo.Field := AField;
PInfo.SortOrder := ASortOrder;
FItems.Add(PInfo);
Changed;
end;
procedure TcxSortingFieldList.Changed;
begin
if Assigned(FOnChanged) then FOnChanged;
end;
procedure TcxSortingFieldList.Delete(Index: Integer);
begin
Dispose(PcxDataSortInfo(FItems[Index]));
FItems.Delete(Index);
Changed;
end;
function TcxSortingFieldList.Find(AField: TcxCustomDataField): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to Count - 1 do
if Items[I].Field = AField then
begin
Result := I;
Break;
end;
end;
procedure TcxSortingFieldList.Insert(Index: Integer; AField: TcxCustomDataField;
ASortOrder: TcxDataSortOrder);
var
PInfo: PcxDataSortInfo;
begin
New(PInfo);
PInfo.Field := AField;
PInfo.SortOrder := ASortOrder;
FItems.Insert(Index, PInfo);
Changed;
end;
procedure TcxSortingFieldList.Move(ACurIndex, ANewIndex: Integer);
begin
if ACurIndex <> ANewIndex then
begin
FItems.Move(ACurIndex, ANewIndex);
Changed;
end;
end;
procedure TcxSortingFieldList.Remove(AField: TcxCustomDataField);
var
AIndex: Integer;
begin
AIndex := Find(AField);
if AIndex <> -1 then
Delete(AIndex);
end;
procedure TcxSortingFieldList.SetSortOrder(Index: Integer; ASortOrder: TcxDataSortOrder);
begin
if PcxDataSortInfo(FItems[Index]).SortOrder <> ASortOrder then
begin
PcxDataSortInfo(FItems[Index]).SortOrder := ASortOrder;
Changed;
end;
end;
function TcxSortingFieldList.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TcxSortingFieldList.GetItem(Index: Integer): TcxDataSortInfo;
begin
Result := PcxDataSortInfo(FItems[Index])^;
end;
{ TcxGroupingFieldList }
procedure TcxGroupingFieldList.ChangeGrouping(AField: TcxCustomDataField; AGroupIndex: Integer);
var
AIndex, ANewIndex: Integer;
begin
if AGroupIndex < 0 then
Remove(AField)
else
begin
ANewIndex := AGroupIndex;
AIndex := Find(AField);
if AIndex = -1 then
begin
if ANewIndex > Count then
ANewIndex := Count;
Insert(ANewIndex, AField, soAscending);
end
else
begin
if ANewIndex > (Count - 1) then
ANewIndex := Count - 1;
Move(AIndex, ANewIndex);
end;
end;
end;
procedure TcxGroupingFieldList.ChangeSorting(AField: TcxCustomDataField; ASortOrder: TcxDataSortOrder);
begin
if ASortOrder = soNone then
ASortOrder := soAscending;
inherited ChangeSorting(AField, ASortOrder);
end;
function TcxGroupingFieldList.GroupIndexByField(AField: TcxCustomDataField): Integer;
begin
Result := Find(AField);
end;
procedure TcxGroupingFieldList.UpdateSorting(ASortingFieldList: TcxSortingFieldList);
var
I, AIndex: Integer;
begin
for I := 0 to Count - 1 do
begin
AIndex := ASortingFieldList.Find(Items[I].Field);
if AIndex <> -1 then
SetSortOrder(I, ASortingFieldList[AIndex].SortOrder)
else
SetSortOrder(I, soAscending);
end;
end;
{ TcxDataGroups }
constructor TcxDataGroups.Create;
begin
inherited Create;
FFields := TList.Create;
FItems := TList.Create;
end;
destructor TcxDataGroups.Destroy;
begin
Clear;
FFields.Free;
FItems.Free;
inherited Destroy;
end;
procedure TcxDataGroups.ChangeExpanding(ARowIndex: Integer; AExpanded, ARecursive: Boolean);
var
AItem: TcxDataGroupInfo;
I: Integer;
begin
if (Find(ARowIndex, AItem) <> -1) and Assigned(AItem) then
begin
AItem.Expanded := AExpanded;
if ARecursive and (AItem.Level < (LevelCount - 1)) then
begin
for I := AItem.BeginRecordListIndex to Count - 1 do
if Items[I].Level > AItem.Level then
Items[I].Expanded := AExpanded
else
Break;
end;
Rebuild;
end;
end;
procedure TcxDataGroups.FullExpanding(AExpanded: Boolean);
var
I: Integer;
begin
for I := 0 to Count - 1 do
Items[I].Expanded := AExpanded;
Rebuild;
end;
function TcxDataGroups.GetChildCount(AIndex: Integer): Integer;
var
I, L: Integer;
begin
if AIndex = -1 then
Result := GetFirstLevelCount
else
begin
Result := 0;
if (0 <= AIndex) and (AIndex < Count) then
begin
L := Items[AIndex].Level + 1;
if L = LevelCount then
Result := Items[AIndex].EndRecordListIndex - Items[AIndex].BeginRecordListIndex + 1
else
for I := Items[AIndex].BeginRecordListIndex to Items[AIndex].EndRecordListIndex do
if Items[I].Level = L then
Inc(Result);
end;
end;
end;
function TcxDataGroups.GetChildIndex(AParentIndex, AIndex: Integer): Integer;
var
I, J, L: Integer;
begin
if AParentIndex = -1 then
Result := GetFirstLevelIndex(AIndex)
else
begin
Result := -1;
if (0 <= AParentIndex) and (AParentIndex < Count) and
(Items[AParentIndex].Level < (LevelCount - 1)) and
(0 <= AIndex) and (AIndex <= (Items[AParentIndex].EndRecordListIndex - Items[AParentIndex].BeginRecordListIndex)) then
// (Items[AParentIndex].BeginRecordListIndex <= AIndex) and
// (AIndex <= Items[AParentIndex].EndRecordListIndex) then
begin
L := Items[AParentIndex].Level + 1;
J := -1;
for I := Items[AParentIndex].BeginRecordListIndex to Items[AParentIndex].EndRecordListIndex do
if Items[I].Level = L then
begin
Inc(J);
if J = AIndex then
begin
Result := I;
Break;
end;
end;
end;
end;
end;
function TcxDataGroups.GetChildRecordListIndex(AParentIndex, AIndex: Integer): Integer;
begin
Result := -1;
if (0 <= AParentIndex) and (AParentIndex < Count) and
(Items[AParentIndex].Level = (LevelCount - 1)) and
(0 <= AIndex) and (AIndex <= (Items[AParentIndex].EndRecordListIndex - Items[AParentIndex].BeginRecordListIndex)) then
begin
Result := Items[AParentIndex].BeginRecordListIndex + AIndex;
end;
end;
function TcxDataGroups.GetDataRecordListIndex(AInfo: TcxDataGroupInfo): Integer;
begin
while AInfo.Level < LevelCount - 1 do
AInfo := Items[AInfo.BeginRecordListIndex];
Result := AInfo.BeginRecordListIndex;
end;
function TcxDataGroups.GetFirstLevelCount: Integer;
var
I: Integer;
begin
Result := 0;
for I := 0 to Count - 1 do
if Items[I].Level = 0 then
Inc(Result);
end;
function TcxDataGroups.GetFirstLevelIndex(AIndex: Integer): Integer;
var
I, J: Integer;
begin
Result := -1;
J := -1;
for I := 0 to Count - 1 do
if Items[I].Level = 0 then
begin
Inc(J);
if J = AIndex then
begin
Result := I;
Break;
end;
end;
end;
function TcxDataGroups.GetIndexByRowIndex(ARowIndex: Integer): Integer;
begin
Result := GetIndexByRowIndexLevel(ARowIndex, -1);
end;
function TcxDataGroups.GetIndexByRowIndexLevel(ARowIndex, ALevel: Integer): Integer;
var
AIndex: Integer;
AItem: TcxDataGroupInfo;
begin
Result := Find(ARowIndex, AItem);
if (Result = -1) and (AItem <> nil) then // if it is a data row, go to the nearest top group row
begin
ARowIndex := AItem.RowIndex;
Result := Find(ARowIndex, AItem);
end;
if (Result <> -1) and (ALevel <> -1) then
begin
AIndex := Result;
Result := -1;
if Items[AIndex].Level < ALevel then
begin
while AIndex < Count do
begin
AItem := Items[AIndex];
if AItem.Level = ALevel then
begin
Result := AIndex;
Break;
end;
Inc(AIndex);
end;
end
else
begin
while AIndex >= 0 do
begin
AItem := Items[AIndex];
if AItem.Level = ALevel then
begin
Result := AIndex;
Break;
end;
Dec(AIndex);
end;
end;
end;
end;
function TcxDataGroups.GetLevel(AIndex: Integer): Integer;
begin
if (0 <= AIndex) and (AIndex < Count) then
Result := Items[AIndex].Level
else
Result := -1;
end;
function TcxDataGroups.GetParentIndex(AChildIndex: Integer): Integer;
var
ALevel: Integer;
begin
Result := -1;
if (0 <= AChildIndex) and (AChildIndex < Count) then
begin
ALevel := Items[AChildIndex].Level;
while AChildIndex >= 0 do
begin
if Items[AChildIndex].Level < ALevel then
begin
Result := AChildIndex;
Break;
end
else
if ALevel = 0 then
Break;
Dec(AChildIndex);
end;
end;
end;
procedure TcxDataGroups.Add(const ADataGroupInfo: TcxDataGroupInfo);
begin
with AddEmpty do
begin
RowIndex := ADataGroupInfo.RowIndex;
Expanded := ADataGroupInfo.Expanded;
Level := ADataGroupInfo.Level;
BeginRecordListIndex := ADataGroupInfo.BeginRecordListIndex;
EndRecordListIndex := ADataGroupInfo.EndRecordListIndex;
SummaryValues := ADataGroupInfo.SummaryValues;
end;
end;
procedure TcxDataGroups.AddField(AField: TcxCustomDataField);
begin
FFields.Add(AField);
end;
function TcxDataGroups.AddEmpty: TcxDataGroupInfo;
begin
Result := TcxDataGroupInfo.Create;
FItems.Add(Result);
end;
procedure TcxDataGroups.Clear;
var
I: Integer;
AInfo: TcxDataGroupInfo;
begin
FFields.Clear;
for I := 0 to FItems.Count - 1 do
begin
AInfo := TcxDataGroupInfo(FItems[I]);
AInfo.Free;
end;
FItems.Clear;
end;
function TcxDataGroups.Find(ARowIndex: Integer; var AItem: TcxDataGroupInfo): Integer;
var
L, H, I, C: Integer;
begin
AItem := nil;
Result := -1;
L := 0;
H := Count - 1;
if L <= H then
repeat
I := (L + H) div 2;
AItem := Items[I];
C := AItem.RowIndex - ARowIndex;
if C = 0 then // It's Group
begin
AItem := GetTopVisibleItem(ARowIndex, I);
Result := I;
Break;
end
else
if C < 0 then
L := I + 1
else
H := I - 1;
if L > H then
begin
AItem := Items[L - 1];
Break;
end;
until False;
end;
function TcxDataGroups.FindByIndex(ARecordListIndex, ALevel: Integer): Integer;
var
I, ALastLevel, AAnchor: Integer;
AItem: TcxDataGroupInfo;
begin
Result := -1;
AAnchor := -1;
ALastLevel := LevelCount - 1;
for I := 0 to Count - 1 do
begin
AItem := Items[I];
if AItem.Level = ALevel then
AAnchor := I;
if (AItem.Level = ALastLevel) and
(AItem.BeginRecordListIndex <= ARecordListIndex) and
(ARecordListIndex <= AItem.EndRecordListIndex) then // only for Data rows
begin
Result := AAnchor;
Break;
end;
end;
end;
function TcxDataGroups.IsVisible(AIndex: Integer): Boolean;
var
I: Integer;
begin
I := AIndex;
GetTopVisibleItem(Items[AIndex].RowIndex, I);
Result := I = AIndex;
end;
function TcxDataGroups.MakeVisible(AIndex: Integer; AExpand: Boolean): Boolean;
procedure ExpandItem(AItem: TcxDataGroupInfo);
begin
if not AItem.Expanded then
begin
AItem.Expanded := True;
Result := True;
end;
end;
var
I, ALevel: Integer;
AItem: TcxDataGroupInfo;
begin
Result := False;
I := AIndex;
repeat
AItem := Items[I];
if AExpand then
ExpandItem(AItem);
AExpand := True;
ALevel := AItem.Level;
if ALevel = 0 then
Break
else
while I > 0 do
begin
Dec(I);
if Items[I].Level < ALevel then
Break;
end;
until False;
if Result then
Rebuild;
end;
function TcxDataGroups.GetField(Index: Integer): TcxCustomDataField;
begin
Result := TcxCustomDataField(FFields[Index]);
end;
function TcxDataGroups.GetFieldCount: Integer;
begin
Result := FFields.Count;
end;
function TcxDataGroups.GetItem(Index: Integer): TcxDataGroupInfo;
begin
Result := TcxDataGroupInfo(FItems[Index]);
end;
function TcxDataGroups.GetItemCount: Integer;
begin
Result := FItems.Count;
end;
function TcxDataGroups.GetRowCount: Integer;
var
AItem: TcxDataGroupInfo;
I, ARowIndex: Integer;
begin
if Count > 0 then
begin
I := Count - 1;
AItem := Items[I];
ARowIndex := AItem.RowIndex;
Result := ARowIndex + 1;
AItem := GetTopVisibleItem(ARowIndex, I);
if AItem.Expanded then
Inc(Result, AItem.EndRecordListIndex - AItem.BeginRecordListIndex + 1); // only for Data rows
end
else
Result := 0;
end;
function TcxDataGroups.GetRowInfo(ARowIndex: Integer): TcxGroupsRowInfo;
var
AItem: TcxDataGroupInfo;
begin
Result.Expanded := False;
Result.Level := 0;
Result.RecordListIndex := 0;
if Find(ARowIndex, AItem) <> -1 then // Group
begin
Result.Level := AItem.Level;
Result.Expanded := AItem.Expanded;
Result.RecordListIndex := GetDataRecordListIndex(AItem);
end
else
if AItem <> nil then
begin
Result.Level := LevelCount;
Result.Expanded := False;
Result.RecordListIndex := AItem.BeginRecordListIndex + (ARowIndex - AItem.RowIndex - 1);
end;
end;
function TcxDataGroups.GetTopVisibleItem(ARowIndex: Integer; var ACurIndex: Integer): TcxDataGroupInfo;
var
I, J: Integer;
AMaxLevel: Integer;
AItem: TcxDataGroupInfo;
begin
I := ACurIndex;
J := I;
AMaxLevel := Items[I].Level;
repeat
AItem := Items[I];
if AItem.RowIndex <> ARowIndex then
Break;
// check level
if AItem.Level < AMaxLevel then
begin
AMaxLevel := AItem.Level;
if not AItem.Expanded then
J := I;
end;
if AItem.Level = 0 then
Break;
Dec(I);
until False;
Result := Items[J];
ACurIndex := J;
end;
procedure TcxDataGroups.Rebuild;
var
ACurIndex, ARowIndex: Integer;
procedure SubItems(ALevel: Integer; AVisible: Boolean);
var
AItem: TcxDataGroupInfo;
begin
Inc(ACurIndex);
while ACurIndex < Count do
begin
AItem := Items[ACurIndex];
if AItem.Level = ALevel then
begin
AItem.RowIndex := ARowIndex;
if AVisible and AItem.Expanded and (AItem.Level = (LevelCount - 1)) then
Inc(ARowIndex, AItem.EndRecordListIndex - AItem.BeginRecordListIndex + 1)
else
begin
if AVisible and AItem.Expanded then
Inc(ARowIndex);
SubItems(AItem.Level + 1, AVisible and AItem.Expanded);
end;
end
else
begin
if AVisible then Dec(ARowIndex);
Dec(ACurIndex);
Break;
end;
Inc(ACurIndex);
if AVisible then Inc(ARowIndex);
end;
end;
begin
ACurIndex := -1;
ARowIndex := 0;
SubItems(0, True);
end;
procedure TcxDataGroups.SetItem(AIndex: Integer; AItem: TcxDataGroupInfo);
begin
FItems[AIndex] := AItem;
end;
{ TcxDataFocusingInfo }
constructor TcxDataFocusingInfo.Create(ADataControllerInfo: TcxCustomDataControllerInfo);
begin
inherited Create;
FDataControllerInfo := ADataControllerInfo;
FLevel := -1;
FPrevRecordIndex := -1;
FRecordIndex := -1;
FRowIndex := -1;
end;
procedure TcxDataFocusingInfo.Assign(AFocusingInfo: TcxDataFocusingInfo);
begin
FLevel := AFocusingInfo.Level;
FPrevRecordIndex := AFocusingInfo.FPrevRecordIndex;
FRecordIndex := AFocusingInfo.RecordIndex;
FRowIndex := AFocusingInfo.RowIndex;
FChangedFlag := False;
FPrevNewItemRowFocused := False;
end;
procedure TcxDataFocusingInfo.Clear;
begin
FLevel := -1;
FRecordIndex := -1;
FRowIndex := -1;
end;
function TcxDataFocusingInfo.IsEqual(AFocusingInfo: TcxDataFocusingInfo): Boolean;
begin
Result := (Self.RecordIndex = AFocusingInfo.RecordIndex) and
(Self.Level = AFocusingInfo.Level) {and
(Self.RowIndex = AFocusingInfo.RowIndex)};
if Result and (dcicSorting in FDataControllerInfo.FChanges) and
(Self.RowIndex <> AFocusingInfo.RowIndex) then
Result := False;
end;
procedure TcxDataFocusingInfo.ResetPos;
begin
FLevel := -1;
FRecordIndex := FDataControllerInfo.DataController.GetRecordIndex;
end;
procedure TcxDataFocusingInfo.SavePos;
begin
if RowIndex <> -1 then
begin
Level := FDataControllerInfo.GetRowInfo(RowIndex).Level;
RecordIndex := FDataControllerInfo.DataController.GetRecordIndex;
end
else
begin
Level := -1;
RecordIndex := -1;
end;
FPrevRecordIndex := FRecordIndex;
end;
{ TcxDataExpandingInfos }
constructor TcxDataExpandingInfos.Create;
begin
inherited Create;
FFields := TList.Create;
FItems := TList.Create;
end;
destructor TcxDataExpandingInfos.Destroy;
begin
Clear;
FItems.Free;
FFields.Free;
inherited Destroy;
end;
procedure TcxDataExpandingInfos.AddField(AField: TcxCustomDataField);
begin
if Find(AField) = -1 then
begin
FFields.Add(AField);
ClearValues;
end
else
InvalidOperation(cxSDataItemExistError);
end;
procedure TcxDataExpandingInfos.AddItem(ALevel: Integer; const AValue: Variant;
AState: TcxDataExpandingInfoStateSet);
var
P: PcxDataExpandingInfo;
begin
New(P);
FItems.Add(P);
P.Level := ALevel;
P.Value := AValue;
P.State := AState;
end;
procedure TcxDataExpandingInfos.CheckField(AField: TcxCustomDataField);
var
I: Integer;
begin
for I := 0 to FieldCount - 1 do
if Fields[I] = AField then
begin
Clear;
Exit;
end;
end;
procedure TcxDataExpandingInfos.Clear;
begin
ClearFields;
ClearValues;
FSaveStates := [];
end;
procedure TcxDataExpandingInfos.ClearFields;
begin
FFields.Clear;
end;
procedure TcxDataExpandingInfos.ClearValues;
var
I: Integer;
begin
for I := 0 to FItems.Count - 1 do
Dispose(PcxDataExpandingInfo(FItems[I]));
FItems.Clear;
end;
function TcxDataExpandingInfos.Find(AField: TcxCustomDataField): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to FieldCount - 1 do
if Fields[I] = AField then
begin
Result := I;
Break;
end;
end;
function TcxDataExpandingInfos.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TcxDataExpandingInfos.GetEmpty: Boolean;
begin
Result := (Count = 0) or (FieldCount = 0);
end;
function TcxDataExpandingInfos.GetFieldCount: Integer;
begin
Result := FFields.Count;
end;
function TcxDataExpandingInfos.GetField(Index: Integer): TcxCustomDataField;
begin
Result := TcxCustomDataField(FFields[Index]);
end;
function TcxDataExpandingInfos.GetItem(Index: Integer): PcxDataExpandingInfo;
begin
Result := PcxDataExpandingInfo(FItems[Index]);
end;
{ TcxDataSelection }
constructor TcxDataSelection.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FDataController := ADataController;
FFields := TList.Create;
FItems := TList.Create;
FAnchorRowIndex := -1;
end;
destructor TcxDataSelection.Destroy;
begin
Clear;
FItems.Free;
FFields.Free;
inherited Destroy;
end;
procedure TcxDataSelection.Add(AIndex, ARowIndex, ARecordIndex, ALevel: Integer);
begin
InternalAdd(AIndex, ARowIndex, ARecordIndex, ALevel);
Changed;
end;
procedure TcxDataSelection.AddField(AField: TcxCustomDataField);
begin
FFields.Add(AField);
end;
procedure TcxDataSelection.Clear;
begin
InternalClear;
Changed;
end;
procedure TcxDataSelection.ClearAll;
begin
ClearAnchor;
InternalClear;
Changed;
end;
procedure TcxDataSelection.ClearFields;
begin
FFields.Clear;
end;
procedure TcxDataSelection.Delete(AIndex: Integer);
begin
InternalDelete(AIndex);
Changed;
end;
function TcxDataSelection.FindByGroupIndex(AGroupIndex: Integer): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to Count - 1 do
if (Items[I].Level = -1) {It's Group} and
(Items[I].RecordIndex = AGroupIndex) then
begin
Result := I;
Break;
end;
end;
function TcxDataSelection.FindByRecordIndex(ARecordIndex: Integer): Integer;
var
I: Integer;
begin
// TODO: to optimize
Result := -1;
for I := 0 to Count - 1 do
if (Items[I].Level <> -1) {It's Detail} and
(Items[I].RecordIndex = ARecordIndex) then
begin
Result := I;
Break;
end;
end;
function TcxDataSelection.FindByRowIndex(ARowIndex: Integer; var AIndex: Integer): Boolean;
var
L, H, I, C: Integer;
begin
AIndex := 0;
Result := False;
L := 0;
H := Count - 1;
if L <= H then
repeat
I := (L + H) div 2;
C := Items[I].RowIndex - ARowIndex;
if C = 0 then
begin
AIndex := I;
Result := True;
Break;
end
else
if C < 0 then
L := I + 1
else
H := I - 1;
if L > H then
begin
AIndex := L;
Break;
end;
until False;
end;
function TcxDataSelection.IsRecordSelected(ARecordIndex: Integer): Boolean;
begin
Result := FindByRecordIndex(ARecordIndex) <> -1;
end;
function TcxDataSelection.IsRowSelected(ARowIndex: Integer): Boolean;
var
I: Integer;
begin
Result := FindByRowIndex(ARowIndex, I);
end;
procedure TcxDataSelection.Sort;
begin
FItems.Sort(CompareSelectionList);
end;
procedure TcxDataSelection.Changed;
begin
if Assigned(FOnChanged) then FOnChanged;
end;
procedure TcxDataSelection.CheckAfterFiltering;
var
I: Integer;
begin
// TODO: opt
for I := Count - 1 downto 0 do
if Items[I].Level <> -1 then
if DataController.DataControllerInfo.GetInternalRecordListIndex(Items[I].RecordIndex) = -1 then
Delete(I);
end;
procedure TcxDataSelection.ClearAnchor;
begin
FAnchorRowIndex := -1;
end;
procedure TcxDataSelection.InternalAdd(AIndex, ARowIndex, ARecordIndex, ALevel: Integer);
var
P: PcxDataSelectionInfo;
begin
New(P);
P.Level := ALevel;
P.RecordIndex := ARecordIndex;
P.RowIndex := ARowIndex;
if AIndex = -1 then
FItems.Add(P)
else
FItems.Insert(AIndex, P);
end;
procedure TcxDataSelection.InternalClear;
var
I: Integer;
begin
ClearFields;
for I := 0 to FItems.Count - 1 do
Dispose(PcxDataSelectioninfo(FItems[I]));
FItems.Clear;
end;
procedure TcxDataSelection.InternalDelete(AIndex: Integer);
var
PInfo: PcxDataSelectioninfo;
begin
PInfo := PcxDataSelectioninfo(FItems[AIndex]);
if PInfo.RowIndex = FAnchorRowIndex then
ClearAnchor;
Dispose(PInfo);
FItems.Delete(AIndex);
end;
procedure TcxDataSelection.SetInternalCount(ACount: Integer);
var
I: Integer;
begin
if ACount < Count then
begin
for I := Count - 1 downto ACount do
InternalDelete(I);
end
else
if ACount > Count then
begin
for I := Count to ACount - 1 do
InternalAdd(-1, I, I, 0);
end;
end;
function TcxDataSelection.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TcxDataSelection.GetField(Index: Integer): TcxCustomDataField;
begin
Result := TcxCustomDataField(FFields[Index]);
end;
function TcxDataSelection.GetFieldCount: Integer;
begin
Result := FFields.Count;
end;
function TcxDataSelection.GetItem(Index: Integer): PcxDataSelectionInfo;
begin
Result := PcxDataSelectionInfo(FItems[Index]);
end;
{ TcxCustomDataControllerInfo }
constructor TcxCustomDataControllerInfo.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FDataController := ADataController;
FRecordList := TList.Create;
FGroupingFieldList := TcxGroupingFieldList.Create;
FGroupingFieldList.OnChanged := GroupingChanged;
FSortingFieldList := TcxSortingFieldList.Create;
FSortingFieldList.OnChanged := SortingChanged;
FTotalSortingFieldList := TcxSortingFieldList.Create;
FDataGroups := TcxDataGroups.Create;
FExpandingInfo := TcxDataExpandingInfos.Create;
FFocusingInfo := TcxDataFocusingInfo.Create(Self);
FPrevFocusingInfo := TcxDataFocusingInfo.Create(Self);
FSelection := DataController.GetDataSelectionClass.Create(DataController);
FSelection.OnChanged := SelectionChanged;
end;
destructor TcxCustomDataControllerInfo.Destroy;
begin
FDataGroups.Free;
FTotalSortingFieldList.Free;
FSortingFieldList.Free;
FGroupingFieldList.Free;
FRecordList.Free;
FFocusingInfo.Free;
FPrevFocusingInfo.Free;
FExpandingInfo.Free;
FSelection.Free;
inherited Destroy;
end;
procedure TcxCustomDataControllerInfo.BeginUpdate;
begin
Inc(FLockCount);
end;
procedure TcxCustomDataControllerInfo.EndUpdate;
begin
Dec(FLockCount);
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.CheckChanges;
begin
if (FChanges <> []) and (LockCount = 0) then
Update;
end;
procedure TcxCustomDataControllerInfo.CheckRowIndex(ARowIndex: Integer);
begin
if not ((0 <= ARowIndex) and (ARowIndex < GetRowCount)) then
InvalidOperation(cxSDataRowIndexError);
end;
procedure TcxCustomDataControllerInfo.Refresh;
begin
FChanges := FChanges + [dcicLoad];
if not DataController.LockOnAfterSummary then
FChanges := FChanges + [dcicSummary];
if SortingFieldList.Count <> 0 then
FChanges := FChanges + [dcicSorting];
if GroupingFieldList.Count <> 0 then
FChanges := FChanges + [dcicGrouping];
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.RefreshFocused;
begin
FChanges := FChanges + [dcicResetFocusedRow];
FFocusingInfo.ResetPos;
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.RefreshSummary(ARedrawOnly: Boolean);
begin
if ARedrawOnly and not IsSortingBySummary then
FChanges := FChanges + [dcicView]
else
FChanges := FChanges + [dcicSummary];
if IsSortingBySummary then
FChanges := FChanges + [dcicGrouping];
{ if IsSortBySummary <> FIsSortBySummary then
begin
FIsSortBySummary := IsSortBySummary;
DataController.DoSortBySummaryChanged;
//FChanges := FChanges + [dcicGrouping];
end;}
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.RefreshView;
begin
FChanges := FChanges + [dcicView];
if not FInfoCalculation then
CheckChanges;
end;
// Notify
procedure TcxCustomDataControllerInfo.ExpandingChanged;
begin
FChanges := FChanges + [dcicExpanding];
FExpandingFlag := True;
end;
procedure TcxCustomDataControllerInfo.FocusedRecordChanged(AChangedFlag: Boolean);
begin
FChanges := FChanges + [dcicFocusedRecord]; //?
if AChangedFlag then
FPrevFocusingInfo.FChangedFlag := True;
end;
procedure TcxCustomDataControllerInfo.GroupingChanged;
begin
FChanges := FChanges + [dcicSorting, dcicGrouping, dcicSummary];
end;
procedure TcxCustomDataControllerInfo.SelectionChanged;
begin
FChanges := FChanges + [dcicSelection];
if [soSelectedRecords, soMultipleSelectedRecords] * DataController.Summary.Options <> [] then
FChanges := FChanges + [dcicSummary];
end;
procedure TcxCustomDataControllerInfo.SortingChanged;
begin
FChanges := FChanges + [dcicSorting];
end;
// Structure
procedure TcxCustomDataControllerInfo.RemoveField(AField: TcxCustomDataField);
begin
FSortingFieldList.ChangeSorting(AField, soNone);
if FGroupingFieldList.Find(AField) <> -1 then FSelection.ClearAll;
FGroupingFieldList.ChangeGrouping(AField, -1);
FGroupingFieldList.UpdateSorting(FSortingFieldList);
FExpandingInfo.CheckField(AField);
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.UpdateField(AField: TcxCustomDataField);
begin
FChanges := FChanges + [dcicView];
FSortingFieldList.CheckField(AField);
if FGroupingFieldList.Find(AField) <> -1 then FSelection.ClearAll;
FGroupingFieldList.CheckField(AField);
FExpandingInfo.CheckField(AField);
CheckChanges;
end;
// Sorting
procedure TcxCustomDataControllerInfo.ChangeSortIndex(AField: TcxCustomDataField; ASortIndex: Integer);
var
ACurIndex: Integer;
begin
ACurIndex := FSortingFieldList.Find(AField);
if ASortIndex < 0 then ASortIndex := 0;
if ASortIndex >= FSortingFieldList.Count then
ASortIndex := FSortingFieldList.Count - 1;
if ACurIndex <> ASortIndex then
begin
FSortingFieldList.Move(ACurIndex, ASortIndex);
DataController.BeforeSorting;
DataController.FSortingChanged := True;
CheckChanges;
end;
end;
procedure TcxCustomDataControllerInfo.ChangeSorting(AField: TcxCustomDataField;
ASortOrder: TcxDataSortOrder);
begin
FSortingFieldList.ChangeSorting(AField, ASortOrder);
FGroupingFieldList.UpdateSorting(FSortingFieldList);
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.ClearSorting(AKeepGroupedItems: Boolean);
var
I, APrevCount: Integer;
begin
APrevCount := FSortingFieldList.Count;
if AKeepGroupedItems then
begin
for I := FSortingFieldList.Count - 1 downto 0 do
if FGroupingFieldList.Find(FSortingFieldList[I].Field) = -1 then
FSortingFieldList.Delete(I);
end
else
begin
FSortingFieldList.Clear;
FGroupingFieldList.UpdateSorting(FSortingFieldList);
end;
if FSortingFieldList.Count <> APrevCount then
begin
DataController.BeforeSorting;
DataController.FSortingChanged := True;
end;
CheckChanges;
end;
// Grouping
procedure TcxCustomDataControllerInfo.ChangeExpanding(ARowIndex: Integer;
AExpanded, ARecursive: Boolean);
begin
CheckRowIndex(ARowIndex);
if IsGrouped and not IsAlwaysExpanded then
begin
// TODO: CheckBrowseMode; if needed
if not AExpanded and (FocusedRowIndex <> -1) and
DataController.Groups.HasAsParent(FocusedRowIndex, ARowIndex) then
DataController.CheckBrowseMode;
DataGroups.ChangeExpanding(ARowIndex, AExpanded, ARecursive);
ExpandingChanged;
BeginUpdate;
try
if not AExpanded then
CheckAfterCollapsing;
finally
EndUpdate;
end;
end;
end;
procedure TcxCustomDataControllerInfo.ChangeGrouping(AField: TcxCustomDataField;
AGroupIndex: Integer);
begin
FGroupingFieldList.ChangeGrouping(AField, AGroupIndex);
FGroupingFieldList.UpdateSorting(FSortingFieldList);
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.ClearGrouping;
begin
FGroupingFieldList.Clear;
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.FullCollapse;
begin
if IsGrouped and not IsAlwaysExpanded then
begin
DataGroups.FullExpanding(False);
ExpandingChanged;
BeginUpdate;
try
CheckAfterCollapsing;
finally
EndUpdate;
end;
end;
end;
procedure TcxCustomDataControllerInfo.FullExpand;
begin
if IsGrouped and not IsAlwaysExpanded then
begin
DataGroups.FullExpanding(True);
ExpandingChanged;
CheckChanges;
end;
end;
// View Data
function TcxCustomDataControllerInfo.GetNearestRowIndex(ARowIndex: Integer): Integer;
begin
// TODO: ADeleteSelection
CheckRowIndex(ARowIndex);
if not IsGrouped then
begin
if (ARowIndex + 1) > (GetRowCount - 1) then // is last
Result := ARowIndex - 1
else
Result := ARowIndex + 1;
end
else
begin
if ((ARowIndex + 1) <= (GetRowCount - 1)) and
(GetRowInfo(ARowIndex + 1).Level = GetRowInfo(ARowIndex).Level) then
Result := ARowIndex + 1
else
begin
if ((ARowIndex - 1) >= 0) and
(GetRowInfo(ARowIndex - 1).Level = GetRowInfo(ARowIndex).Level) then
Result := ARowIndex - 1
else
if (ARowIndex + 1) <= (GetRowCount - 1) then
Result := ARowIndex + 1
else
if (ARowIndex - 1) >= 0 then
Result := ARowIndex - 1
else
Result := ARowIndex;
end;
end;
end;
function TcxCustomDataControllerInfo.GetRowCount: Integer;
begin
if IsGrouped then
Result := DataGroups.RowCount
else
Result := FRecordList.Count;
end;
function TcxCustomDataControllerInfo.GetRowIndexByRecordIndex(ARecordIndex: Integer;
AMakeVisible: Boolean): Integer;
var
ARecordListIndex, AGroupIndex: Integer;
begin
Result := -1;
ARecordListIndex := GetInternalRecordListIndex(ARecordIndex);
if IsGrouped then
begin
AGroupIndex := DataGroups.FindByIndex(ARecordListIndex, DataGroups.LevelCount - 1);
if AGroupIndex <> -1 then
Result := LocateDetail(AGroupIndex, ARecordListIndex, AMakeVisible);
end
else
Result := ARecordListIndex;
if FExpandingFlag and AMakeVisible then
CheckChanges;
end;
function TcxCustomDataControllerInfo.GetRowInfo(ARowIndex: Integer): TcxRowInfo;
var
AGroupsRowInfo: TcxGroupsRowInfo;
begin
CheckRowIndex(ARowIndex);
if IsGrouped then
AGroupsRowInfo := DataGroups.RowInfo[ARowIndex]
else
begin
AGroupsRowInfo.Level := 0;
AGroupsRowInfo.Expanded := False;
AGroupsRowInfo.RecordListIndex := ARowIndex;
end;
Result.Expanded := AGroupsRowInfo.Expanded;
Result.Level := AGroupsRowInfo.Level;
Result.RecordIndex := GetInternalRecordIndex(AGroupsRowInfo.RecordListIndex);
end;
// Selection
procedure TcxCustomDataControllerInfo.ChangeRowSelection(ARowIndex: Integer; ASelection: Boolean);
var
AIndex, AGroupIndex: Integer;
ARowInfo: TcxRowInfo;
AItem: TcxDataGroupInfo;
begin
CheckRowIndex(ARowIndex);
if Selection.FindByRowIndex(ARowIndex, AIndex) then
begin
if not ASelection then
Selection.Delete(AIndex);
end
else
begin
if ASelection and DataController.CanSelectRow(ARowIndex) then
begin
AGroupIndex := DataGroups.Find(ARowIndex, AItem);
if AGroupIndex <> -1 then // It's Group
Selection.Add(AIndex, ARowIndex, AGroupIndex, -1)
else
begin
ARowInfo := GetRowInfo(ARowIndex);
Selection.Add(AIndex, ARowIndex, ARowInfo.RecordIndex, ARowInfo.Level);
end;
end;
end;
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.ClearSelection;
begin
Selection.Clear;
CheckChanges;
end;
procedure TcxCustomDataControllerInfo.ClearSelectionAnchor;
begin
Selection.ClearAnchor;
end;
function TcxCustomDataControllerInfo.GetSelectedCount: Integer;
begin
Result := FSelection.Count;
end;
function TcxCustomDataControllerInfo.GetSelectedRowIndex(Index: Integer): Integer;
begin
Result := Selection[Index].RowIndex;
end;
function TcxCustomDataControllerInfo.IsRowSelected(ARowIndex: Integer): Boolean;
begin
CheckRowIndex(ARowIndex);
Result := Selection.IsRowSelected(ARowIndex);
end;
function TcxCustomDataControllerInfo.CanFocusedRowChanging(ARowIndex: Integer): Boolean;
begin
FInCanFocusedRowChanging := True;
try
if ARowIndex <> -1 then
Result := DataController.CanFocusRecord(GetRowInfo(ARowIndex).RecordIndex)
else
Result := DataController.CanFocusRecord(-1);
finally
FInCanFocusedRowChanging := False;
end;
end;
procedure TcxCustomDataControllerInfo.CorrectFocusedRow(ARowIndex: Integer);
begin
FFocusingInfo.Clear;
DoChangeFocusedRow(ARowIndex, False);
end;
procedure TcxCustomDataControllerInfo.DoChangeFocusedRow(AValue: Integer; AIsInternal: Boolean);
procedure CheckFocusedRanges(AFocusingInfo: TcxDataFocusingInfo);
begin
if AFocusingInfo.FRowIndex > (GetRowCount - 1) then
AFocusingInfo.FRowIndex := -1;
end;
procedure SaveFocusedPos;
begin
FFocusingInfo.SavePos;
if (FFocusingInfo.RowIndex <> -1) and
(GetRowInfo(FFocusingInfo.RowIndex).RecordIndex <> FFocusingInfo.RecordIndex) then
begin
{ error locate - Tried to set focus on a row, but it turned out,
that another row was focused (it is possible for DB, for example:
the key field is incorrect) }
FFocusingInfo.FRowIndex := FindFocusedRow(False);
CheckFocusedRanges(FFocusingInfo);
end;
end;
var
AAllowChangeFocusedRow: Boolean;
begin
if AValue < -1 then AValue := -1;
if AValue > (GetRowCount - 1) then AValue := GetRowCount - 1;
if FFocusingInfo.RowIndex <> AValue then
begin
FFocusingFlag := True;
AAllowChangeFocusedRow := False;
try
if not (dcicFocusedRow in FChanges) then
FPrevFocusingInfo.Assign(FFocusingInfo);
AAllowChangeFocusedRow := AIsInternal or CanFocusedRowChanging(AValue);
if AAllowChangeFocusedRow then
begin
DataController.DoBeforeFocusedRowChange(AValue);
FFocusingInfo.FRowIndex := AValue;
end;
finally
CheckFocusedRanges(FPrevFocusingInfo);
CheckFocusedRanges(FFocusingInfo);
if (FPrevFocusingInfo.FRowIndex <> -1) or AAllowChangeFocusedRow then
SaveFocusedPos;
if not FFocusingInfo.IsEqual(FPrevFocusingInfo) then
begin
FChanges := FChanges + [dcicFocusedRecord, dcicFocusedRow];
if DataController.IsFocusedSelectedMode and
(soSelectedRecords in DataController.Summary.Options) then
FChanges := FChanges + [dcicSummary];
DataController.ResetNewItemRowFocused;
DataController.CheckDataSetCurrent;
DataController.SyncSelected(True);
end;
FFocusingFlag := False;
end;
end;
end;
procedure TcxCustomDataControllerInfo.DoFilter;
var
I, APrevRecordCount: Integer;
AIsIncrementalFilter, AIsFiltering, AIsOnFilterRecord: Boolean;
begin
AIsIncrementalFilter := DataController.IsIncrementalFilter;
AIsFiltering := DataController.Filter.IsFiltering;
AIsOnFilterRecord := not DataController.IsGridMode and DataController.IsFilterRecord;
if not (AIsIncrementalFilter or AIsFiltering or AIsOnFilterRecord) then Exit;
APrevRecordCount := FRecordList.Count;
for I := FRecordList.Count - 1 downto 0 do
if I <> DataController.Provider.FEditingRecordIndex then // TODO:
begin
if AIsOnFilterRecord and
not DataController.DoFilterRecord(I) then
begin
FRecordList.Delete(I);
Continue;
end;
if AIsIncrementalFilter and
not DataController.DoIncrementalFilterRecord(I) then
begin
FRecordList.Delete(I);
Continue;
end;
if AIsFiltering and
not DataController.Filter.DoFilterRecord(I) then
begin
FRecordList.Delete(I);
Continue;
end;
end;
if FRecordList.Count <> APrevRecordCount then
begin
FSelection.CheckAfterFiltering;
if DataController.FCheckFocusingAfterFilterNeeded then
begin
DataController.FCheckFocusingAfterFilterNeeded := False;
DataController.FFilterChanged := True;
end;
end;
end;
type
TcxDataGroupCompareInfo = record
Info: TcxDataGroupInfo;
RecordIndex: Integer;
end;
PcxDataGroupCompareInfo = ^TcxDataGroupCompareInfo;
procedure TcxCustomDataControllerInfo.DoGrouping;
var
ACurLevels: TList;
ARowIndex: Integer;
ARecordIndex: Integer;
I, J: Integer;
procedure BeginBuilding;
var
I: Integer;
ARecordIndex: Integer;
PCompareInfo: PcxDataGroupCompareInfo;
begin
ACurLevels := TList.Create;
ARecordIndex := GetInternalRecordIndex(0);
for I := 0 to FDataGroups.LevelCount - 1 do
begin
New(PCompareInfo);
PCompareInfo.RecordIndex := ARecordIndex;
PCompareInfo.Info := DataGroups.AddEmpty;
PCompareInfo.Info.RowIndex := ARowIndex;
PCompareInfo.Info.Expanded := False;
PCompareInfo.Info.Level := I;
if I < (FDataGroups.LevelCount - 1) then
PCompareInfo.Info.BeginRecordListIndex := I + 1
else
PCompareInfo.Info.BeginRecordListIndex := 0;
ACurLevels.Add(PCompareInfo);
end;
end;
procedure CloseGroups(ARecordListIndex, ALevelIndex: Integer);
begin
if ALevelIndex >= (FDataGroups.LevelCount - 1) then
begin
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.EndRecordListIndex := ARecordListIndex - 1;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info := DataGroups.AddEmpty;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.RowIndex := ARowIndex;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.Expanded := False;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.Level := ALevelIndex;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.BeginRecordListIndex := ARecordListIndex;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).RecordIndex := ARecordIndex;
end
else
begin
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.EndRecordListIndex := DataGroups.Count - 1;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info := DataGroups.AddEmpty;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.RowIndex := ARowIndex;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.Expanded := False;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.Level := ALevelIndex;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).Info.BeginRecordListIndex := DataGroups.Count;
PcxDataGroupCompareInfo(ACurLevels[ALevelIndex]).RecordIndex := ARecordIndex;
CloseGroups(ARecordListIndex, ALevelIndex + 1);
end;
end;
procedure CloseBuilding;
var
I, AEndRecordListIndex: Integer;
begin
AEndRecordListIndex := FRecordList.Count - 1;
for I := FDataGroups.LevelCount - 1 downto 0 do
begin
PcxDataGroupCompareInfo(ACurLevels[I]).Info.EndRecordListIndex := AEndRecordListIndex;
if I = (FDataGroups.LevelCount - 1) then
AEndRecordListIndex := DataGroups.Count - 1
else
AEndRecordListIndex := PcxDataGroupCompareInfo(ACurLevels[I]).Info.BeginRecordListIndex - 1;
end;
end;
procedure EndBuilding;
var
I: Integer;
begin
for I := 0 to ACurLevels.Count - 1 do
Dispose(PcxDataGroupCompareInfo(ACurLevels[I]));
ACurLevels.Free;
end;
begin
FDataGroups.Clear;
for I := 0 to GroupingFieldList.Count - 1 do
FDataGroups.AddField(GroupingFieldList[I].Field);
if (FRecordList.Count > 0) and (FDataGroups.LevelCount > 0) then
begin
ARowIndex := 0;
BeginBuilding;
try
PrepareSorting;
try
for I := 1 to FRecordList.Count - 1 do
begin
ARecordIndex := GetInternalRecordIndex(I);
for J := 0 to FDataGroups.LevelCount - 1 do
begin
if CompareGroupRecords(PcxDataGroupCompareInfo(ACurLevels[J]).RecordIndex, ARecordIndex, J) <> 0 then
begin
if J = 0 then Inc(ARowIndex);
CloseGroups(I, J);
Break;
end;
end;
end;
finally
UnprepareSorting;
end;
CloseBuilding;
finally
EndBuilding;
end;
end;
end;
procedure TcxCustomDataControllerInfo.DoLoad;
var
I: Integer;
begin
ListSetCount(FRecordList, DataController.RecordCount);
for I := 0 to FRecordList.Count - 1 do
FRecordList[I] := Pointer(I);
end;
procedure TcxCustomDataControllerInfo.DoSort;
begin
FTotalSortingFieldList.Clear;
FTotalSortingFieldList.AppendFrom(GroupingFieldList);
FTotalSortingFieldList.AppendFrom(SortingFieldList);
if not DataController.IsGridMode and (FRecordList.Count > 0) then
begin
PrepareSorting;
try
if DataController.IsProviderMode and
DataController.Provider.IsCustomSorting then
DataController.Provider.CustomSort
else
QuickSortList(FRecordList.List, 0, FRecordList.Count - 1, CompareRecords);
finally
UnprepareSorting;
end;
end;
end;
procedure TcxCustomDataControllerInfo.DoBeginSummary;
begin
// Footer Summary can be calculated before DoSort if soSelectedRecords=False
if not IsSummaryForSelectedRecords then
DataController.Summary.BeginCalculate;
end;
procedure TcxCustomDataControllerInfo.DoEndSummary;
begin
if not IsSummaryForSelectedRecords then
DataController.Summary.EndCalculate
else
DataController.Summary.Calculate;
end;
function TcxCustomDataControllerInfo.FindDataGroup(ARecordListIndex: Integer): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to DataGroups.Count - 1 do
if (DataGroups[I].Level = (DataGroups.LevelCount - 1)) and
(DataGroups[I].BeginRecordListIndex <= ARecordListIndex) and
(ARecordListIndex <= DataGroups[I].EndRecordListIndex) then
begin
Result := I;
Break;
end;
end;
function TcxCustomDataControllerInfo.FindFocusedRow(ANearest: Boolean): Integer;
function FindFocused(ARecordListIndex, AGroupIndex: Integer): Integer;
begin
if FFocusingInfo.Level = -1 then
begin
if DataGroups[AGroupIndex].BeginRecordListIndex <> ARecordListIndex then // Seek Detail
Result := LocateDetail(AGroupIndex, ARecordListIndex, True)
else
Result := LocateGroupByLevel(AGroupIndex, -1);
end
else
begin
if FFocusingInfo.Level = DataGroups.LevelCount then // Seek Detail
Result := LocateDetail(AGroupIndex, ARecordListIndex, True)
else
begin
if DataGroups[AGroupIndex].BeginRecordListIndex = ARecordListIndex then
Result := LocateGroupByLevel(AGroupIndex, FFocusingInfo.Level)
else
Result := LocateDetail(AGroupIndex, ARecordListIndex, True)
end;
end;
end;
function FindParentGroup(AIndex, ALevel: Integer): Integer;
var
I: Integer;
begin
Result := -1;
for I := AIndex downto 0 do
if DataGroups[I].Level = ALevel then
begin
Result := I;
Break;
end;
end;
function FindNearest(ARecordListIndex, AGroupIndex: Integer): Integer;
var
I, ALevel: Integer;
AItem: TcxDataGroupInfo;
begin
if AGroupIndex <> -1 then
begin
I := AGroupIndex;
AItem := DataGroups.GetTopVisibleItem(DataGroups[AGroupIndex].RowIndex, I);
ALevel := FFocusingInfo.Level;
if ALevel = DataGroups.LevelCount then // Detail
begin
if (AGroupIndex = I) and DataGroups[AGroupIndex].Expanded then
begin
Result := AItem.RowIndex;
Inc(Result, ARecordListIndex - AItem.BeginRecordListIndex + 1);
Exit;
end
else
Dec(ALevel);
end;
if (ALevel < 0) and (AGroupIndex = I) then
begin
Result := AItem.RowIndex;
Exit;
end;
AGroupIndex := FindParentGroup(AGroupIndex, ALevel);
if AGroupIndex <> -1 then
Result := DataGroups[AGroupIndex].RowIndex
else
Result := -1;
end
else
Result := -1;
end;
var
I: Integer;
ARecordListIndex, AGroupIndex: Integer;
begin
Result := -1;
if FFocusingInfo.RecordIndex < 0 then Exit;
ARecordListIndex := -1;
for I := 0 to FRecordList.Count - 1 do
if GetInternalRecordIndex(I) = FFocusingInfo.RecordIndex then
begin
ARecordListIndex := I;
Break;
end;
if ARecordListIndex <> -1 then
if IsGrouped then
begin
AGroupIndex := FindDataGroup(ARecordListIndex); // LastLevel Group
if ANearest then
Result := FindNearest(ARecordListIndex, AGroupIndex)
else
Result := FindFocused(ARecordListIndex, AGroupIndex);
end
else
Result := ARecordListIndex;
end;
procedure TcxCustomDataControllerInfo.ForwardChanges;
var
ADataControllerChanges: TcxDataControllerChanges;
begin
ADataControllerChanges := [];
if [dcicSorting, dcicView] * FChanges <> [] then
ADataControllerChanges := [dccLayout];
if [dcicLoad, dcicGrouping, dcicExpanding] * FChanges <> [] then
ADataControllerChanges := ADataControllerChanges + [dccData];
if [dcicFocusedRow, dcicResetFocusedRow, dcicFocusedRecord] * FChanges <> [] then
ADataControllerChanges := ADataControllerChanges + [dccFocus];
if dcicSelection in FChanges then
ADataControllerChanges := ADataControllerChanges + [dccSelection];
if dcicSummary in FChanges then
ADataControllerChanges := ADataControllerChanges + [dccSummary];
if DataController.FGroupingChanged then
ADataControllerChanges := ADataControllerChanges + [dccGrouping];
if DataController.FSortingChanged then
ADataControllerChanges := ADataControllerChanges + [dccSorting];
FChanges := [];
DataController.FGroupingChanged := False;
DataController.FSortingChanged := False;
DataController.FFilterChanged := False;
DataController.FSortingBySummaryChanged := False;
DataController.Change(ADataControllerChanges);
DataController.FLoadedStorage := False;
end;
function TcxCustomDataControllerInfo.GetInternalRecordCount: Integer;
begin
Result := FRecordList.Count;
end;
function TcxCustomDataControllerInfo.GetInternalRecordIndex(ARecordListIndex: Integer): Integer;
begin
Result := Integer(FRecordList[ARecordListIndex]);
end;
function TcxCustomDataControllerInfo.GetInternalRecordListIndex(ARecordIndex: Integer): Integer;
begin
Result := FRecordList.IndexOf(Pointer(ARecordIndex));
end;
function TcxCustomDataControllerInfo.IsAlwaysExpanded: Boolean;
begin
Result := dcoGroupsAlwaysExpanded in DataController.Options;
end;
function TcxCustomDataControllerInfo.IsGrouped: Boolean;
begin
Result := FDataGroups.Count > 0;
end;
function TcxCustomDataControllerInfo.IsSummaryForSelectedRecords: Boolean;
begin
Result := (soSelectedRecords in DataController.Summary.Options) or
((soMultipleSelectedRecords in DataController.Summary.Options) and (GetSelectedCount > 1))
end;
function TcxCustomDataControllerInfo.LocateGroupByLevel(AGroupIndex, ALevel: Integer): Integer;
function FindParentGroup(AIndex: Integer): Integer;
var
I, ALevel: Integer;
begin
Result := -1;
ALevel := DataGroups[AIndex].Level - 1;
for I := AIndex downto 0 do
if DataGroups[I].Level = ALevel then
begin
Result := I;
Break;
end;
end;
var
AParentGroupIndex: Integer;
begin
repeat
if DataGroups[AGroupIndex].Level = ALevel then
Break;
AParentGroupIndex := FindParentGroup(AGroupIndex);
if (AParentGroupIndex <> -1) and (DataGroups[AParentGroupIndex].BeginRecordListIndex = AGroupIndex) then
AGroupIndex := AParentGroupIndex
else
Break;
until False;
if DataGroups.MakeVisible(AGroupIndex, False) then
ExpandingChanged;
Result := DataGroups[AGroupIndex].RowIndex;
end;
function TcxCustomDataControllerInfo.LocateDetail(AGroupIndex, ARecordListIndex: Integer;
AMakeVisible: Boolean): Integer;
begin
if not AMakeVisible and
not (DataGroups.IsVisible(AGroupIndex) and DataGroups[AGroupIndex].Expanded) then
Result := -1
else
begin
if AMakeVisible and DataGroups.MakeVisible(AGroupIndex, True) then
ExpandingChanged;
Result := DataGroups[AGroupIndex].RowIndex +
ARecordListIndex - DataGroups[AGroupIndex].BeginRecordListIndex + 1;
end;
end;
procedure TcxCustomDataControllerInfo.PrepareSorting;
var
I: Integer;
begin
for I := 0 to FTotalSortingFieldList.Count - 1 do
if DataController.IsConversionNeededForCompare(FTotalSortingFieldList[I].Field) then
begin
if DataController.IsSortByDisplayTextNeeded(FTotalSortingFieldList[I].Field) then
FTotalSortingFieldList[I].Field.FNeedConversionFlag := 2
else
FTotalSortingFieldList[I].Field.FNeedConversionFlag := 1;
end
else
FTotalSortingFieldList[I].Field.FNeedConversionFlag := -1;
end;
procedure TcxCustomDataControllerInfo.RecalcSelection;
var
I: Integer;
begin
for I := 0 to Selection.Count - 1 do
begin
if Selection[I].Level = -1 then // It's Group
Selection[I].RowIndex := DataGroups[Selection[I].RecordIndex].RowIndex
else
Selection[I].RowIndex := GetRowIndexByRecordIndex(Selection[I].RecordIndex, False);
end;
end;
procedure TcxCustomDataControllerInfo.ResetFocusing;
begin
if DataController.FInNotifyControl then Exit;
if (dcoFocusTopRowAfterSorting in DataController.Options) and
(DataController.FGroupingChanged or DataController.FSortingChanged or
DataController.FLoadedStorage) then
begin
DataController.FLoadedStorage := False;
FFocusingInfo.Clear;
DoChangeFocusedRow(0, False);
if Selection.Count > 0 then
Selection.ClearAll;
end;
end;
procedure TcxCustomDataControllerInfo.SaveExpanding(ASaveStates: TcxDataExpandingInfoStateSet);
function IsValidInfo: Boolean;
var
I: Integer;
begin
Result := (DataGroups.FieldCount = GroupingFieldList.Count) and
(DataController.RecordCount > 0);
if Result then
for I := 0 to GroupingFieldList.Count - 1 do
if DataGroups.Fields[I] <> GroupingFieldList[I].Field then
begin
Result := False;
Break;
end;
end;
procedure AddGroup(ADataGroupInfo: TcxDataGroupInfo; AGroupIndex: Integer; AExpanded: Boolean);
var
ARecordIndex: Integer;
AInfo: TcxDataGroupInfo;
AStateSet: TcxDataExpandingInfoStateSet;
begin
// begin proc
AInfo := ADataGroupInfo;
while AInfo.Level < (DataGroups.LevelCount - 1) do
AInfo := DataGroups[AInfo.BeginRecordListIndex];
ARecordIndex := GetInternalRecordIndex(AInfo.BeginRecordListIndex);
// if DataController.Provider.IsInserting then // !!!
// begin
// if ARecordIndex >= DataController.Provider.FEditingRecordIndex then
// Inc(ARecordIndex);
// end;
// if ARecordIndex >= DataController.FDeletingRecordIndex then
// Dec(ARecordIndex); // !!!
// end proc
if ARecordIndex < DataController.RecordCount then
begin
AStateSet := [];
if AExpanded and ((Selection.Count > 0) or not IsAlwaysExpanded) then
AStateSet := AStateSet + [eisExpanded];
if (eisSelected in FExpandingInfo.SaveStates) and
(Selection.FindByGroupIndex(AGroupIndex) <> -1) then
AStateSet := AStateSet + [eisSelected];
if AStateSet <> [] then
FExpandingInfo.AddItem(ADataGroupInfo.Level,
DataController.GetComparedValue(ARecordIndex, GroupingFieldList[ADataGroupInfo.Level].Field),
AStateSet);
end;
end;
procedure AddGroups;
var
I, AVisibleLevel: Integer;
ADataGroupInfo: TcxDataGroupInfo;
begin
I := 0;
AVisibleLevel := 0;
while I < DataGroups.Count do
begin
ADataGroupInfo := DataGroups[I];
if ADataGroupInfo.Level <= AVisibleLevel then
begin
if ADataGroupInfo.Expanded then
begin
AddGroup(ADataGroupInfo, I, True);
AVisibleLevel := ADataGroupInfo.Level + 1;
end
else
begin
if eisSelected in FExpandingInfo.SaveStates then
AddGroup(ADataGroupInfo, I, False);
AVisibleLevel := ADataGroupInfo.Level;
end;
end;
Inc(I);
end;
end;
var
I: Integer;
begin
if FExpandingInfo.SaveStates <> ASaveStates then
begin
if (ASaveStates <> []) and IsValidInfo then
begin
FExpandingInfo.SaveStates := ASaveStates;
FExpandingInfo.ClearFields;
for I := 0 to DataGroups.FieldCount - 1 do
FExpandingInfo.AddField(DataGroups.Fields[I]);
if IsGrouped then
AddGroups;
end
else
begin
FExpandingInfo.Clear;
FExpandingInfo.SaveStates := ASaveStates;
end;
end;
end;
procedure TcxCustomDataControllerInfo.TruncateSelection;
var
I, ARecordCount: Integer;
begin
if FSelection.Count = 0 then Exit;
ARecordCount := DataController.RecordCount;
if ARecordCount = 0 then
FSelection.ClearAll
else
if (not DataController.FInDeleteSelection or DataController.FInLoadStorage) and
(not DataController.IsKeyNavigation or DataController.IsSmartRefresh or DataController.IsProviderMode) then
begin
for I := FSelection.Count - 1 downto 0 do
if FSelection[I].RecordIndex >= ARecordCount then
FSelection.Delete(I);
end;
end;
procedure TcxCustomDataControllerInfo.UnprepareSorting;
var
I: Integer;
begin
for I := 0 to FTotalSortingFieldList.Count - 1 do
FTotalSortingFieldList[I].Field.FNeedConversionFlag := 0;
end;
procedure TcxCustomDataControllerInfo.Update;
var
ASummaryChanged: Boolean;
begin
FInfoCalculation := True;
DataController.CheckMode;
if DataController.FSortingBySummaryChanged then
begin
FChanges := FChanges + [dcicSorting{, dcicGrouping, dcicSummary}];
if not IsSortingBySummary then
FChanges := FChanges + [dcicGrouping, dcicSummary];
end;
ASummaryChanged := ((dcicSummary in FChanges) {or SortBySummaryChanged}) and
([csDestroying, csReading] * DataController.FOwner.ComponentState = []);
FExpandingFlag := False;
SaveInfo;
try
if dcicLoad in FChanges then
begin
DoLoad;
DoFilter;
end;
if ASummaryChanged then
DoBeginSummary;
if ([dcicSorting, dcicGrouping] * FChanges <> []) then
DoSort;
if dcicGrouping in FChanges then
begin
DataController.BeforeGroupingNotification;
DoGrouping;
end;
if ([dcicSorting, dcicGrouping] * FChanges <> []) then
ResetFocusing;
CheckInfo; // AB7162
if ASummaryChanged then
DoEndSummary;
if (([dcicSorting, dcicGrouping] * FChanges <> []) or ASummaryChanged) and
IsSortingBySummary then
begin
DoSortBySummary;
CheckInfo; // AB7162
end;
//CheckInfo; AB7162
finally
ClearInfo;
if FExpandingFlag then
RecalcSelection;
if [dcicSorting, dcicExpanding] * FChanges <> [] then
DataController.CorrectPrevSelectionChangedInfo;
FInfoCalculation := False;
ForwardChanges;
end;
end;
procedure TcxCustomDataControllerInfo.DoSortBySummary;
var
AEngine: TcxSortingBySummaryEngine;
begin
if DataController.GetSortingBySummaryEngineClass = nil then Exit;
AEngine := DataController.GetSortingBySummaryEngineClass.Create(Self);
try
AEngine.Sort;
finally
AEngine.Free;
end;
end;
function TcxCustomDataControllerInfo.IsSortingBySummary: Boolean;
var
I: Integer;
begin
Result := False;
if DataController.Summary.Options * [soSelectedRecords, soMultipleSelectedRecords] <> [] then
Exit;
for I := 0 to GroupingFieldList.Count - 1 do
if DataController.Summary.GroupSummaryItems[I].SortedSummaryItem <> nil then
begin
Result := True;
Break;
end;
end;
procedure TcxCustomDataControllerInfo.CheckAfterCollapsing;
var
ANewFocusedRowIndex: Integer;
begin
ANewFocusedRowIndex := FindFocusedRow(True);
if FFocusingInfo.RowIndex <> ANewFocusedRowIndex then
begin
if DataController.IsSmartRefresh then
CorrectFocusedRow(ANewFocusedRowIndex)
else
DoChangeFocusedRow(ANewFocusedRowIndex, False);
end;
// RecalcSelection; // TODO: check selection
end;
procedure TcxCustomDataControllerInfo.CheckExpanding;
var
ASelectionCount, ACurIndex: Integer;
function IsEqualFields: Boolean;
var
I: Integer;
begin
Result := FExpandingInfo.FieldCount = DataGroups.FieldCount;
if Result then
for I := 0 to DataGroups.FieldCount - 1 do
if FExpandingInfo.Fields[I] <> DataGroups.Fields[I] then
begin
Result := False;
Break;
end;
end;
procedure PrepareSelection;
var
I, ALevel, ALevelCount: Integer;
begin
// Clear Groups
ALevelCount := DataGroups.LevelCount;
for I := Selection.Count - 1 downto 0 do
begin
ALevel := Selection[I].Level;
if (ALevel = -1) or (ALevel <> ALevelCount) then
Selection.Delete(I);
end;
end;
function GroupNeedsRestoration(AExpandingInfo: PcxDataExpandingInfo): Boolean;
begin
Result :=
(eisExpanded in AExpandingInfo.State) and (eisExpanded in FExpandingInfo.SaveStates) or
(eisSelected in AExpandingInfo.State);
end;
procedure ExpandGroup(ARecordIndex, ALevel: Integer; AStateSet: TcxDataExpandingInfoStateSet);
var
I, ARecordListIndex: Integer;
begin
// begin proc
ARecordListIndex := GetInternalRecordListIndex(ARecordIndex);
I := DataGroups.FindByIndex(ARecordListIndex, ALevel);
// end proc
if I <> -1 then
begin
if (eisExpanded in AStateSet) and (eisExpanded in FExpandingInfo.SaveStates) then
DataGroups[I].Expanded := True;
if eisSelected in AStateSet then
Selection.Add(-1, -1, I, -1);
end;
end;
procedure ExpandGroups;
var
ALocateObject: TcxDataControllerGroupLocateObject;
AValueDefReader: TcxValueDefUnboundReader;
I, ARecordIndex: Integer;
PExpandingInfo: PcxDataExpandingInfo;
begin
ALocateObject := TcxDataControllerGroupLocateObject.Create(DataController);
try
AValueDefReader := TcxValueDefUnboundReader.Create;
try
ALocateObject.Level := 0;
for I := 0 to FExpandingInfo.Count - 1 do
begin
PExpandingInfo := FExpandingInfo[I];
if PExpandingInfo.Level < ALocateObject.Level then
AValueDefReader.Truncate(PExpandingInfo.Level);
AValueDefReader.SetValue(FExpandingInfo.Fields[PExpandingInfo.Level].ValueDef,
PExpandingInfo.Value);
if GroupNeedsRestoration(PExpandingInfo) then
begin
ALocateObject.Level := PExpandingInfo.Level;
ALocateObject.ReadData(AValueDefReader);
ARecordIndex := ALocateObject.FindRecordIndex;
if ARecordIndex <> -1 then
ExpandGroup(ARecordIndex, ALocateObject.Level, PExpandingInfo.State);
end;
end;
finally
AValueDefReader.Free;
end;
finally
ALocateObject.Free;
end;
end;
procedure RebuildSelection;
procedure CheckGroup(ADataGroupInfo: TcxDataGroupInfo);
begin
if (ADataGroupInfo.Level = (DataGroups.LevelCount - 1)) then
begin
// Delete Hidden
while (ACurIndex < ASelectionCount) and
(Selection[ACurIndex].RowIndex < ADataGroupInfo.BeginRecordListIndex) do
begin
Selection.Delete(ACurIndex);
Dec(ASelectionCount);
end;
// Skip Visible
if ADataGroupInfo.Expanded then
while (ACurIndex < ASelectionCount) and
(ADataGroupInfo.BeginRecordListIndex <= Selection[ACurIndex].RowIndex) and
(Selection[ACurIndex].RowIndex <= ADataGroupInfo.EndRecordListIndex) do
begin
Selection[ACurIndex].RowIndex := ADataGroupInfo.RowIndex +
(Selection[ACurIndex].RowIndex - ADataGroupInfo.BeginRecordListIndex + 1);
Inc(ACurIndex);
end;
end;
end;
var
I, AVisibleLevel, AStartIndex: Integer;
ADataGroupInfo: TcxDataGroupInfo;
begin
if Selection.Count = 0 then Exit;
{
1 - first is groups (with RowIndex = -1), second - Data Rows (with RowIndex = RecordListIndex)
2 - locate begin of Data Rows
3 - process Data Rows
if visible then correct RowIndex
else delete
4 - fill RowIndex for Groups (Level = -1)
sort by RowIndex
}
// 1 - Sort Selection By <RecordListIndex>: write RecordListIndex -> RowIndex (temporal)
for I := 0 to Selection.Count - 1 do
if Selection[I].Level <> -1 then
Selection[I].RowIndex := GetInternalRecordListIndex(Selection[I].RecordIndex)
else
Selection[I].RowIndex := -1;
Selection.Sort;
// 2 -
ASelectionCount := Selection.Count;
AStartIndex := ASelectionCount;
for I := 0 to ASelectionCount - 1 do
if Selection[I].RowIndex > -1 then
begin
AStartIndex := I;
Break;
end;
// 3 -
I := 0;
ACurIndex := AStartIndex;
AVisibleLevel := 0;
while (I < DataGroups.Count) and (ACurIndex < ASelectionCount) do
begin
ADataGroupInfo := DataGroups[I];
if ADataGroupInfo.Level <= AVisibleLevel then
begin
CheckGroup(ADataGroupInfo);
if ADataGroupInfo.Expanded then
AVisibleLevel := ADataGroupInfo.Level + 1
else
AVisibleLevel := ADataGroupInfo.Level;
end;
Inc(I);
end;
// Clear Hidden
for I := ACurIndex to ASelectionCount - 1 do
begin
Selection.Delete(ACurIndex);
Dec(ASelectionCount);
end;
// 4 -
for I := 0 to AStartIndex - 1 do // for Group's
Selection[I].RowIndex := DataGroups[Selection[I].RecordIndex].RowIndex;
Selection.Sort;
end;
procedure RestoreRowsSelection;
var
I: Integer;
begin
if DataController.IsGridMode then Exit;
for I := 0 to Selection.Count - 1 do
Selection[I].RowIndex := GetInternalRecordListIndex(Selection[I].RecordIndex);
Selection.Sort;
end;
var
ATempIndex: Integer;
begin
if IsGrouped and IsAlwaysExpanded then
DataGroups.FullExpanding(True);
if FExpandingInfo.SaveStates <> [] then
begin
if IsEqualFields then
begin
PrepareSelection;
if IsGrouped then
begin
try
// if not IsAlwaysExpanded then
ExpandGroups;
finally
if not IsAlwaysExpanded and (eisExpanded in FExpandingInfo.SaveStates) then
DataGroups.Rebuild;
RebuildSelection;
end;
end
else
if eisSelected in FExpandingInfo.SaveStates then
RestoreRowsSelection;
// check Selection Anchor
if (Selection.AnchorRowIndex <> -1) and
not Selection.FindByRowIndex(Selection.AnchorRowIndex, ATempIndex) then
Selection.ClearAnchor;
end
else
begin
if eisSelected in FExpandingInfo.SaveStates then
Selection.ClearAll;
end;
end;
end;
procedure TcxCustomDataControllerInfo.CheckFocusing;
var
ANewFocusedRowIndex: Integer;
begin
ANewFocusedRowIndex := FindFocusedRow(False);
if FFocusingInfo.RowIndex <> ANewFocusedRowIndex then
begin
// check old row index
if (0 <= FFocusingInfo.RowIndex) and (FFocusingInfo.RowIndex < GetRowCount) and
(FFocusingInfo.RecordIndex = GetRowInfo(FFocusingInfo.RowIndex).RecordIndex) then
// do nothing
else
DoChangeFocusedRow(ANewFocusedRowIndex, True);
end;
if (dcicFocusedRow in FChanges) and (FPrevFocusingInfo.RowIndex >= GetRowCount) then
FPrevFocusingInfo.RowIndex := -1;
if DataController.FFilterChanged then
CheckFocusingAfterFilter;
end;
procedure TcxCustomDataControllerInfo.CheckFocusingAfterFilter;
begin
if (FocusedRowIndex = -1) and not DataController.NewItemRowFocused then
begin
DataController.FFilterChanged := False;
if {DataController.IsSmartRefresh and} (DataController.Provider.LocateCount = 0) then
CorrectFocusedRow(0);
end;
end;
procedure TcxCustomDataControllerInfo.CheckInfo;
begin
CheckExpanding;
CheckFocusing;
DataController.CheckBookmarkValid(-1);
end;
procedure TcxCustomDataControllerInfo.ClearInfo;
begin
FExpandingInfo.Clear;
end;
function TcxCustomDataControllerInfo.CompareGroupRecords(ARecordIndex1, ARecordIndex2, AIndex: Integer): Integer;
var
AI, A1, A2: Integer;
begin
Result := 0;
if ARecordIndex1 = ARecordIndex2 then
Exit;
if DataController.Provider.FSavedRecordIndex <> 0 then
begin
if ARecordIndex1 = DataController.Provider.FEditingRecordIndex then
ARecordIndex1 := DataController.Provider.FSavedRecordIndex
else
if ARecordIndex2 = DataController.Provider.FEditingRecordIndex then
ARecordIndex2 := DataController.Provider.FSavedRecordIndex;
end
else
begin
// check Inserting - see CompareRecords
A1 := DataController.Provider.FEditingRecordIndex1;
A2 := DataController.Provider.FEditingRecordIndex2;
if (A1 <> cxNullEditingRecordIndex) or (A2 <> cxNullEditingRecordIndex) then
begin
AI := DataController.Provider.FEditingRecordIndex;
if ARecordIndex1 = AI then
begin
if A1 <> cxNullEditingRecordIndex then // appending
begin
Result := 0; // !!!
Exit;
end
else // inserting
if ARecordIndex2 = A2 then
begin
Result := 0; // !!!
Exit;
end
else
ARecordIndex1 := A2;
end
else
if ARecordIndex2 = AI then
begin
if A1 <> cxNullEditingRecordIndex then // appending
begin
Result := 0; // !!!
Exit;
end
else // inserting
if ARecordIndex1 = A2 then
begin
Result := 0; // !!!
Exit;
end
else
ARecordIndex2 := A2;
end;
end;
end;
// compare
Result := DataController.CompareRecords(ARecordIndex1, ARecordIndex2,
FTotalSortingFieldList[AIndex], dccmGrouping);
end;
function TcxCustomDataControllerInfo.CompareRecords(ARecord1, ARecord2: Pointer): Integer;
var
I, AI, A1, A2: Integer;
begin
Result := 0;
if ARecord1 = ARecord2 then
Exit;
// check Inserting
A1 := DataController.Provider.FEditingRecordIndex1;
A2 := DataController.Provider.FEditingRecordIndex2;
if (A1 <> cxNullEditingRecordIndex) or (A2 <> cxNullEditingRecordIndex) then
begin
AI := DataController.Provider.FEditingRecordIndex;
if Integer(ARecord1) = AI then
begin
if A1 <> cxNullEditingRecordIndex then // appending
begin
Result := 1;
Exit;
end
else // inserting
if Integer(ARecord2) = A2 then
begin
Result := -1;
Exit;
end
else
Integer(ARecord1) := A2;
end
else
if Integer(ARecord2) = AI then
begin
if A1 <> cxNullEditingRecordIndex then // appending
begin
Result := -1;
Exit;
end
else // inserting
if Integer(ARecord1) = A2 then
begin
Result := 1;
Exit;
end
else
Integer(ARecord2) := A2;
end;
end;
// compare
for I := 0 to FTotalSortingFieldList.Count - 1 do
begin
Result := DataController.CompareRecords(Integer(ARecord1), Integer(ARecord2),
FTotalSortingFieldList[I], dccmSorting);
if Result <> 0 then Exit;
end;
if Result = 0 then
Result := DataController.CompareEqualRecords(Integer(ARecord1), Integer(ARecord2));
end;
function TcxCustomDataControllerInfo.GetFocusedRecordIndex: Integer;
begin
if FFocusingInfo.RowIndex <> -1 then
Result := GetRowInfo(FFocusingInfo.RowIndex).RecordIndex
else
Result := -1;
end;
function TcxCustomDataControllerInfo.GetFocusedRowIndex: Integer;
begin
Result := FFocusingInfo.RowIndex;
end;
function TcxCustomDataControllerInfo.GetNewItemRowFocusingChanged: Boolean;
begin
Result := FPrevFocusingInfo.FChangedFlag;
end;
function TcxCustomDataControllerInfo.GetPrevFocusedRecordIndex: Integer;
begin
Result := FPrevFocusingInfo.FPrevRecordIndex;
end;
function TcxCustomDataControllerInfo.GetPrevFocusedRowIndex: Integer;
begin
Result := FPrevFocusingInfo.RowIndex;
end;
procedure TcxCustomDataControllerInfo.SaveInfo;
var
ASaveStates: TcxDataExpandingInfoStateSet;
begin
ASaveStates := [];
if (dcoSaveExpanding in DataController.Options) or (Selection.Count > 0) then
begin
if (dcoSaveExpanding in DataController.Options) and
([dcicLoad, dcicGrouping] * FChanges <> []) then
ASaveStates := ASaveStates + [eisExpanded];
if (Selection.Count > 0) and
([dcicLoad, dcicGrouping, dcicSorting, dcicExpanding] * FChanges <> []) then
ASaveStates := ASaveStates + [eisSelected];
end;
SaveExpanding(ASaveStates);
end;
procedure TcxCustomDataControllerInfo.SetFocusedRowIndex(Value: Integer);
begin
DataController.BeginFullUpdate;
//BeginUpdate;
try
DoChangeFocusedRow(Value, False);
finally
// EndUpdate;
DataController.EndFullUpdate;
end;
end;
{ TcxDataFilterCriteriaItem }
function TcxDataFilterCriteriaItem.GetDataValue(AData: TObject): Variant;
begin
if Assigned(Field) then
Result := DataController.GetFilterDataValue(Integer(AData), Field)
else
Result := Null;
end;
function TcxDataFilterCriteriaItem.GetFieldCaption: string;
begin
if Assigned(Field) and (Field.Item <> nil) then
Result := DataController.GetFilterItemFieldCaption(Field.Item)
else
Result := '';
end;
function TcxDataFilterCriteriaItem.GetFieldName: string;
begin
if Assigned(Field) and (Field.Item <> nil) then
Result := DataController.GetFilterItemFieldName(Field.Item)
else
Result := '';
end;
function TcxDataFilterCriteriaItem.GetItemLink: TObject;
begin
if Assigned(Field) then
Result := Field.Item
else
Result := nil; // !!!
end;
procedure TcxDataFilterCriteriaItem.SetItemLink(Value: TObject);
begin
if not (Value is TcxCustomDataField) then
Value := DataController.Fields.FieldByItem(Value);
inherited;
end;
function TcxDataFilterCriteriaItem.GetDataController: TcxCustomDataController;
begin
Result := (Criteria as TcxDataFilterCriteria).DataController;
end;
function TcxDataFilterCriteriaItem.GetField: TcxCustomDataField;
begin
Result := inherited GetItemLink as TcxCustomDataField;
end;
{ TcxDataFilterValueList }
procedure TcxDataFilterValueList.Load(AItemIndex: Integer; AInitSortByDisplayText: Boolean = True;
AUseFilteredValues: Boolean = False);
procedure LoadRecords(var AUseFilteredRecords: Boolean; out ANullExists: Boolean);
var
AField: TcxCustomDataField;
I, ARecordIndex: Integer;
V: Variant;
begin
ANullExists := False;
AField := DataController.Fields[AItemIndex];
if AUseFilteredRecords and (DataController.FilteredRecordCount <> 0) then
I := DataController.FilteredRecordCount
else
begin
AUseFilteredRecords := False;
I := DataController.RecordCount;
end;
for I := 0 to I - 1 do
begin
// TODO: ignore editing record
if AUseFilteredRecords then
ARecordIndex := DataController.FilteredRecordIndex[I]
else
ARecordIndex := I;
V := DataController.GetFilterDataValue(ARecordIndex, AField);
if Criteria.ValueIsNull(V) then
ANullExists := True
else
Add(fviValue, V, DataController.GetFilterDisplayText(ARecordIndex, AItemIndex), False);
end;
end;
function NonBlanksFilterApplied: Boolean;
var
ACriteriaItem: TcxFilterCriteriaItem;
begin
ACriteriaItem := Criteria.FindItemByItemLink(DataController.GetItem(AItemIndex));
Result := (ACriteriaItem <> nil) and (ACriteriaItem.Operator is TcxFilterNotNullOperator);
end;
var
ANullExists: Boolean;
begin
Clear;
if AInitSortByDisplayText then
// SortByDisplayText := DataController.GetItemValueSource(AItemIndex) <> evsText; // TODO: Text for Lookup Field!!!
SortByDisplayText := DataController.IsFilterItemSortByDisplayText(AItemIndex);
Add(fviAll, Null, cxSFilterString(@cxSFilterBoxAllCaption), False);
Add(fviCustom, Null, cxSFilterString(@cxSFilterBoxCustomCaption), False);
if DataController.IsGridMode then
ANullExists := True // custom loading
else
LoadRecords(AUseFilteredValues, ANullExists);
if ANullExists then
Add(fviBlanks, Null, cxSFilterString(@cxSFilterBoxBlanksCaption), False);
if ANullExists or AUseFilteredValues and NonBlanksFilterApplied then
Add(fviNonBlanks, Null, cxSFilterString(@cxSFilterBoxNonBlanksCaption), False);
if Assigned((Criteria as TcxDataFilterCriteria).FOnGetValueList) then
(Criteria as TcxDataFilterCriteria).FOnGetValueList(Criteria, AItemIndex, Self);
end;
function TcxDataFilterValueList.GetDataController: TcxCustomDataController;
begin
Result := (Criteria as TcxDataFilterCriteria).DataController;
end;
{ TcxDataFilterCriteria }
constructor TcxDataFilterCriteria.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FDataController := ADataController;
Version := cxDataFilterVersion;
end;
destructor TcxDataFilterCriteria.Destroy;
begin
FDestroying := True;
if (FDataController <> nil) and (FDataController.FFilters <> nil) then
FDataController.FFilters.Remove(Self);
Active := False;
inherited Destroy;
end;
procedure TcxDataFilterCriteria.Assign(Source: TPersistent; AIgnoreItemNames: Boolean = False);
begin
if Source is TcxDataFilterCriteria then
begin
BeginUpdate;
try
inherited;
Active := TcxDataFilterCriteria(Source).Active;
finally
EndUpdate;
end;
end
else
inherited;
end;
procedure TcxDataFilterCriteria.AssignEvents(Source: TPersistent);
begin
inherited AssignEvents(Source);
if Source is TcxDataFilterCriteria then
OnGetValueList := TcxDataFilterCriteria(Source).OnGetValueList;
end;
function TcxDataFilterCriteria.CreateValueList: TcxDataFilterValueList;
begin
Result := GetValueListClass.Create(Self) as TcxDataFilterValueList;
end;
function TcxDataFilterCriteria.FindItemByItemLink(AItemLink: TObject): TcxFilterCriteriaItem;
begin
if not (AItemLink is TcxCustomDataField) then
AItemLink := DataController.Fields.FieldByItem(AItemLink);
Result := inherited FindItemByItemLink(AItemLink);
end;
function TcxDataFilterCriteria.IsFiltering: Boolean;
begin
Result := Active and not IsEmpty and not DataController.IsGridMode;
end;
procedure TcxDataFilterCriteria.RemoveItemByField(AField: TcxCustomDataField);
begin
RemoveItemByItemLink(AField);
end;
procedure TcxDataFilterCriteria.DefineProperties(Filer: TFiler);
begin
inherited DefineProperties(Filer);
Filer.DefineBinaryProperty('Criteria', ReadData, WriteData, IsStore);
end;
function TcxDataFilterCriteria.DoFilterRecord(ARecordIndex: Integer): Boolean;
begin
Result := DoFilterData(TObject(ARecordIndex));
end;
function TcxDataFilterCriteria.GetIDByItemLink(AItemLink: TObject): Integer;
begin
Result := DataController.GetItemID(AItemLink);
end;
function TcxDataFilterCriteria.GetNameByItemLink(AItemLink: TObject): string;
begin
Result := DataController.GetItemName(AItemLink);
end;
function TcxDataFilterCriteria.GetItemClass: TcxFilterCriteriaItemClass;
begin
Result := TcxDataFilterCriteriaItem;
end;
function TcxDataFilterCriteria.GetItemLinkByID(AID: Integer): TObject;
begin
Result := DataController.FindItemByInternalID(AID);
end;
function TcxDataFilterCriteria.GetItemLinkByName(const AName: string): TObject;
begin
Result := DataController.FindItemByName(AName);
end;
function TcxDataFilterCriteria.GetValueListClass: TcxFilterValueListClass;
begin
Result := TcxDataFilterValueList;
end;
function TcxDataFilterCriteria.IsDestroying: Boolean;
begin
Result := Destroying or DataController.IsDestroying;
end;
function TcxDataFilterCriteria.IsInternal: Boolean;
begin
Result := (DataController <> nil) and (DataController.FFilters.IndexOf(Self) <> -1);
end;
function TcxDataFilterCriteria.IsLoading: Boolean;
begin
Result := DataController.IsLoading;
end;
procedure TcxDataFilterCriteria.Update;
begin
if not (IsInternal or IsDestroying or IsLoading) then
begin
if not DataController.Provider.IsDataSource then
DataController.Post;
DataController.FilterChanged;
if Assigned(OnChanged) then
OnChanged(Self);
end;
end;
procedure TcxDataFilterCriteria.SetActive(Value: Boolean);
begin
if FActive <> Value then
begin
FActive := Value;
Changed;
end;
end;
{ TcxDataFilterList }
procedure TcxDataFilterList.Clear;
begin
while Count > 0 do
Items[Count - 1].Free;
inherited Clear;
end;
procedure TcxDataFilterList.RemoveItemByField(AField: TcxCustomDataField);
var
I: Integer;
begin
for I := 0 to Count - 1 do
Items[I].RemoveItemByField(AField);
end;
function TcxDataFilterList.GetItem(Index: Integer): TcxDataFilterCriteria;
begin
Result := TcxDataFilterCriteria(inherited Items[Index]);
end;
{ TcxDataControllerGroups }
constructor TcxDataControllerGroups.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FDataController := ADataController;
end;
procedure TcxDataControllerGroups.ChangeExpanding(ARowIndex: Integer;
AExpanded, ARecursive: Boolean);
begin
DataControllerInfo.ChangeExpanding(ARowIndex, AExpanded, ARecursive);
end;
procedure TcxDataControllerGroups.ChangeGrouping(AItemIndex, AGroupIndex: Integer);
begin
DataController.CheckItemRange(AItemIndex);
if GetItemGroupIndex(AItemIndex) <> AGroupIndex then
begin
DataController.BeforeSorting;
DataController.FGroupingChanged := True;
DataControllerInfo.ChangeGrouping(DataController.Fields[AItemIndex], AGroupIndex);
end;
end;
procedure TcxDataControllerGroups.ClearGrouping;
begin
if GetGroupingItemCount > 0 then
begin
DataController.BeforeSorting;
DataController.FGroupingChanged := True;
end;
DataControllerInfo.ClearGrouping;
end;
procedure TcxDataControllerGroups.FullCollapse;
begin
DataControllerInfo.FullCollapse;
end;
procedure TcxDataControllerGroups.FullExpand;
begin
DataControllerInfo.FullExpand;
end;
function TcxDataControllerGroups.GetDataGroupIndexByGroupValue(AParentDataGroupIndex: TcxDataGroupIndex;
const AGroupValue: Variant): TcxDataGroupIndex;
var
I: Integer;
AChildDataGroupIndex: TcxDataGroupIndex;
begin
Result := -1;
{ if (DataControllerInfo.LockCount > 0) and (DataControllerInfo.FChanges <> []) then
Exit; // !!!}
DataControllerInfo.PrepareSorting;
try
for I := 0 to ChildCount[AParentDataGroupIndex] - 1 do
begin
AChildDataGroupIndex := ChildDataGroupIndex[AParentDataGroupIndex, I];
if VarEquals(GroupValues[AChildDataGroupIndex] , AGroupValue) then
begin
Result := AChildDataGroupIndex;
Break;
end;
end;
finally
DataControllerInfo.UnprepareSorting;
end;
end;
function TcxDataControllerGroups.HasAsParent(ARowIndex, AParentRowIndex: Integer): Boolean;
var
AIndex, AParentIndex: TcxDataGroupIndex;
begin
if LevelCount = 0 then
begin
Result := False;
Exit;
end;
AParentIndex := DataGroupIndexByRowIndex[AParentRowIndex];
AIndex := DataGroupIndexByRowIndex[ARowIndex];
Result := (AParentIndex = AIndex) or
((Level[AParentIndex] < Level[AIndex]) and
(DataGroups[AParentIndex].BeginRecordListIndex <= AIndex) and
(AIndex <= DataGroups[AParentIndex].EndRecordListIndex));
end;
procedure TcxDataControllerGroups.LoadRecordIndexes(AList: TList; ADataGroupIndex: TcxDataGroupIndex);
var
I, AChildCount: Integer;
begin
AChildCount := ChildCount[ADataGroupIndex];
if Level[ADataGroupIndex] < LevelCount - 1 then // It's Sub Groups
begin
for I := 0 to AChildCount - 1 do
LoadRecordIndexes(AList, ChildDataGroupIndex[ADataGroupIndex, I]);
end
else
for I := 0 to AChildCount - 1 do
AList.Add(Pointer(ChildRecordIndex[ADataGroupIndex, I]));
end;
procedure TcxDataControllerGroups.LoadRecordIndexesByRowIndex(AList: TList; ARowIndex: Integer);
begin
LoadRecordIndexes(AList, DataGroupIndexByRowIndex[ARowIndex]);
end;
function TcxDataControllerGroups.GetGroupRecordIndex(ADataGroupIndex: TcxDataGroupIndex): Integer;
var
ARecordDataGroupIndex: TcxDataGroupIndex;
begin
ARecordDataGroupIndex := ADataGroupIndex;
while Level[ARecordDataGroupIndex] < LevelCount - 1 do // It's Sub Groups
ARecordDataGroupIndex := ChildDataGroupIndex[ARecordDataGroupIndex, 0];
Result := ChildRecordIndex[ARecordDataGroupIndex, 0];
end;
function TcxDataControllerGroups.GetChildCount(DataGroupIndex: TcxDataGroupIndex): Integer;
begin
Result := DataControllerInfo.DataGroups.GetChildCount(DataGroupIndex);
end;
function TcxDataControllerGroups.GetChildDataGroupIndex(ParentDataGroupIndex: TcxDataGroupIndex; ChildIndex: Integer): TcxDataGroupIndex;
begin
Result := DataControllerInfo.DataGroups.GetChildIndex(ParentDataGroupIndex, ChildIndex);
end;
function TcxDataControllerGroups.GetChildRecordIndex(ParentDataGroupIndex: TcxDataGroupIndex;
ChildIndex: Integer): Integer;
var
I: Integer;
begin
I := DataControllerInfo.DataGroups.GetChildRecordListIndex(ParentDataGroupIndex, ChildIndex);
if I <> -1 then
Result := DataControllerInfo.GetInternalRecordIndex(I)
else
Result := -1;
end;
function TcxDataControllerGroups.GetDataControllerInfo: TcxCustomDataControllerInfo;
begin
Result := DataController.DataControllerInfo;
end;
function TcxDataControllerGroups.GetDataGroupIndexByRowIndex(RowIndex: Integer): TcxDataGroupIndex;
begin
Result := DataControllerInfo.DataGroups.GetIndexByRowIndex(RowIndex);
end;
function TcxDataControllerGroups.GetDataGroups: TcxDataGroups;
begin
Result := DataControllerInfo.DataGroups;
end;
function TcxDataControllerGroups.GetFieldGroupIndex(AField: TcxCustomDataField): Integer;
begin
Result := DataControllerInfo.GroupingFieldList.GroupIndexByField(AField);
end;
function TcxDataControllerGroups.GetGroupingItemCount: Integer;
begin
Result := DataControllerInfo.GroupingFieldList.Count;
end;
function TcxDataControllerGroups.GetGroupingItemIndex(Index: Integer): Integer;
begin
Result := DataControllerInfo.GroupingFieldList[Index].Field.Index;
end;
function TcxDataControllerGroups.GetGroupDisplayText(ADataGroupIndex: TcxDataGroupIndex): string;
var
ARecordIndex, ALevel: Integer;
AField: TcxCustomDataField;
begin
ARecordIndex := GetGroupRecordIndex(ADataGroupIndex);
ALevel := Level[ADataGroupIndex];
AField := DataControllerInfo.GroupingFieldList[ALevel].Field;
Result := DataController.GetDisplayText(ARecordIndex, AField.Index);
end;
function TcxDataControllerGroups.GetGroupValue(ADataGroupIndex: TcxDataGroupIndex): Variant;
var
ARecordIndex, ALevel: Integer;
AField: TcxCustomDataField;
begin
ARecordIndex := GetGroupRecordIndex(ADataGroupIndex);
ALevel := Level[ADataGroupIndex];
AField := DataControllerInfo.GroupingFieldList[ALevel].Field;
Result := DataController.GetComparedValue(ARecordIndex, AField);
end;
function TcxDataControllerGroups.GetItemGroupIndex(AItemIndex: Integer): Integer;
begin
DataController.CheckItemRange(AItemIndex);
Result := FieldGroupIndex[DataController.Fields[AItemIndex]];
end;
function TcxDataControllerGroups.GetLevel(ADataGroupIndex: TcxDataGroupIndex): Integer;
begin
Result := DataControllerInfo.DataGroups.GetLevel(ADataGroupIndex);
end;
function TcxDataControllerGroups.GetParentDataGroupIndex(ChildDataGroupIndex: TcxDataGroupIndex): TcxDataGroupIndex;
begin
Result := DataControllerInfo.DataGroups.GetParentIndex(ChildDataGroupIndex);
end;
{ TcxCustomDataSummaryItem }
procedure TcxCustomDataSummaryItem.Assign(Source: TPersistent);
begin
if Source is TcxCustomDataSummaryItem then
begin
BeginUpdate;
try
AssignValues(TcxCustomDataSummaryItem(Source));
finally
EndUpdate;
end;
end
else
inherited Assign(Source);
end;
procedure TcxCustomDataSummaryItem.BeginUpdate;
begin
if Assigned(Collection) then Collection.BeginUpdate;
end;
procedure TcxCustomDataSummaryItem.EndUpdate;
begin
if Assigned(Collection) then Collection.EndUpdate;
end;
function TcxCustomDataSummaryItem.FormatValue(const AValue: Variant;
AIsFooter: Boolean): string;
var
S: string;
begin
if not VarIsNull(AValue) then
begin
if Kind <> skNone then
begin
S := GetValueFormat(GetValueType(VarType(AValue)), Kind, AIsFooter);
if (VarType(AValue) = varDate) and (Kind <> skCount) then
begin
if TimeOf(AValue) = 0 then
Result := DateToStr(AValue)
else
if DateOf(AValue) = 0 then
Result := TimeToStr(AValue)
else
Result := DateTimeToStr(AValue);
Result := S + Result;
end
else
Result := FormatFloat(S, AValue);
end
else
Result := VarToStr(AValue);
end
else
Result := '';
end;
procedure TcxCustomDataSummaryItem.AssignValues(ASource: TcxCustomDataSummaryItem);
begin
if DataController <> nil then
ItemLink := DataController.FindProperItemLink(ASource.ItemLink);
Format := ASource.Format;
Kind := ASource.Kind;
end;
function TcxCustomDataSummaryItem.GetValueFormat(AValueType: TcxSummaryValueType;
const AValue: Variant; AIsFooter: Boolean): string;
begin
Result := Format;
end;
function TcxCustomDataSummaryItem.GetValueType(
AVarType: TVarType): TcxSummaryValueType;
begin
if IsCurrency(AVarType) then
Result := svtCurrency
else
begin
case AVarType of
varDate:
Result := svtDate;
else
Result := svtFloat;
end;
end;
end;
function TcxCustomDataSummaryItem.IsCurrency(AVarType: TVarType): Boolean;
begin
Result := VarTypeIsCurrency(AVarType);
end;
procedure TcxCustomDataSummaryItem.ItemLinkChanging(AField: TcxCustomDataField);
begin
end;
function TcxCustomDataSummaryItem.GetItemLink: TObject;
begin
if Assigned(Field) then
Result := Field.Item
else
Result := nil;
end;
procedure TcxCustomDataSummaryItem.SetFormat(const Value: string);
begin
if FFormat <> Value then
begin
FFormat := Value;
Changed(False);
end;
end;
procedure TcxCustomDataSummaryItem.SetItemLink(Value: TObject);
var
AField: TcxCustomDataField;
begin
if DataController = nil then Exit;
if ItemLink <> Value then
begin
AField := DataController.Fields.FieldByItem(Value);
BeginUpdate;
try
ItemLinkChanging(AField);
FField := AField;
Changed(True);
finally
EndUpdate;
end;
end;
end;
procedure TcxCustomDataSummaryItem.SetKind(Value: TcxSummaryKind);
begin
if FKind <> Value then
begin
FKind := Value;
Changed(True);
end;
end;
{ TcxDataSummaryItem }
destructor TcxDataSummaryItem.Destroy;
begin
Sorted := False;
inherited Destroy;
end;
procedure TcxDataSummaryItem.Assign(Source: TPersistent);
begin
inherited Assign(Source);
if Source is TcxDataSummaryItem then
Tag := TcxDataSummaryItem(Source).Tag;
end;
function TcxDataSummaryItem.DataField: TcxCustomDataField;
begin
Result := Field;
end;
function TcxDataSummaryItem.FormatValue(const AValue: Variant; AIsFooter: Boolean): string;
begin
Result := inherited FormatValue(AValue, AIsFooter);
if Assigned(FOnGetText) then
FOnGetText(Self, AValue, AIsFooter, Result);
end;
procedure TcxDataSummaryItem.AssignValues(Source: TcxCustomDataSummaryItem);
begin
inherited;
Position := TcxDataSummaryItem(Source).Position;
Sorted := TcxDataSummaryItem(Source).Sorted;
OnGetText := TcxDataSummaryItem(Source).OnGetText;
end;
function TcxDataSummaryItem.GetDataController: TcxCustomDataController;
begin
Result := SummaryItems.DataController;
end;
function TcxDataSummaryItem.GetValueFormat(AValueType: TcxSummaryValueType; const AValue: Variant;
AIsFooter: Boolean): string;
begin
Result := inherited GetValueFormat(AValueType, AValue, AIsFooter);
if Result = '' then
Result := SummaryItems.DefaultFormat(AValueType, Kind, AIsFooter);
end;
function TcxDataSummaryItem.IsDataBinded: Boolean;
begin
Result := (DataField <> nil) or ((Kind = skCount) and (Position = spGroup));
end;
function TcxDataSummaryItem.IsPositionStored: Boolean;
begin
Result := SummaryItems.IsPositionStored(Self);
end;
function TcxDataSummaryItem.GetPosition: TcxSummaryPosition;
begin
Result := SummaryItems.GetItemPosition(Self);
end;
function TcxDataSummaryItem.GetSummaryItems: TcxDataSummaryItems;
begin
if Assigned(Collection) and (Collection is TcxDataSummaryItems) then
Result := TcxDataSummaryItems(Collection)
else
Result := nil;
end;
procedure TcxDataSummaryItem.SetPosition(Value: TcxSummaryPosition);
begin
if Position <> Value then
begin
FPosition := Value;
Changed(False);
end;
end;
procedure TcxDataSummaryItem.SetSorted(Value: Boolean);
begin
if DataController.SortingBySummaryDataItemIndex <> -1 then Exit;
if FSorted <> Value then
begin
BeginUpdate;
try
SummaryItems.BeforeSortingChange(Self, Value);
FSorted := Value;
DataController.DoSortingBySummaryChanged;
Changed(True);
finally
EndUpdate;
end;
end;
end;
{ TcxDataSummaryItems }
constructor TcxDataSummaryItems.Create(ASummary: TcxDataSummary; AItemClass: TcxDataSummaryItemClass);
begin
inherited Create(AItemClass);
FSummary := ASummary;
end;
function TcxDataSummaryItems.Add: TcxDataSummaryItem;
begin
Result := TcxDataSummaryItem(inherited Add);
end;
function TcxDataSummaryItems.Add(AItemLink: TObject; APosition: TcxSummaryPosition;
AKind: TcxSummaryKind; const AFormat: string = ''): TcxDataSummaryItem;
begin
BeginUpdate;
try
Result := Add;
Result.ItemLink := AItemLink;
Result.Position := APosition;
Result.Kind := AKind;
Result.Format := AFormat;
finally
EndUpdate;
end;
end;
procedure TcxDataSummaryItems.Assign(Source: TPersistent);
begin
inherited Assign(Source);
AssignEvents(Source);
end;
procedure TcxDataSummaryItems.AssignEvents(Source: TPersistent);
begin
if Source is TcxDataSummaryItems then
OnSummary := TcxDataSummaryItems(Source).OnSummary;
end;
function TcxDataSummaryItems.DefaultFormat(AValueType: TcxSummaryValueType;
ASummaryKind: TcxSummaryKind; AIsFooter: Boolean): string;
begin
Result := cxDataGetDataSummaryValueDefaultFormat(AValueType, ASummaryKind, AIsFooter);
end;
procedure TcxDataSummaryItems.DeleteItems(AItemLink: TObject; APosition: TcxSummaryPosition);
var
I: Integer;
AItem: TcxDataSummaryItem;
begin
BeginUpdate;
try
for I := Count - 1 downto 0 do
begin
AItem := Items[I];
if (AItem.ItemLink = AItemLink) and (AItem.Position = APosition) then
AItem.Free;
end;
finally
EndUpdate;
end;
end;
function TcxDataSummaryItems.FindByTag(ATag: Longint): TcxDataSummaryItem;
var
I: Integer;
begin
Result := nil;
for I := 0 to Count - 1 do
if Items[I].Tag = ATag then
begin
Result := Items[I];
Break;
end;
end;
function TcxDataSummaryItems.GetGroupText(const ASummaryValues: Variant): string;
begin
Result := '';
end;
function TcxDataSummaryItems.IndexOf(AItem: TcxDataSummaryItem): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to Count - 1 do
if Items[I] = AItem then
begin
Result := I;
Break;
end;
end;
function TcxDataSummaryItems.IndexOfItemLink(AItemLink: TObject): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to Count - 1 do
if Items[I].ItemLink = AItemLink then
begin
Result := I;
Break;
end;
end;
function TcxDataSummaryItems.ItemOfItemLink(AItemLink: TObject): TcxDataSummaryItem;
var
AIndex: Integer;
begin
AIndex := IndexOfItemLink(AItemLink);
if AIndex <> -1 then
Result := Items[AIndex]
else
Result := nil;
end;
function TcxDataSummaryItems.GetDataItem(AItemIndex: Integer; APosition: TcxSummaryPosition;
ACheckKind: Boolean = False; AKind: TcxSummaryKind = skNone): TcxDataSummaryItem;
var
I: Integer;
AField: TcxCustomDataField;
begin
DataController.CheckItemRange(AItemIndex);
Result := nil;
AField := DataController.Fields[AItemIndex];
for I := 0 to Count - 1 do
if (Items[I].Field = AField) and (Items[I].Position = APosition) and
(not ACheckKind or (Items[I].Kind = AKind)) then
begin
Result := Items[I];
Break;
end;
end;
function TcxDataSummaryItems.GetDataItemFormat(AItemIndex: Integer;
APosition: TcxSummaryPosition): string;
var
AKind: TcxSummaryKind;
ASorted: Boolean;
begin
GetDataItemProperties(AItemIndex, APosition, [spFormat], Result, AKind, ASorted);
end;
function TcxDataSummaryItems.GetDataItemKind(AItemIndex: Integer;
APosition: TcxSummaryPosition): TcxSummaryKind;
var
AFormat: string;
ASorted: Boolean;
begin
GetDataItemProperties(AItemIndex, APosition, [spKind], AFormat, Result, ASorted);
end;
function TcxDataSummaryItems.GetDataItemSorted(AItemIndex: Integer;
APosition: TcxSummaryPosition): Boolean;
var
AFormat: string;
AKind: TcxSummaryKind;
begin
GetDataItemProperties(AItemIndex, APosition, [spSorted], AFormat, AKind, Result);
end;
procedure TcxDataSummaryItems.SetDataItemFormat(AItemIndex: Integer;
APosition: TcxSummaryPosition; const Value: string);
begin
SetDataItemProperties(AItemIndex, APosition, [spFormat], Value, skNone, False);
end;
procedure TcxDataSummaryItems.SetDataItemKind(AItemIndex: Integer;
APosition: TcxSummaryPosition; Value: TcxSummaryKind);
begin
SetDataItemProperties(AItemIndex, APosition, [spKind], '', Value, False);
end;
procedure TcxDataSummaryItems.SetDataItemSorted(AItemIndex: Integer;
APosition: TcxSummaryPosition; Value: Boolean);
begin
SetDataItemProperties(AItemIndex, APosition, [spSorted], '', skNone, Value);
end;
procedure TcxDataSummaryItems.BeforeSortingChange(AItem: TcxDataSummaryItem; AValue: Boolean);
begin
end;
procedure TcxDataSummaryItems.ChangedView;
begin
if Count > 0 then
Update(Items[0]);
// Items[0].Changed(False);
end;
function TcxDataSummaryItems.GetItemPosition(AItem: TcxDataSummaryItem): TcxSummaryPosition;
begin
Result := AItem.FPosition;
end;
function TcxDataSummaryItems.IsPositionStored(AItem: TcxDataSummaryItem): Boolean;
begin
Result := AItem.Position <> TcxSummaryPosition(0);
end;
function TcxDataSummaryItems.GetOwner: TPersistent;
begin
Result := FSummary;
end;
function TcxDataSummaryItems.IndexOfField(AField: TcxCustomDataField): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to Count - 1 do
if Items[I].Field = AField then
begin
Result := I;
Break;
end;
end;
function TcxDataSummaryItems.ItemOfField(AField: TcxCustomDataField): TcxDataSummaryItem;
var
AIndex: Integer;
begin
AIndex := IndexOfField(AField);
if AIndex <> -1 then
Result := Items[AIndex]
else
Result := nil;
end;
procedure TcxDataSummaryItems.Update(Item: TCollectionItem);
begin
FSummary.Changed(Item <> nil);
end;
function TcxDataSummaryItems.AddDataItem(AItemIndex: Integer;
APosition: TcxSummaryPosition): TcxDataSummaryItem;
begin
Result := Add(DataController.GetItem(AItemIndex), APosition, skNone);
end;
procedure TcxDataSummaryItems.CheckItemEmpty(AItem: TcxDataSummaryItem);
begin
if IsPropertiesEmpty([spFormat, spKind, spSorted], AItem.Format, AItem.Kind, AItem.Sorted) then
AItem.Free;
// TODO: Modified
end;
procedure TcxDataSummaryItems.GetDataItemProperties(AItemIndex: Integer;
APosition: TcxSummaryPosition; AProperties: TcxSummaryProperties;
var AFormat: string; var AKind: TcxSummaryKind; var ASorted: Boolean);
var
AItem: TcxDataSummaryItem;
begin
AItem := GetDataItem(AItemIndex, APosition);
if AItem <> nil then
begin
if spFormat in AProperties then AFormat := AItem.Format;
if spKind in AProperties then AKind := AItem.Kind;
if spSorted in AProperties then ASorted := AItem.Sorted;
end
else
begin
AFormat := '';
AKind := skNone;
ASorted := False;
end;
end;
function TcxDataSummaryItems.IsPropertiesEmpty(AProperties: TcxSummaryProperties;
const AFormat: string; AKind: TcxSummaryKind; ASorted: Boolean): Boolean;
begin
Result := True;
if (spFormat in AProperties) and (AFormat <> '') then
Result := False;
if (spKind in AProperties) and (AKind <> skNone) then
Result := False;
if (spSorted in AProperties) and ASorted then
Result := False;
end;
procedure TcxDataSummaryItems.SetDataItemProperties(AItemIndex: Integer;
APosition: TcxSummaryPosition; AProperties: TcxSummaryProperties;
const AFormat: string; AKind: TcxSummaryKind; ASorted: Boolean);
var
AItem: TcxDataSummaryItem;
begin
AItem := GetDataItem(AItemIndex, APosition);
if AItem <> nil then
begin
SetItemProperties(AItem, APosition, AProperties, AFormat, AKind, ASorted);
CheckItemEmpty(AItem);
end
else
if not IsPropertiesEmpty(AProperties, AFormat, AKind, ASorted) then
begin
AItem := AddDataItem(AItemIndex, APosition);
SetItemProperties(AItem, APosition, AProperties, AFormat, AKind, ASorted);
end;
// TODO: Modified
end;
procedure TcxDataSummaryItems.SetItemProperties(AItem: TcxDataSummaryItem;
APosition: TcxSummaryPosition; AProperties: TcxSummaryProperties;
const AFormat: string; AKind: TcxSummaryKind; ASorted: Boolean);
begin
if spFormat in AProperties then AItem.Format := AFormat;
if spKind in AProperties then AItem.Kind := AKind;
if spSorted in AProperties then AItem.Sorted := ASorted;
end;
function TcxDataSummaryItems.GetDataController: TcxCustomDataController;
begin
Result := Summary.DataController;
end;
function TcxDataSummaryItems.GetItem(Index: Integer): TcxDataSummaryItem;
begin
Result := TcxDataSummaryItem(inherited GetItem(Index));
end;
procedure TcxDataSummaryItems.SetItem(Index: Integer; Value: TcxDataSummaryItem);
begin
inherited SetItem(Index, Value);
end;
{ TcxDataFooterSummaryItems }
function TcxDataFooterSummaryItems.GetItemPosition(AItem: TcxDataSummaryItem): TcxSummaryPosition;
begin
Result := spFooter;
end;
function TcxDataFooterSummaryItems.IsPositionStored(AItem: TcxDataSummaryItem): Boolean;
begin
Result := False;
end;
{ TcxDataGroupSummaryItems }
constructor TcxDataGroupSummaryItems.Create(ASummary: TcxDataSummary;
AItemClass: TcxDataSummaryItemClass);
begin
inherited Create(ASummary, AItemClass);
FBeginText := stBeginText;
FEndText := stEndText;
FSeparator := stSeparator;
end;
procedure TcxDataGroupSummaryItems.Assign(Source: TPersistent);
begin
inherited;
if Source is TcxDataGroupSummaryItems then
with TcxDataGroupSummaryItems(Source) do
begin
Self.BeginText := BeginText;
Self.EndText := EndText;
Self.Separator := Separator;
end;
end;
function TcxDataGroupSummaryItems.GetGroupText(const ASummaryValues: Variant): string;
var
I: Integer;
S: string;
begin
Result := '';
for I := 0 to Count - 1 do
if Items[I].Position = spGroup then
begin
S := Items[I].FormatValue(ASummaryValues[I], False);
if Result = '' then
Result := S
else
Result := Result + Separator + ' ' + S;
end;
if Result <> '' then
begin
if FBeginText <> '' then
Result := FBeginText + Result;
if FEndText <> '' then
Result := Result + FEndText;
end;
end;
function TcxDataGroupSummaryItems.SortedSummaryItem: TcxDataSummaryItem;
var
I, ASortingBySummaryDataItemIndex: Integer;
begin
Result := nil;
ASortingBySummaryDataItemIndex := DataController.SortingBySummaryDataItemIndex;
if ASortingBySummaryDataItemIndex <> -1 then
Result := ItemOfItemLink(DataController.GetItem(ASortingBySummaryDataItemIndex))
else
for I := 0 to Count - 1 do
if Items[I].Sorted then
begin
Result := Items[I];
Break;
end;
end;
procedure TcxDataGroupSummaryItems.BeforeSortingChange(AItem: TcxDataSummaryItem;
AValue: Boolean);
begin
inherited;
if AValue and (SortedSummaryItem <> nil) then
SortedSummaryItem.Sorted := False;
end;
function TcxDataGroupSummaryItems.GetOwner: TPersistent;
begin
if FOwner <> nil then
Result := FOwner
else
Result := inherited GetOwner;
end;
function TcxDataGroupSummaryItems.IsBeginTextStored: Boolean;
begin
Result := FBeginText <> stBeginText;
end;
function TcxDataGroupSummaryItems.IsEndTextStored: Boolean;
begin
Result := FEndText <> stEndText;
end;
function TcxDataGroupSummaryItems.IsSeparatorStored: Boolean;
begin
Result := FSeparator <> stSeparator;
end;
procedure TcxDataGroupSummaryItems.SetBeginText(const Value: string);
begin
if FBeginText <> Value then
begin
FBeginText := Value;
ChangedView;
end;
end;
procedure TcxDataGroupSummaryItems.SetEndText(const Value: string);
begin
if FEndText <> Value then
begin
FEndText := Value;
ChangedView;
end;
end;
procedure TcxDataGroupSummaryItems.SetSeparator(const Value: string);
begin
if FSeparator <> Value then
begin
FSeparator := Value;
ChangedView;
end;
end;
{ TcxDataSummaryGroupItemLink }
function TcxDataSummaryGroupItemLink.GetSummaryGroupItemLinks: TcxDataSummaryGroupItemLinks;
begin
if Assigned(Collection) and (Collection is TcxDataSummaryGroupItemLinks) then
Result := TcxDataSummaryGroupItemLinks(Collection)
else
Result := nil;
end;
function TcxDataSummaryGroupItemLink.GetDataController: TcxCustomDataController;
begin
Result := SummaryGroupItemLinks.SummaryGroup.SummaryGroups.Summary.DataController;
end;
procedure TcxDataSummaryGroupItemLink.ItemLinkChanging(AField: TcxCustomDataField);
var
I: Integer;
ASummaryGroups: TcxDataSummaryGroups;
ASummaryGroup: TcxDataSummaryGroup;
AItem: TcxDataSummaryGroupItemLink;
begin
BeginUpdate;
try
ASummaryGroups := SummaryGroupItemLinks.SummaryGroup.SummaryGroups;
for I := 0 to ASummaryGroups.Count - 1 do
begin
ASummaryGroup := ASummaryGroups[I];
AItem := ASummaryGroup.Links.ItemOfField(AField);
if AItem <> nil then
AItem.ItemLink := nil;
end;
finally
EndUpdate;
end;
end;
{ TcxDataSummaryGroupItemLinks }
constructor TcxDataSummaryGroupItemLinks.Create(ASummaryGroup: TcxDataSummaryGroup;
AItemClass: TcxDataSummaryGroupItemLinkClass);
begin
inherited Create(AItemClass);
FSummaryGroup := ASummaryGroup;
end;
function TcxDataSummaryGroupItemLinks.Add: TcxDataSummaryGroupItemLink;
begin
Result := TcxDataSummaryGroupItemLink(inherited Add);
end;
function TcxDataSummaryGroupItemLinks.IndexOfItemLink(AItemLink: TObject): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to Count - 1 do
if Items[I].ItemLink = AItemLink then
begin
Result := I;
Break;
end;
end;
function TcxDataSummaryGroupItemLinks.ItemOfItemLink(AItemLink: TObject): TcxDataSummaryGroupItemLink;
var
AIndex: Integer;
begin
AIndex := IndexOfItemLink(AItemLink);
if AIndex <> -1 then
Result := Items[AIndex]
else
Result := nil;
end;
function TcxDataSummaryGroupItemLinks.GetOwner: TPersistent;
begin
Result := FSummaryGroup;
end;
function TcxDataSummaryGroupItemLinks.IndexOfField(AField: TcxCustomDataField): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to Count - 1 do
if Items[I].Field = AField then
begin
Result := I;
Break;
end;
end;
function TcxDataSummaryGroupItemLinks.ItemOfField(AField: TcxCustomDataField): TcxDataSummaryGroupItemLink;
var
AIndex: Integer;
begin
AIndex := IndexOfField(AField);
if AIndex <> -1 then
Result := Items[AIndex]
else
Result := nil;
end;
procedure TcxDataSummaryGroupItemLinks.Update(Item: TCollectionItem);
begin
// FSummaryGroup.Changed(Item <> nil);
FSummaryGroup.SummaryGroups.Update(nil);
end;
function TcxDataSummaryGroupItemLinks.GetItem(Index: Integer): TcxDataSummaryGroupItemLink;
begin
Result := TcxDataSummaryGroupItemLink(inherited GetItem(Index));
end;
procedure TcxDataSummaryGroupItemLinks.SetItem(Index: Integer; Value: TcxDataSummaryGroupItemLink);
begin
inherited SetItem(Index, Value);
end;
{ TcxDataSummaryGroup }
constructor TcxDataSummaryGroup.Create(Collection: TCollection);
begin
FSummaryItems := TcxDataGroupSummaryItems.Create({SummaryGroups}(Collection as TcxDataSummaryGroups).Summary,
{SummaryGroups}(Collection as TcxDataSummaryGroups).Summary.GetSummaryItemClass);
FSummaryItems.FOwner := Self;
inherited Create(Collection);
end;
destructor TcxDataSummaryGroup.Destroy;
begin
FSummaryItems.Free;
FSummaryItems := nil;
FItemLinks.Free;
FItemLinks := nil;
inherited Destroy;
end;
procedure TcxDataSummaryGroup.Assign(Source: TPersistent);
begin
if Source is TcxDataSummaryGroup then
begin
Links := TcxDataSummaryGroup(Source).Links;
SummaryItems := TcxDataSummaryGroup(Source).SummaryItems;
end
else
inherited Assign(Source);
end;
function TcxDataSummaryGroup.GetItemLinks: TcxDataSummaryGroupItemLinks;
begin
if not Assigned(FItemLinks) then
FItemLinks := TcxDataSummaryGroupItemLinks.Create(Self,
{SummaryGroups}(Collection as TcxDataSummaryGroups).Summary.GetSummaryGroupItemLinkClass);
Result := FItemLinks;
end;
function TcxDataSummaryGroup.GetSummaryGroups: TcxDataSummaryGroups;
begin
if Assigned(Collection) and (Collection is TcxDataSummaryGroups) then
Result := TcxDataSummaryGroups(Collection)
else
Result := nil;
end;
procedure TcxDataSummaryGroup.SetItemLinks(Value: TcxDataSummaryGroupItemLinks);
begin
Links.Assign(Value);
end;
procedure TcxDataSummaryGroup.SetSummaryItems(Value: TcxDataGroupSummaryItems);
begin
SummaryItems.Assign(Value);
end;
{ TcxDataSummaryGroups }
constructor TcxDataSummaryGroups.Create(ASummary: TcxDataSummary);
begin
inherited Create(TcxDataSummaryGroup);
FSummary := ASummary;
end;
function TcxDataSummaryGroups.Add: TcxDataSummaryGroup;
begin
Result := TcxDataSummaryGroup(inherited Add);
end;
function TcxDataSummaryGroups.FindByItemLink(AItemLink: TObject): TcxDataSummaryGroup;
var
I: Integer;
begin
Result := nil;
for I := 0 to Count - 1 do
if Items[I].Links.IndexOfItemLink(AItemLink) <> -1 then
begin
Result := Items[I];
Break;
end;
end;
function TcxDataSummaryGroups.GetOwner: TPersistent;
begin
Result := FSummary;
end;
procedure TcxDataSummaryGroups.Update(Item: TCollectionItem);
begin
Summary.Changed(False);
end;
function TcxDataSummaryGroups.GetItem(Index: Integer): TcxDataSummaryGroup;
begin
Result := TcxDataSummaryGroup(inherited GetItem(Index));
end;
procedure TcxDataSummaryGroups.SetItem(Index: Integer; Value: TcxDataSummaryGroup);
begin
inherited SetItem(Index, Value);
end;
{ TcxDataSummary }
constructor TcxDataSummary.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FDataController := ADataController;
FDefaultGroupSummaryItems := TcxDataGroupSummaryItems.Create(Self, GetSummaryItemClass);
FFooterSummaryItems := TcxDataFooterSummaryItems.Create(Self, GetSummaryItemClass);
FSummaryGroups := TcxDataSummaryGroups.Create(Self);
end;
destructor TcxDataSummary.Destroy;
begin
FDestroying := True;
FDefaultGroupSummaryItems.Free;
FDefaultGroupSummaryItems := nil;
FFooterSummaryItems.Free;
FFooterSummaryItems := nil;
FSummaryGroups.Free;
FSummaryGroups := nil;
inherited Destroy;
end;
procedure TcxDataSummary.Assign(Source: TPersistent);
begin
if Source is TcxDataSummary then
begin
BeginUpdate;
try
DefaultGroupSummaryItems := TcxDataSummary(Source).DefaultGroupSummaryItems;
FooterSummaryItems := TcxDataSummary(Source).FooterSummaryItems;
SummaryGroups := TcxDataSummary(Source).SummaryGroups;
Options := TcxDataSummary(Source).Options;
AssignEvents(Source); // TODO: option?
finally
EndUpdate;
end;
end
else
inherited Assign(Source);
end;
procedure TcxDataSummary.AssignEvents(Source: TPersistent);
begin
if Source is TcxDataSummary then
OnAfterSummary := TcxDataSummary(Source).OnAfterSummary;
end;
procedure TcxDataSummary.BeginUpdate;
begin
DataController.BeginUpdate;
Inc(FLockCount);
end;
procedure TcxDataSummary.BeginCalculate;
begin
if FSetCustomSummary then Exit;
CalculateFooterSummary;
end;
procedure TcxDataSummary.EndCalculate;
begin
if FSetCustomSummary then Exit;
CalculateGroupSummary;
DoAfterSummary;
end;
procedure TcxDataSummary.Calculate;
begin
BeginCalculate;
EndCalculate;
end;
procedure TcxDataSummary.CalculateFooterSummary;
var
ACountValues: TcxDataSummaryCountValues;
ASummaryValues: TcxDataSummaryValues;
begin
BeginCalculateSummary(FooterSummaryItems, ACountValues, ASummaryValues);
if not DataController.IsGridMode then
CalculateSummary(FooterSummaryItems, 0, DataController.DataControllerInfo.GetInternalRecordCount - 1,
ACountValues, ASummaryValues, FFooterSummaryValues);
EndCalculateSummary(FooterSummaryItems, ACountValues, ASummaryValues, FFooterSummaryValues);
end;
procedure TcxDataSummary.CalculateGroupSummary;
procedure CalcLevel(ADataGroups: TcxDataGroups; ALevel: Integer);
var
ASummaryItems: TcxDataSummaryItems;
ACurIndex, AGroupIndex: Integer;
ACountValues: TcxDataSummaryCountValues;
ASummaryValues: TcxDataSummaryValues;
SV: Variant;
begin
ASummaryItems := GroupSummaryItems[ALevel];
ACurIndex := 0;
AGroupIndex := -1;
while ACurIndex < ADataGroups.Count do
begin
if ADataGroups[ACurIndex].Level = ALevel then
begin
if AGroupIndex <> -1 then // close summary
begin
SV := ADataGroups[AGroupIndex].SummaryValues;
EndCalculateSummary(ASummaryItems, ACountValues, ASummaryValues, SV);
ADataGroups[AGroupIndex].SummaryValues := SV;
end;
AGroupIndex := ACurIndex;
BeginCalculateSummary(ASummaryItems, ACountValues, ASummaryValues);
end;
if ADataGroups[ACurIndex].Level = (ADataGroups.LevelCount - 1) then
begin
SV := ADataGroups[ACurIndex].SummaryValues;
CalculateSummary(ASummaryItems, ADataGroups[ACurIndex].BeginRecordListIndex,
ADataGroups[ACurIndex].EndRecordListIndex, ACountValues, ASummaryValues,
SV);
ADataGroups[ACurIndex].SummaryValues := SV;
end;
Inc(ACurIndex);
end;
if AGroupIndex <> -1 then // close summary
begin
SV := ADataGroups[AGroupIndex].SummaryValues;
EndCalculateSummary(ASummaryItems, ACountValues, ASummaryValues, SV);
ADataGroups[AGroupIndex].SummaryValues := SV;
end;
end;
var
I: Integer;
begin
for I := 0 to DataController.DataControllerInfo.GroupingFieldList.Count - 1 do
CalcLevel(DataController.DataControllerInfo.DataGroups, I);
end;
procedure TcxDataSummary.CancelUpdate;
begin
DataController.EndUpdate; //?
Dec(FLockCount);
end;
procedure TcxDataSummary.EndUpdate;
begin
Dec(FLockCount);
Changed(False);
DataController.EndUpdate;
end;
function TcxDataSummary.GetGroupSummaryInfo(ARowIndex: Integer; var ASummaryItems: TcxDataSummaryItems;
var ASummaryValues: PVariant): Boolean;
var
ADataGroupIndex, ALevel: Integer;
begin
ADataGroupIndex := DataController.DataControllerInfo.DataGroups.GetIndexByRowIndex(ARowIndex);
Result := ADataGroupIndex <> -1;
if Result then
begin
ALevel := DataController.DataControllerInfo.DataGroups[ADataGroupIndex].Level;
ASummaryItems := GroupSummaryItems[ALevel];
ASummaryValues := GetGroupSummaryValues(ADataGroupIndex);
Result := (ASummaryItems <> nil) and (ASummaryValues <> nil) and
not VarIsEmpty(ASummaryValues^);
end;
end;
function TcxDataSummary.GetPatternSummaryItems(APatternSummary: TcxDataSummary;
ASummaryItems: TcxDataSummaryItems): TcxDataSummaryItems;
begin
if ASummaryItems = FooterSummaryItems then
Result := APatternSummary.FooterSummaryItems
else
if ASummaryItems = DefaultGroupSummaryItems then
Result := APatternSummary.DefaultGroupSummaryItems
else
Result := APatternSummary.SummaryGroups[(ASummaryItems.Owner as TcxDataSummaryGroup).Index].SummaryItems;
end;
procedure TcxDataSummary.Recalculate;
begin
Changed(False);
// BeginUpdate;
// EndUpdate;
end;
procedure TcxDataSummary.RemoveItemByField(AField: TcxCustomDataField);
function RemoveInSummaryItems(ASummaryItems: TcxDataSummaryItems): Boolean;
var
AItem: TcxDataSummaryItem;
begin
Result := False;
repeat
AItem := ASummaryItems.ItemOfField(AField);
if AItem <> nil then
begin
//AItem.Free;
AItem.FField := nil;
Result := True;
end;
until AItem = nil;
end;
function RemoveInLinks(ALinks: TcxDataSummaryGroupItemLinks): Boolean;
var
AItem: TcxDataSummaryGroupItemLink;
begin
Result := False;
repeat
AItem := ALinks.ItemOfField(AField);
if AItem <> nil then
begin
//AItem.Free;
AItem.FField := nil;
Result := True;
end;
until AItem = nil;
end;
function RemoveInSummaryGroup(ASummaryGroup: TcxDataSummaryGroup): Boolean;
begin
Result := False;
if RemoveInSummaryItems(ASummaryGroup.SummaryItems) then
Result := True;
if RemoveInLinks(ASummaryGroup.Links) then
Result := True;
end;
function RemoveInSummaryGroups: Boolean;
var
I: Integer;
begin
Result := False;
for I := 0 to SummaryGroups.Count - 1 do
if RemoveInSummaryGroup(SummaryGroups[I]) then
Result := True;
end;
var
AChanged: Boolean;
begin
BeginUpdate;
AChanged := False;
try
if RemoveInSummaryItems(FooterSummaryItems) then
AChanged := True;
if RemoveInSummaryItems(DefaultGroupSummaryItems) then
AChanged := True;
if RemoveInSummaryGroups then
AChanged := True;
finally
if AChanged then
EndUpdate
else
CancelUpdate;
end;
end;
procedure TcxDataSummary.BeginCalculateSummary(ASummaryItems: TcxDataSummaryItems;
var ACountValues: TcxDataSummaryCountValues; var ASummaryValues: TcxDataSummaryValues);
var
I: Integer;
begin
SetLength(ASummaryValues, 0); // Clear
SetLength(ACountValues, 0); // Clear
SetLength(ASummaryValues, ASummaryItems.Count);
SetLength(ACountValues, ASummaryItems.Count); // for Average
for I := 0 to ASummaryItems.Count - 1 do
ACountValues[I] := 0;
end;
procedure TcxDataSummary.CalculateSummary(ASummaryItems: TcxDataSummaryItems;
ABeginIndex, AEndIndex: Integer; var ACountValues: TcxDataSummaryCountValues;
var ASummaryValues: TcxDataSummaryValues; var SummaryValues: Variant);
var
I, J, ARecordIndex: Integer;
begin
for I := ABeginIndex to AEndIndex do
begin
ARecordIndex := GetRecordIndex(I);
if ARecordIndex <> -1 then
for J := 0 to ASummaryItems.Count - 1 do
DoSummaryValue(ASummaryItems[J], ARecordIndex, ASummaryValues[J], ACountValues[J]);
end;
end;
procedure TcxDataSummary.Changed(ARedrawOnly: Boolean);
begin
if FInAfterSummary then Exit;
if LockCount = 0 then
Update(ARedrawOnly);
end;
procedure TcxDataSummary.DoAfterSummary;
begin
if Assigned(FOnAfterSummary) then
begin
if DataController.IsLoading then
DataController.FAfterSummaryFlag := True
else
begin
DataController.FAfterSummaryFlag := False;
FInAfterSummary := True;
try
if not DataController.IsPattern and not DataController.LockOnAfterSummary then
FOnAfterSummary(Self);
finally
FInAfterSummary := False;
end;
end;
end;
end;
procedure TcxDataSummary.DoFinishSummaryValue(ASummaryItem: TcxDataSummaryItem;
var SummaryValue: Variant; var CountValue: Integer);
var
AVarIsDate: Boolean;
begin
if not ASummaryItem.IsDataBinded then Exit;
if VarIsEmpty(SummaryValue) then
SummaryValue := Null;
case ASummaryItem.Kind of
skCount:
SummaryValue := CountValue;
skAverage:
if (CountValue > 0) and not VarIsEmpty(SummaryValue) then
begin
AVarIsDate := VarIsDate(SummaryValue);
SummaryValue := SummaryValue / CountValue;
if AVarIsDate then
VarCast(SummaryValue, SummaryValue, varDate); // WORKAROUND
end;
end;
end;
procedure TcxDataSummary.DoSummaryValue(ASummaryItem: TcxDataSummaryItem;
ARecordIndex: Integer; var SummaryValue: Variant; var CountValue: Integer);
var
AArguments: TcxSummaryEventArguments;
AOutArguments: TcxSummaryEventOutArguments;
begin
if not ASummaryItem.IsDataBinded then Exit;
if ASummaryItem.DataField <> nil then
AOutArguments.Value := DataController.GetInternalValue(ARecordIndex, ASummaryItem.DataField)
else
AOutArguments.Value := Null;
AOutArguments.Done := False;
if Assigned(ASummaryItem.SummaryItems.FOnSummary) then
begin
AArguments.RecordIndex := ARecordIndex;
AArguments.SummaryItem := ASummaryItem;
AOutArguments.SummaryValue := SummaryValue;
AOutArguments.CountValue := CountValue;
ASummaryItem.SummaryItems.FOnSummary(ASummaryItem.SummaryItems,
AArguments, AOutArguments);
SummaryValue := AOutArguments.SummaryValue;
CountValue := AOutArguments.CountValue;
end;
if not AOutArguments.Done and (ASummaryItem.Kind <> skNone) and
not ((soNullIgnore in Options) and VarIsNull(AOutArguments.Value)) then
begin
if VarIsEmpty(SummaryValue) then
begin
if ASummaryItem.Kind <> skCount then
begin
if not (VarIsNull(AOutArguments.Value) and (ASummaryItem.Kind in [skMin, skMax])) then
begin
SummaryValue := AOutArguments.Value;
if VarIsNull(SummaryValue) and (ASummaryItem.Kind in [skSum, skAverage]) then
SummaryValue := 0;
end;
end;
end
else
if not VarIsNull(AOutArguments.Value) and not VarIsNull(SummaryValue) then
begin
case ASummaryItem.Kind of
skSum, skAverage:
SummaryValue := SummaryValue + AOutArguments.Value;
skMin:
if AOutArguments.Value < SummaryValue then
SummaryValue := AOutArguments.Value;
skMax:
if AOutArguments.Value > SummaryValue then
SummaryValue := AOutArguments.Value;
end;
end;
Inc(CountValue);
end;
end;
procedure TcxDataSummary.EndCalculateSummary(ASummaryItems: TcxDataSummaryItems;
var ACountValues: TcxDataSummaryCountValues; var ASummaryValues: TcxDataSummaryValues;
var SummaryValues: Variant);
var
J: Integer;
begin
for J := 0 to ASummaryItems.Count - 1 do
DoFinishSummaryValue(ASummaryItems[J], ASummaryValues[J], ACountValues[J]);
// {$IFDEF DELPHI5}
// SummaryValues := Variant(ASummaryValues);
// {$ELSE}
if Length(ASummaryValues) = 0 then
SummaryValues := Null
else
SummaryValues := Variant(ASummaryValues);
// {$ENDIF}
end;
function TcxDataSummary.GetGroupSummaryValues(ADataGroupIndex: TcxDataGroupIndex): PVariant;
begin
if ADataGroupIndex <> -1 then
Result := @DataController.DataControllerInfo.DataGroups[ADataGroupIndex].SummaryValues
else
Result := nil;
end;
function TcxDataSummary.GetOwner: TPersistent;
begin
Result := FDataController;
end;
function TcxDataSummary.GetRecordIndex(ARecordListIndex: Integer): Integer;
begin
Result := DataController.DataControllerInfo.GetInternalRecordIndex(ARecordListIndex);
if DataController.DataControllerInfo.IsSummaryForSelectedRecords then
begin
if DataController.IsFocusedSelectedMode then
begin
if Result <> DataController.DataControllerInfo.FocusedRecordIndex then
Result := -1;
end
else
if not DataController.DataControllerInfo.Selection.IsRecordSelected(Result) then
Result := -1;
end;
end;
function TcxDataSummary.GetSummaryItemClass: TcxDataSummaryItemClass;
begin
Result := DataController.GetSummaryItemClass;
end;
function TcxDataSummary.GetSummaryGroupItemLinkClass: TcxDataSummaryGroupItemLinkClass;
begin
Result := DataController.GetSummaryGroupItemLinkClass;
end;
function TcxDataSummary.IsValidSummaryValuesIndex(const ASummaryValues: Variant;
AIndex: Integer): Boolean;
begin
Result := VarIsArray(ASummaryValues) and
(VarArrayLowBound(ASummaryValues, 1) <= AIndex) and
(AIndex <= VarArrayHighBound(ASummaryValues, 1));
end;
procedure TcxDataSummary.Update(ARedrawOnly: Boolean);
begin
if not FDestroying then
DataController.SummaryChanged(ARedrawOnly);
end;
function TcxDataSummary.GetFooterSummaryValue(Index: Integer): Variant;
begin
Result := Null;
if IsValidSummaryValuesIndex(FFooterSummaryValues, Index) then
Result := FFooterSummaryValues[Index];
end;
function TcxDataSummary.GetFooterSummaryText(Index: Integer): string;
begin
Result := FooterSummaryItems[Index].FormatValue(FooterSummaryValues[Index], True);
end;
function TcxDataSummary.GetGroupSummaryItems(Level: Integer): TcxDataGroupSummaryItems;
var
ASummaryGroup: TcxDataSummaryGroup;
begin
if (0 <= Level) and (Level < DataController.DataControllerInfo.GroupingFieldList.Count) then
begin
ASummaryGroup := SummaryGroups.FindByItemLink(DataController.DataControllerInfo.GroupingFieldList[Level].Field.Item);
// ASummaryGroup := SummaryGroups.FindByItemLink(DataController.DataControllerInfo.DataGroups.Fields[Level].Item);
if ASummaryGroup <> nil then
Result := ASummaryGroup.SummaryItems
else
Result := DefaultGroupSummaryItems;
end
else
Result := nil;
end;
function TcxDataSummary.GetGroupSummaryText(RowIndex: Integer): string;
var
ASummaryItems: TcxDataSummaryItems;
PSummaryValues: PVariant;
begin
if GetGroupSummaryInfo(RowIndex, ASummaryItems, PSummaryValues) then
Result := ASummaryItems.GetGroupText(PSummaryValues^)
else
Result := '';
end;
function TcxDataSummary.GetGroupSummaryDisplayValue(RowIndex, Level, Index: Integer): Variant;
var
ADataGroupIndex: Integer;
begin
ADataGroupIndex := DataController.DataControllerInfo.DataGroups.GetIndexByRowIndexLevel(RowIndex, Level);
Result := GroupSummaryValues[ADataGroupIndex, Index];
end;
function TcxDataSummary.GetGroupSummaryValue(DataGroupIndex: TcxDataGroupIndex;
Index: Integer): Variant;
var
PSummaryValues: PVariant;
begin
PSummaryValues := GetGroupSummaryValues(DataGroupIndex);
if (PSummaryValues <> nil) and IsValidSummaryValuesIndex(PSummaryValues^, Index) then
Result := PSummaryValues^[Index]
else
Result := Null;
end;
function TcxDataSummary.GetGroupFooterIndexOfItemLink(Level: Integer; ItemLink: TObject): Integer;
var
ASummaryItems: TcxDataSummaryItems;
I: Integer;
begin
Result := -1;
ASummaryItems := GroupSummaryItems[Level];
if ASummaryItems <> nil then
for I := 0 to ASummaryItems.Count - 1 do
if (ASummaryItems[I].Position = spFooter) and (ASummaryItems[I].ItemLink = ItemLink) then
begin
Result := I;
Break;
end;
end;
function TcxDataSummary.GetGroupFooterSummaryText(RowIndex, Level, Index: Integer): string;
var
ADataGroupIndex: Integer;
ASummaryItems: TcxDataSummaryItems;
PSummaryValues: PVariant;
begin
Result := '';
ADataGroupIndex := DataController.DataControllerInfo.DataGroups.GetIndexByRowIndexLevel(RowIndex, Level);
if ADataGroupIndex <> -1 then
begin
ASummaryItems := GroupSummaryItems[Level];
PSummaryValues := GetGroupSummaryValues(ADataGroupIndex);
if (ASummaryItems <> nil) and (PSummaryValues <> nil) and
IsValidSummaryValuesIndex(PSummaryValues^, Index) then
Result := ASummaryItems[Index].FormatValue(PSummaryValues^[Index], True);
end;
end;
function TcxDataSummary.GetOptions: TcxSummaryOptions;
begin
Result := FOptions;
end;
procedure TcxDataSummary.SetDefaultGroupSummaryItems(Value: TcxDataGroupSummaryItems);
begin
FDefaultGroupSummaryItems.Assign(Value);
end;
procedure TcxDataSummary.SetFooterSummaryItems(Value: TcxDataFooterSummaryItems);
begin
FFooterSummaryItems.Assign(Value);
end;
procedure TcxDataSummary.SetFooterSummaryValue(Index: Integer; Value: Variant);
begin
if FooterSummaryValues[Index] <> Value then
begin
if IsValidSummaryValuesIndex(FFooterSummaryValues, Index) then
begin
FSetCustomSummary := True;
try
FFooterSummaryValues[Index] := Value;
Changed(True);
finally
FSetCustomSummary := False;
end;
end;
end;
end;
procedure TcxDataSummary.SetGroupSummaryDisplayValue(RowIndex, Level, Index: Integer; const Value: Variant);
var
ADataGroupIndex: Integer;
begin
ADataGroupIndex := DataController.DataControllerInfo.DataGroups.GetIndexByRowIndexLevel(RowIndex, Level);
GroupSummaryValues[ADataGroupIndex, Index] := Value;
end;
procedure TcxDataSummary.SetGroupSummaryValue(DataGroupIndex: TcxDataGroupIndex;
Index: Integer; const Value: Variant);
var
PSummaryValues: PVariant;
begin
if GetGroupSummaryValue(DataGroupIndex, Index) <> Value then
begin
PSummaryValues := GetGroupSummaryValues(DataGroupIndex);
if (PSummaryValues <> nil) and IsValidSummaryValuesIndex(PSummaryValues^, Index) then
begin
FSetCustomSummary := True;
try
PSummaryValues^[Index] := Value;
Changed(True);
finally
FSetCustomSummary := False;
end;
end;
end;
end;
procedure TcxDataSummary.SetOptions(Value: TcxSummaryOptions);
begin
if FOptions <> Value then
begin
// only one: soSelectedRecords or soSelections
if Value * [soSelectedRecords, soMultipleSelectedRecords] = [soSelectedRecords, soMultipleSelectedRecords] then
begin
if soMultipleSelectedRecords in FOptions then
Value := Value - [soMultipleSelectedRecords]
else
Value := Value - [soSelectedRecords];
end;
FOptions := Value;
Changed(False);
end;
end;
procedure TcxDataSummary.SetSummaryGroups(Value: TcxDataSummaryGroups);
begin
SummaryGroups.Assign(Value);
end;
{ TcxDataControllerSearch }
constructor TcxDataControllerSearch.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FDataController := ADataController;
end;
procedure TcxDataControllerSearch.Cancel;
begin
if Locked then Exit;
if DataController.FIncrementalSearching then
begin
DataController.FIncrementalSearchText := '';
DataController.FIncrementalSearching := False;
DataController.Change([dccSearch]);
end;
end;
function TcxDataControllerSearch.Locate(AItemIndex: Integer; const ASubText: string): Boolean;
var
AFilteredRecordIndex, AStartFilteredRecordIndex, AEndFilteredRecordIndex, AFocusedRecordIndex: Integer;
begin
Result := False;
if (ASubText = '') or (DataController.FilteredRecordCount = 0) then Exit;
ItemIndex := AItemIndex;
if DataController.IsGridMode then
Result := DataController.DoSearchInGridMode(ASubText, True, False)
else
begin
AFocusedRecordIndex := DataController.GetFocusedRecordIndex;
if AFocusedRecordIndex < 0 then
AStartFilteredRecordIndex := 0
else
AStartFilteredRecordIndex := DataController.FilteredIndexByRecordIndex[AFocusedRecordIndex];
AFilteredRecordIndex := DoSearch(AStartFilteredRecordIndex, -1, ASubText, True);
if AFilteredRecordIndex = -1 then
begin
AEndFilteredRecordIndex := AStartFilteredRecordIndex - 1;
AStartFilteredRecordIndex := 0;
AFilteredRecordIndex := DoSearch(AStartFilteredRecordIndex, AEndFilteredRecordIndex, ASubText, True);
end;
if AFilteredRecordIndex <> -1 then
begin
DoFocusedRecord(AFilteredRecordIndex);
Result := True;
end;
end;
if Result then
begin
DataController.FIncrementalSearching := True;
DataController.FIncrementalSearchText := ASubText;
DataController.Change([dccSearch]);
DataController.CheckFocusedSelected;
end;
end;
function TcxDataControllerSearch.LocateNext(AForward: Boolean): Boolean;
var
AFilteredRecordIndex, AFocusedRecordIndex: Integer;
begin
Result := False;
if not Searching or (SearchText = '') or (ItemIndex = -1) or
(DataController.FilteredRecordCount = 0) then Exit;
if DataController.IsGridMode then
Result := DataController.DoSearchInGridMode(SearchText, AForward, True)
else
begin
AFocusedRecordIndex := DataController.GetFocusedRecordIndex;
if AFocusedRecordIndex < 0 then Exit;
if AForward then
begin
AFilteredRecordIndex := DataController.FilteredIndexByRecordIndex[AFocusedRecordIndex] + 1;
if AFilteredRecordIndex >= DataController.FilteredRecordCount then Exit;
end
else
begin
AFilteredRecordIndex := DataController.FilteredIndexByRecordIndex[AFocusedRecordIndex] - 1;
if AFilteredRecordIndex < 0 then Exit;
end;
AFilteredRecordIndex := DoSearch(AFilteredRecordIndex, -1, SearchText, AForward);
if AFilteredRecordIndex <> -1 then
begin
DoFocusedRecord(AFilteredRecordIndex);
Result := True;
end;
end;
if Result then
DataController.CheckFocusedSelected;
end;
procedure TcxDataControllerSearch.Lock;
begin
FLocked := True;
end;
procedure TcxDataControllerSearch.Unlock;
begin
FLocked := False;
end;
procedure TcxDataControllerSearch.DoFocusedRecord(AFilteredRecordIndex: Integer);
var
AFocusedRecordIndex: Integer;
begin
AFocusedRecordIndex := DataController.FilteredRecordIndex[AFilteredRecordIndex];
Lock;
try
DataController.ChangeFocusedRecordIndex(AFocusedRecordIndex);
DataController.SyncSelectionFocusedRecord;
finally
Unlock;
end;
end;
function TcxDataControllerSearch.DoSearch(AStartFilteredRecordIndex, AEndFilteredRecordIndex: Integer;
const ASubText: string; AForward: Boolean): Integer;
var
I, ARecordIndex, AFieldIndex: Integer;
S: string;
begin
Result := -1;
if AEndFilteredRecordIndex = -1 then // auto
begin
if AForward then
AEndFilteredRecordIndex := DataController.FilteredRecordCount - 1
else
AEndFilteredRecordIndex := 0;
end;
AFieldIndex := DataController.FIncrementalSearchField.Index;
I := AStartFilteredRecordIndex;
while (AForward and (I <= AEndFilteredRecordIndex)) or
(not AForward and (I >= AEndFilteredRecordIndex)) do
begin
ARecordIndex := DataController.FilteredRecordIndex[I];
S := DataController.GetIncrementalSearchText(ARecordIndex, AFieldIndex);
if DataCompareText(S, ASubText, True) then
begin
Result := I;
Break;
end;
if AForward then
Inc(I)
else
Dec(I);
end;
end;
function TcxDataControllerSearch.GetItemIndex: Integer;
begin
if DataController.FIncrementalSearchField <> nil then
Result := DataController.FIncrementalSearchField.Index
else
Result := -1;
end;
function TcxDataControllerSearch.GetSearching: Boolean;
begin
Result := DataController.FIncrementalSearching;
end;
function TcxDataControllerSearch.GetSearchText: string;
begin
Result := DataController.FIncrementalSearchText;
end;
procedure TcxDataControllerSearch.SetItemIndex(const Value: Integer);
begin
DataController.CheckItemRange(Value);
DataController.FIncrementalSearchField := DataController.Fields[Value];
end;
{ TcxFocusedRecordChangedInfo }
constructor TcxFocusedRecordChangedInfo.Create(APrevFocusedRecordIndex, AFocusedRecordIndex,
APrevFocusedRowIndex, AFocusedRowIndex: Integer; ANewItemRowFocusingChanged: Boolean);
begin
inherited Create;
FFocusedRecordIndex := APrevFocusedRecordIndex;
FPrevFocusedRecordIndex := AFocusedRecordIndex;
FPrevFocusedRowIndex := APrevFocusedRowIndex;
FFocusedRowIndex := AFocusedRowIndex;
FNewItemRowFocusingChanged := ANewItemRowFocusingChanged;
end;
{ TcxFocusedRowChangedInfo }
constructor TcxFocusedRowChangedInfo.Create(APrevFocusedRowIndex, AFocusedRowIndex: Integer);
begin
inherited Create;
FFocusedRowIndex := AFocusedRowIndex;
FPrevFocusedRowIndex := APrevFocusedRowIndex;
end;
{ TcxUpdateRecordInfo }
constructor TcxUpdateRecordInfo.Create(ARecordIndex: Integer);
begin
inherited Create;
FRecordIndex := ARecordIndex;
end;
{ TcxSelectionChangedInfo }
constructor TcxSelectionChangedInfo.Create;
begin
inherited Create;
FRowIndexes := TList.Create;
end;
constructor TcxSelectionChangedInfo.CreateEx(ARowIndex1, ARowIndex2: Integer);
begin
Create;
if ARowIndex1 <> -1 then
FRowIndexes.Add(Pointer(ARowIndex1));
if (ARowIndex2 <> -1) and (ARowIndex2 <> ARowIndex1) then
FRowIndexes.Add(Pointer(ARowIndex2));
end;
destructor TcxSelectionChangedInfo.Destroy;
begin
FRowIndexes.Free;
inherited Destroy;
end;
function TcxSelectionChangedInfo.GetCount: Integer;
begin
Result := FRowIndexes.Count;
end;
function TcxSelectionChangedInfo.GetRowIndex(Index: Integer): Integer;
begin
Result := Integer(FRowIndexes[Index]);
end;
{ TcxDataControllerLocateObject }
constructor TcxDataControllerLocateObject.Create(ADataController: TcxCustomDataController);
begin
inherited Create;
FDataController := ADataController;
end;
destructor TcxDataControllerLocateObject.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TcxDataControllerLocateObject.Clear;
begin
if FInternalRecordIndex < 0 then
begin
FDataController.DataStorage.DeleteRecord(FInternalRecordIndex);
FInternalRecordIndex := 0;
end;
end;
procedure TcxDataControllerLocateObject.ReadData(AValueDefReader: TcxValueDefReader);
begin
if FInternalRecordIndex = 0 then
FInternalRecordIndex := FDataController.DataStorage.AddInternalRecord;
FDataController.BeginReadRecord;
try
FDataController.DataStorage.ReadRecord(FInternalRecordIndex, AValueDefReader);
finally
FDataController.EndReadRecord;
end;
end;
{ TcxDataControllerKeyLocateObject }
constructor TcxDataControllerKeyLocateObject.Create(ADataController: TcxCustomDataController);
begin
inherited Create(ADataController);
FFields := TList.Create;
end;
destructor TcxDataControllerKeyLocateObject.Destroy;
begin
Clear;
FFields.Free;
inherited Destroy;
end;
procedure TcxDataControllerKeyLocateObject.AddField(AField: TcxCustomDataField);
begin
FFields.Add(AField);
end;
procedure TcxDataControllerKeyLocateObject.ClearFields;
begin
FFields.Clear;
end;
function TcxDataControllerKeyLocateObject.FindRecordIndex: Integer;
begin
if InternalRecordIndex < 0 then
Result := FDataController.FindRecordByFields(InternalRecordIndex, FFields)
else
Result := -1;
end;
{ TcxDataControllerGroupLocateObject }
constructor TcxDataControllerGroupLocateObject.Create(ADataController: TcxCustomDataController);
var
I: Integer;
AField: TcxCustomDataField;
begin
inherited Create(ADataController);
FIsGroupDataSorted := True;
for I := 0 to DataController.Groups.GroupingItemCount - 1 do
begin
AField := DataController.Fields[DataController.Groups.GroupingItemIndex[I]];
if DataController.IsSortByDisplayTextNeeded(AField) then
begin
FIsGroupDataSorted := False;
Break;
end;
end;
end;
function TcxDataControllerGroupLocateObject.FindRecordIndex: Integer;
begin
if InternalRecordIndex < 0 then
begin
FDataController.FInternalFindRecord := True;
try
Result := FDataController.FindGroupRecord(InternalRecordIndex, Level + 1, FIsGroupDataSorted);
finally
FDataController.FInternalFindRecord := False;
end;
end
else
Result := -1;
end;
{ TcxValueDefUnboundReader }
constructor TcxValueDefUnboundReader.Create;
begin
inherited Create;
FItems := TList.Create;
FValues := TList.Create;
end;
destructor TcxValueDefUnboundReader.Destroy;
begin
Clear;
FValues.Free;
FItems.Free;
inherited Destroy;
end;
procedure TcxValueDefUnboundReader.Clear;
begin
Truncate(0);
end;
function TcxValueDefUnboundReader.GetValue(AValueDef: TcxValueDef): Variant;
var
I: Integer;
P: PVariant;
begin
if AValueDef is TcxInternalValueDef then
AValueDef := TcxInternalValueDef(AValueDef).GetValueDef;
I := FItems.IndexOf(AValueDef);
if I <> -1 then
begin
P := PVariant(FValues[I]);
if P <> nil then
Result := P^
else
Result := Null;
end
else
Result := Null;
end;
function TcxValueDefUnboundReader.IsInternal(AValueDef: TcxValueDef): Boolean;
begin
Result := IsValueDefInternal(AValueDef);
end;
procedure TcxValueDefUnboundReader.SetValue(AValueDef: TcxValueDef; const AValue: Variant);
var
I: Integer;
P: PVariant;
begin
I := FItems.IndexOf(AValueDef);
if I = -1 then
I := FItems.Add(AValueDef);
while FValues.Count < FItems.Count do
FValues.Add(nil);
P := PVariant(FValues[I]);
if P = nil then
begin
New(P);
FValues[I] := P;
end;
P^ := AValue;
end;
procedure TcxValueDefUnboundReader.Truncate(AItemCount: Integer);
var
I: Integer;
begin
for I := FValues.Count - 1 downto AItemCount do
begin
if PVariant(FValues[I]) <> nil then
Dispose(PVariant(FValues[I]));
FValues.Delete(I);
end;
end;
{ TcxValueDefRecordReader }
constructor TcxValueDefRecordReader.Create(ADataController: TcxCustomDataController;
ARecordIndex: Integer);
begin
inherited Create;
FDataController := ADataController;
FRecordIndex := ARecordIndex;
end;
function TcxValueDefRecordReader.GetDisplayText(AValueDef: TcxValueDef): string;
var
AField: TcxCustomDataField;
begin
AField := AValueDef.LinkObject as TcxCustomDataField;
if not AField.IsInternal then
Result := FDataController.GetInternalDisplayText(FRecordIndex, AField)
else
Result := '';
end;
function TcxValueDefRecordReader.GetValue(AValueDef: TcxValueDef): Variant;
var
AField: TcxCustomDataField;
begin
AField := AValueDef.LinkObject as TcxCustomDataField;
// if not AField.IsInternal then
// Result := FDataController.GetInternalValue(FRecordIndex, AField)
// else
// Result := Null;
Result := FDataController.GetInternalValue(FRecordIndex, AField);
end;
function TcxValueDefRecordReader.IsInternal(AValueDef: TcxValueDef): Boolean;
begin
Result := IsValueDefInternal(AValueDef);
end;
{ TcxDataControllerNotifier }
constructor TcxDataControllerNotifier.Create(AOwner: TComponent; ADataController: TcxCustomDataController);
begin
inherited Create(AOwner);
FDataController := ADataController;
end;
procedure TcxDataControllerNotifier.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and
not ((AComponent = Self) and (csDestroying in ComponentState)) then
DataController.RemoveNotification(AComponent);
end;
{ TcxSortingBySummaryEngine }
constructor TcxSortingBySummaryEngine.Create(ADataControllerInfo: TcxCustomDataControllerInfo);
begin
inherited Create;
FDataControllerInfo := ADataControllerInfo;
end;
function TcxSortingBySummaryEngine.GetDataController: TcxCustomDataController;
begin
Result := FDataControllerInfo.DataController;
end;
{ TcxCustomDataController }
constructor TcxCustomDataController.Create(AOwner: TComponent);
begin
inherited Create;
FOwner := AOwner;
FFocusedSelected := True;
FOptions := [dcoAssignGroupingValues, dcoAssignMasterDetailKeys, dcoSaveExpanding];
FDataChangedListeners := TList.Create;
FDataStorage := TcxDataStorage.Create;
FGroups := TcxDataControllerGroups.Create(Self);
FFields := TcxCustomDataFieldList.Create(Self);
FFilters := TcxDataFilterList.Create;
FFilter := GetFilterCriteriaClass.Create(Self);
FSearch := GetSearchClass.Create(Self);
FSummary := GetSummaryClass.Create(Self);
FProvider := GetDataProviderClass.Create(Self);
FDataControllerInfo := TcxCustomDataControllerInfo.Create(Self);
FMasterRecordIndex := -1;
FBookmarkRecordIndex := -1;
FNearestRecordIndex := -1;
ResetDataChangeInfo;
FNotifier := TcxDataControllerNotifier.Create(nil, Self);
FDataStorage.OnClearInternalRecords := DataStorageClearInternalRecords;
UpdateUseRecordIDState;
FListenerLinks := TList.Create;
end;
destructor TcxCustomDataController.Destroy;
begin
Destroying; // for CLR
FNotifier.Free;
FNotifier := nil;
FRelations.Free;
FRelations := nil;
FDataControllerInfo.Free;
FSummary.Free;
FSummary := nil;
FFilter.Free;
FFilter := nil;
FFilters.Free;
FFilters := nil;
FDataStorage.Clear(False);
FGroups.Free;
FFields.Free;
FDataStorage.Free;
FProvider.Free;
FSearch.Free;
ClearDataChangedListeners;
FDataChangedListeners.Free;
NotifyListenerLinks;
FListenerLinks.Free;
FListenerLinks := nil;
inherited Destroy;
end;
procedure TcxCustomDataController.Assign(Source: TPersistent);
begin
if Source is TcxCustomDataController then
begin
Options := TcxCustomDataController(Source).Options;
//Filter := TcxCustomDataController(Source).Filter;
Filter.Assign(TcxCustomDataController(Source).Filter, True);
Summary := TcxCustomDataController(Source).Summary;
OnAfterCancel := TcxCustomDataController(Source).OnAfterCancel;
OnAfterDelete := TcxCustomDataController(Source).OnAfterDelete;
OnAfterInsert := TcxCustomDataController(Source).OnAfterInsert;
OnAfterPost := TcxCustomDataController(Source).OnAfterPost;
OnBeforeCancel := TcxCustomDataController(Source).OnBeforeCancel;
OnBeforeDelete := TcxCustomDataController(Source).OnBeforeDelete;
OnBeforeInsert := TcxCustomDataController(Source).OnBeforeInsert;
OnBeforePost := TcxCustomDataController(Source).OnBeforePost;
OnFilterRecord := TcxCustomDataController(Source).OnFilterRecord;
OnNewRecord := TcxCustomDataController(Source).OnNewRecord;
OnCompare := TcxCustomDataController(Source).OnCompare;
OnDataChanged := TcxCustomDataController(Source).OnDataChanged;
OnDetailCollapsing := TcxCustomDataController(Source).OnDetailCollapsing;
OnDetailCollapsed := TcxCustomDataController(Source).OnDetailCollapsed;
OnDetailExpanding := TcxCustomDataController(Source).OnDetailExpanding;
OnDetailExpanded := TcxCustomDataController(Source).OnDetailExpanded;
OnGroupingChanged := TcxCustomDataController(Source).OnGroupingChanged;
OnRecordChanged := TcxCustomDataController(Source).OnRecordChanged;
OnSortingChanged := TcxCustomDataController(Source).OnSortingChanged;
end
else
inherited Assign(Source);
end;
procedure TcxCustomDataController.BeforeDestruction;
begin
inherited BeforeDestruction;
FDestroying := True;
end;
procedure TcxCustomDataController.Destroying;
begin
end;
function TcxCustomDataController.GetOwner: TPersistent;
begin
Result := FOwner;
end;
procedure TcxCustomDataController.BeginUpdate;
begin
DataControllerInfo.BeginUpdate;
end;
procedure TcxCustomDataController.EndUpdate;
begin
DataControllerInfo.EndUpdate;
CheckChanges;
end;
procedure TcxCustomDataController.BeginFullUpdate;
begin
BeginUpdate;
end;
procedure TcxCustomDataController.EndFullUpdate;
begin
EndUpdate;
end;
procedure TcxCustomDataController.BeginUpdateFields;
begin
Inc(FLockUpdateFieldsCount);
end;
procedure TcxCustomDataController.EndUpdateFields;
begin
Dec(FLockUpdateFieldsCount);
if (FLockUpdateFieldsCount = 0) and FUpdateFieldsFlag then
begin
FUpdateFieldsFlag := False;
LayoutChanged([lcStructure]);
end;
end;
procedure TcxCustomDataController.SaveKeys;
begin
if FSaveObjectLockCount = 0 then
begin
BeginFullUpdate;
FSaveObject := TcxSaveObject.Create;
TcxSaveObject(FSaveObject).Save(Self);
end;
Inc(FSaveObjectLockCount);
end;
procedure TcxCustomDataController.RestoreKeys;
begin
Dec(FSaveObjectLockCount);
if FSaveObjectLockCount = 0 then
begin
try
TcxSaveObject(FSaveObject).Restore(Self);
FSaveObject.Free;
FSaveObject := nil;
finally
EndFullUpdate;
end;
end;
end;
function TcxCustomDataController.CreateFilter: TcxDataFilterCriteria;
begin
Result := GetFilterCriteriaClass.Create(Self);
FFilters.Add(Result);
end;
function TcxCustomDataController.AddItem(AItem: TObject): TcxCustomDataField;
begin
if (AItem <> nil) and (Fields.FieldByItem(AItem) <> nil) then
InvalidOperation(cxSDataItemExistError);
Result := AddField;
Result.Item := AItem;
end;
function TcxCustomDataController.ItemPropertiesChanged(AItemIndex: Integer): Boolean;
begin
Result := (AItemIndex = -1) or (GetItemSortOrder(AItemIndex) <> soNone);
if Result then
Refresh;
end;
procedure TcxCustomDataController.Loaded;
begin
if FDataChangedFlag then
DoDataChanged;
if FAfterSummaryFlag then
Summary.DoAfterSummary;
end;
procedure TcxCustomDataController.RemoveItem(AItem: TObject);
var
AField: TcxCustomDataField;
begin
AField := Fields.FieldByItem(AItem);
if AField <> nil then
begin
BeginUpdate;
try
DataControllerInfo.RemoveField(AField);
AField.Free;
finally
EndUpdate;
end;
end;
end;
procedure TcxCustomDataController.UpdateItemIndexes;
var
I: Integer;
AField: TcxCustomDataField;
begin
for I := 0 to ItemCount - 1 do
begin
AField := Fields.FieldByItem(GetItem(I));
AField.Index := I;
end;
Change([dccLayout]);
end;
procedure TcxCustomDataController.UpdateItems(AUpdateFields: Boolean);
begin
if FUpdateItems then Exit;
FUpdateItems := True;
try
if LockCount <> 0 then
FStructureChanged := True;
if IsProviderDataSource then
UpdateStorage(AUpdateFields);
finally
FUpdateItems := False;
end;
end;
function TcxCustomDataController.ExecuteAction(Action: TBasicAction): Boolean;
begin
Result := False;
end;
function TcxCustomDataController.UpdateAction(Action: TBasicAction): Boolean;
begin
Result := False;
end;
// Data Changed notify
procedure TcxCustomDataController.AddDataChangedListener(AInstance: TObject;
ADataChangedEvent: TNotifyEvent);
var
E: TNotifyEventItem;
begin
E := TNotifyEventItem.Create;
E.Instance := AInstance;
E.Event := ADataChangedEvent;
FDataChangedListeners.Add(E);
end;
procedure TcxCustomDataController.RemoveDataChangedListener(AInstance: TObject;
ADataChangedEvent: TNotifyEvent);
var
I: Integer;
E: TNotifyEventItem;
begin
for I := 0 to FDataChangedListeners.Count - 1 do
begin
E := TNotifyEventItem(FDataChangedListeners[I]);
if (E.Instance = AInstance) and (@E.Event = @ADataChangedEvent) then
begin
E.Free;
FDataChangedListeners.Delete(I);
Break;
end;
end;
end;
procedure TcxCustomDataController.AddDataChangeRefCount;
begin
Inc(FDataChangeRefCount);
IsPattern := FIsPatternSave;
end;
procedure TcxCustomDataController.RemoveDataChangeRefCount;
begin
Dec(FDataChangeRefCount);
IsPattern := FIsPatternSave;
end;
function TcxCustomDataController.DataChangedNotifyLocked: Boolean;
begin
Result := FDataChangedLockCount <> 0;
end;
function TcxCustomDataController.IsDataLoading: Boolean;
begin
Result := (Provider <> nil) and (Provider.LockCount <> 0);
end;
procedure TcxCustomDataController.LockDataChangedNotify;
begin
Inc(FDataChangedLockCount);
end;
procedure TcxCustomDataController.UnlockDataChangedNotify;
begin
Dec(FDataChangedLockCount);
end;
procedure TcxCustomDataController.LockGridModeNotify;
begin
Inc(FLockGridModeNotifyCount);
Provider.BeginLocate;
end;
procedure TcxCustomDataController.UnlockGridModeNotify;
begin
Provider.EndLocate;
Dec(FLockGridModeNotifyCount);
end;
// Structure
procedure TcxCustomDataController.ChangeNeedConversion(AItemIndex: Integer;
ANeedConversion: Boolean);
begin
CheckItemRange(AItemIndex);
if GetItemNeedConversion(AItemIndex) <> ANeedConversion then
begin
Fields[AItemIndex].NeedConversion := ANeedConversion;
DataControllerInfo.UpdateField(Fields[AItemIndex]);
end;
end;
procedure TcxCustomDataController.ChangeTextStored(AItemIndex: Integer;
ATextStored: Boolean);
begin
CheckItemRange(AItemIndex);
if GetItemTextStored(AItemIndex) <> ATextStored then
begin
Fields[AItemIndex].TextStored := ATextStored;
DataControllerInfo.UpdateField(Fields[AItemIndex]);
end;
end;
procedure TcxCustomDataController.ChangeValueTypeClass(AItemIndex: Integer;
AValueTypeClass: TcxValueTypeClass);
begin
CheckItemRange(AItemIndex);
if GetItemValueTypeClass(AItemIndex) <> AValueTypeClass then
begin
Fields[AItemIndex].ValueTypeClass := AValueTypeClass;
if IsProviderMode then
RestructData;
DataControllerInfo.UpdateField(Fields[AItemIndex]);
DoValueTypeClassChanged(AItemIndex);
end;
end;
function TcxCustomDataController.GetItemCount: Integer;
begin
Result := Fields.ItemCount;
end;
function TcxCustomDataController.GetItemNeedConversion(AItemIndex: Integer): Boolean;
begin
Result := False;
end;
function TcxCustomDataController.GetItemTextStored(AItemIndex: Integer): Boolean;
begin
CheckItemRange(AItemIndex);
Result := Fields[AItemIndex].TextStored;
end;
function TcxCustomDataController.GetItemValueTypeClass(AItemIndex: Integer): TcxValueTypeClass;
begin
CheckItemRange(AItemIndex);
Result := Fields[AItemIndex].ValueTypeClass;
end;
function TcxCustomDataController.IsDisplayFormatDefined(AItemIndex: Integer;
AIgnoreSimpleCurrency: Boolean): Boolean;
begin
Result := GetItemTextStored(AItemIndex);
end;
// Data
function TcxCustomDataController.AppendRecord: Integer;
function DataStorageAppendRecord: Integer;
begin
Result := DataStorage.AppendRecord;
DataChanged(dcNew, -1, -1);
end;
begin
if IsSmartLoad then
Result := AppendInSmartLoad
else
if IsProviderMode then
Result := Provider.AppendRecord
else
Result := DataStorageAppendRecord;
end;
procedure TcxCustomDataController.DeleteRecord(ARecordIndex: Integer);
procedure DataStorageDeleteRecord(ARecordIndex: Integer);
begin
DataStorage.DeleteRecord(ARecordIndex);
CorrectAfterDelete(ARecordIndex);
CheckSelectedCount(ARecordIndex);
CheckInternalRecordRange(ARecordIndex);
DataChanged(dcDeleted, -1, -1);
end;
begin
CheckRecordRange(ARecordIndex);
if IsSmartLoad and FInSmartLoad then
DeleteInSmartLoad(ARecordIndex)
else
begin
if Provider.EditingRecordIndex = ARecordIndex then
Provider.FEditingRecordIndex := cxNullEditingRecordIndex;
if IsProviderMode and (ARecordIndex >= 0) then
Provider.DeleteRecord(ARecordIndex)
else
DataStorageDeleteRecord(ARecordIndex);
end;
end;
function TcxCustomDataController.GetGroupValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant;
begin
Result := GetInternalValue(ARecordIndex, AField);
end;
procedure TcxCustomDataController.GetGroupValues(ARecordIndex: Integer; var AValues: TcxDataSummaryValues);
var
I: Integer;
begin
// Clear
SetLength(AValues, 0);
SetLength(AValues, Groups.GroupingItemCount);
for I := 0 to Groups.GroupingItemCount - 1 do
AValues[I] := Null;
// Load Values
if (0 <= ARecordIndex) and (ARecordIndex < RecordCount) then
for I := 0 to Groups.GroupingItemCount - 1 do
AValues[I] := GetGroupValue(ARecordIndex, Fields[Groups.GroupingItemIndex[I]]);
end;
function TcxCustomDataController.GetDisplayText(ARecordIndex, AItemIndex: Integer): string;
begin
CheckRange(ARecordIndex, AItemIndex);
Result := GetInternalDisplayText(ARecordIndex, Fields[AItemIndex]);
end;
function TcxCustomDataController.GetRecordCount: Integer;
begin
{ if IsProviderMode then
Result := Provider.GetRecordCount
else}
Result := DataStorage.RecordCount;
end;
function TcxCustomDataController.GetRecordId(ARecordIndex: Integer): Variant;
begin
if not ((0 <= ARecordIndex) and (ARecordIndex < RecordCount)) then
InvalidOperation(cxSDataRecordIndexError);
if IsRecordID then
Result := DataStorage.GetRecordID(ARecordIndex)
else
begin
if IsProviderMode and Provider.IsRecordIdSupported then
Result := Provider.GetRecordId(ARecordIndex)
else
Result := Null;
end;
end;
function TcxCustomDataController.GetValue(ARecordIndex, AItemIndex: Integer): Variant;
begin
CheckRange(ARecordIndex, AItemIndex);
Result := GetInternalValue(ARecordIndex, Fields[AItemIndex]);
end;
function TcxCustomDataController.InsertRecord(ARecordIndex: Integer): Integer;
function DataStorageInsertRecord(ARecordIndex: Integer): Integer;
begin
Result := ARecordIndex;
DataStorage.InsertRecord(ARecordIndex);
CorrectAfterInsert(ARecordIndex);
DataChanged(dcNew, -1, -1);
end;
begin
if (ARecordIndex < 0) or (ARecordIndex > RecordCount) then
InvalidOperation(cxSDataRecordIndexError);
if IsSmartLoad then
Result := AppendInSmartLoad
else
if IsProviderMode then
Result := Provider.InsertRecord(ARecordIndex)
else
Result := DataStorageInsertRecord(ARecordIndex);
end;
procedure TcxCustomDataController.Refresh;
begin
BeginUpdate;
try
DataControllerInfo.Refresh;
finally
EndUpdate;
end;
end;
procedure TcxCustomDataController.SetDisplayText(ARecordIndex, AItemIndex: Integer;
const Value: string);
begin
if IsGridMode then Exit;
CheckRange(ARecordIndex, AItemIndex);
if IsProviderMode and (ARecordIndex >= 0) then
begin
Provider.SetDisplayText(ARecordIndex, Fields[AItemIndex], Value);
DataChanged(dcField, AItemIndex, ARecordIndex);
end
else
begin
if Fields[AItemIndex].ValueDef <> nil then
begin
DataStorage.SetDisplayText(ARecordIndex, Fields[AItemIndex].ValueDef, Value);
DataChanged(dcField, AItemIndex, ARecordIndex);
end;
end;
end;
procedure TcxCustomDataController.SetRecordCount(Value: Integer);
procedure SetDataStorageRecordCount;
const
DataChangeA: array[Boolean] of TcxDataChange = (dcDeleted , dcNew);
var
PrevRecordCount, RealRecordCount: Integer;
begin
PrevRecordCount := DataStorage.RecordCount;
DataStorage.RecordCount := Value;
RealRecordCount := DataStorage.RecordCount;
if RealRecordCount <> PrevRecordCount then
begin
CheckSelectedCount(-1);
CheckEditingRecordIndex(-1);
DataChanged(DataChangeA[RealRecordCount > PrevRecordCount], -1, -1);
end;
end;
begin
if IsGridMode then Exit;
if Value < 0 then Value := 0;
if RecordCount <> Value then
begin
if IsProviderMode then
Provider.SetRecordCount(Value)
else
SetDataStorageRecordCount;
end;
end;
procedure TcxCustomDataController.SetValue(ARecordIndex, AItemIndex: Integer; const Value: Variant);
begin
if IsGridMode then Exit;
CheckRange(ARecordIndex, AItemIndex);
if IsProviderMode and (ARecordIndex >= 0) then
begin
Provider.SetValue(ARecordIndex, Fields[AItemIndex], Value);
DataChanged(dcField, AItemIndex, ARecordIndex);
end
else
begin
if Fields[AItemIndex].ValueDef <> nil then
begin
SetStoredValue(ARecordIndex, Fields[AItemIndex], Value);
// DataStorage.SetValue(ARecordIndex, Fields[AItemIndex].ValueDef, Value);
DataChanged(dcField, AItemIndex, ARecordIndex);
end;
end;
end;
procedure TcxCustomDataController.SortByDisplayTextChanged;
begin
// if dcoSortByDisplayText in Options then
Refresh;
end;
// Data Editing
procedure TcxCustomDataController.Append;
begin
if Provider.CanAppend then
Provider.Append;
end;
procedure TcxCustomDataController.Cancel;
var
APrevRowIndex: Integer;
begin
if FInCancel then Exit;
FInCancel := True;
try
APrevRowIndex := FocusedRowIndex;
if Provider.IsEditing then
Provider.Cancel;
if APrevRowIndex > (GetRowCount - 1) then
APrevRowIndex := (GetRowCount - 1);
if not DataControllerInfo.FInCanFocusedRowChanging then
FocusedRowIndex := APrevRowIndex;
finally
FInCancel := False;
end;
end;
function TcxCustomDataController.CanInitEditing(AItemIndex: Integer): Boolean;
begin
Result := False;
CheckItemRange(AItemIndex);
if (Provider.CanModify or Fields[AItemIndex].IsUnbound) and
Fields[AItemIndex].CanModify(GetItemValueSource(AItemIndex)) then
Result := Provider.CanInitEditing(EditingRecordIndex);
end;
procedure TcxCustomDataController.CheckBrowseMode;
begin
end;
procedure TcxCustomDataController.DeleteFocused;
var
AList: TList;
ARowIndex: Integer;
begin
ARowIndex := GetFocusedRowIndex;
if (ARowIndex <> -1) and Provider.CanDelete then
begin
Provider.BeginDeleting;
try
if GetRowInfo(ARowIndex).Level < Groups.LevelCount then // It's Group Row
begin
AList := TList.Create;
try
Groups.LoadRecordIndexesByRowIndex(AList, ARowIndex);
DeleteRecords(AList);
finally
AList.Free;
end;
end
else
DeleteFocusedRecord;
ClearSelection; // !!!
finally
Provider.EndDeleting;
end;
end;
end;
procedure TcxCustomDataController.DeleteSelection;
begin
if (GetSelectedCount > 0) and Provider.CanDelete then
begin
Provider.BeginDeleting;
try
if IsFocusedSelectedMode then
DeleteFocusedRecord
else
Provider.DeleteSelection;
finally
Provider.EndDeleting;
end;
end;
end;
procedure TcxCustomDataController.Edit;
begin
if Provider.CanModify then
Provider.Edit;
end;
procedure TcxCustomDataController.FocusControl(AItemIndex: Integer; var Done: Boolean);
begin
Done := False;
end;
function TcxCustomDataController.GetEditValue(AItemIndex: Integer;
AEditValueSource: TcxDataEditValueSource): Variant;
begin
CheckItemRange(AItemIndex);
if UseNewItemRowForEditing and NewItemRowFocused and not Provider.IsEditing then
Result := Null
else
Result := Provider.GetEditValue(EditingRecordIndex, Fields[AItemIndex], AEditValueSource);
end;
function TcxCustomDataController.GetItemValueSource(AItemIndex: Integer): TcxDataEditValueSource;
begin
Result := evsText;
end;
procedure TcxCustomDataController.Insert;
begin
if Provider.CanInsert then
Provider.Insert;
end;
procedure TcxCustomDataController.Post(AForcePost: Boolean);
var
ALink: TcxDataListenerLink;
begin
ALink := AddListenerLink(Self);
try
FCheckFocusingAfterFilterNeeded := True;
if Provider.IsEditing then
Provider.Post(AForcePost);
finally
if ALink.Ref <> nil then
Provider.FInsertedRecordIndex := -1;
RemoveListenerLink(ALink);
end;
end;
procedure TcxCustomDataController.PostEditingData;
begin
Provider.PostEditingData;
end;
procedure TcxCustomDataController.RefreshExternalData;
begin
end;
function TcxCustomDataController.SetEditValue(AItemIndex: Integer;
const AValue: Variant; AEditValueSource: TcxDataEditValueSource): Boolean;
begin
if CanInitEditing(AItemIndex) then
Result := Provider.SetEditValue(EditingRecordIndex, Fields[AItemIndex], AValue, AEditValueSource)
else
Result := False;
// Immediate Post
if Result and IsImmediatePost then
Post;
end;
procedure TcxCustomDataController.UpdateData;
begin
end;
// New Item Row
function TcxCustomDataController.GetEditingRecordIndex: Integer;
begin
if Provider.FEditingRecordIndex <> cxNullEditingRecordIndex then
Result := Provider.FEditingRecordIndex
else
Result := CalcEditingRecordIndex;
end;
function TcxCustomDataController.GetNewItemRecordIndex: Integer;
begin
if FNewItemRecordIndex = 0 then
FNewItemRecordIndex := DataStorage.AddInternalRecord;
Result := FNewItemRecordIndex;
end;
// Data Save/Load
procedure TcxCustomDataController.LoadFromStream(AStream: TStream);
var
S: AnsiString;
I, AValueCount, ADataValueCount: Integer;
AValueDef: TcxValueDef;
AReader: TcxReader;
begin
if IsProviderMode then
InvalidOperation(cxSDataProviderModeError);
AReader := TcxReader.Create(AStream);
try
if AReader.ReadAnsiString <> stDataControllerSignature then
InvalidOperation(cxSDataInvalidStreamFormat);
// ValueCount
AValueCount := AReader.ReadInteger;
// ValueDefs
// if AValueCount > 0 then
begin
ADataValueCount := 0;
for I := 0 to DataStorage.ValueDefs.Count - 1 do
begin
AValueDef := DataStorage.ValueDefs[I];
AValueDef.StreamStored := not IsValueDefInternal(AValueDef);
if AValueDef.StreamStored then
Inc(ADataValueCount);
end;
if ADataValueCount <> AValueCount then
InvalidOperation(cxSDataInvalidStreamFormat);
DataStorage.BeginStreaming(CompareByLinkObject);
try
// ValueDefs
for I := 0 to DataStorage.ValueDefs.Count - 1 do
begin
AValueDef := DataStorage.ValueDefs[I];
if AValueDef.StreamStored then
begin
ReadAnsiStringProc(AStream, S);
if AValueDef.ValueTypeClass.ClassName <> dxAnsiStringToString(S) then
InvalidOperation(cxSDataInvalidStreamFormat);
end;
end;
BeginUpdate;
try
ClearSelection;
// RecordCount
RecordCount := AReader.ReadInteger;
// Records
for I := 0 to RecordCount - 1 do
DataStorage.ReadData(I, AStream);
DataControllerInfo.Refresh;
finally
EndUpdate;
end;
finally
DataStorage.EndStreaming;
end;
end;
finally
AReader.Free;
end;
end;
procedure TcxCustomDataController.SaveToStream(AStream: TStream);
var
I, AValueCount: Integer;
AValueDef: TcxValueDef;
AWriter: TcxWriter;
begin
AWriter := TcxWriter.Create(AStream);
try
AWriter.WriteAnsiString(stDataControllerSignature);
// ValueCount
AValueCount := 0;
if not IsProviderMode then
begin
for I := 0 to DataStorage.ValueDefs.Count - 1 do
begin
AValueDef := DataStorage.ValueDefs[I];
AValueDef.StreamStored := not IsValueDefInternal(AValueDef);
if AValueDef.StreamStored then
Inc(AValueCount);
end;
end;
AWriter.WriteInteger(AValueCount);
// if AValueCount > 0 then
begin
DataStorage.BeginStreaming(CompareByLinkObject);
try
// ValueDefs
for I := 0 to DataStorage.ValueDefs.Count - 1 do
begin
AValueDef := DataStorage.ValueDefs[I];
if AValueDef.StreamStored then
AWriter.WriteAnsiString(dxStringToAnsiString(AValueDef.ValueTypeClass.ClassName));
end;
// RecordCount
AWriter.WriteInteger(RecordCount);
// Records
for I := 0 to RecordCount - 1 do
DataStorage.WriteData(I, AStream);
finally
DataStorage.EndStreaming;
end;
end;
finally
AWriter.Free;
end;
end;
// Master-Detail: Relations
function TcxCustomDataController.GetMasterDataController: TcxCustomDataController;
var
AMasterRelation: TcxCustomDataRelation;
begin
AMasterRelation := GetMasterRelation;
if AMasterRelation <> nil then
Result := AMasterRelation.DataController
else
Result := nil;
end;
function TcxCustomDataController.GetMasterRecordIndex: Integer;
begin
Result := FMasterRecordIndex;
end;
function TcxCustomDataController.GetMasterRelation: TcxCustomDataRelation;
begin
Result := FMasterRelation;
end;
function TcxCustomDataController.GetPatternDataController: TcxCustomDataController;
function FindByItem(ADataController: TcxCustomDataController; AItem: TObject): TcxCustomDataController;
var
I: Integer;
ARelation: TcxCustomDataRelation;
begin
ARelation := ADataController.Relations.FindByItem(AItem);
if ARelation <> nil then
Result := ARelation.DetailDataController
else
begin
Result := nil;
for I := 0 to ADataController.Relations.Count - 1 do
if ADataController.Relations[I].DetailDataController <> nil then
begin
Result := FindByItem(ADataController.Relations[I].DetailDataController, AItem);
if Result <> nil then
Break;
end;
end;
end;
begin
if FMasterRelation <> nil then
Result := FindByItem(GetRootDataController, FMasterRelation.Item)
else
Result := Self;
end;
function TcxCustomDataController.GetRootDataController: TcxCustomDataController;
begin
Result := Self;
while Result.FMasterRelation <> nil do
Result := Result.FMasterRelation.DataController;
end;
function TcxCustomDataController.IsDetailMode: Boolean;
begin
Result := (FMasterRelation <> nil);
end;
function TcxCustomDataController.CreateDetailLinkObject(ARelation: TcxCustomDataRelation; ARecordIndex: Integer): TObject;
begin
Result := nil;
end;
procedure TcxCustomDataController.FocusDetails(ARecordIndex: Integer);
var
I: Integer;
ADataController: TcxCustomDataController;
begin
for I := 0 to Relations.Count - 1 do
if IsDetailDataControllerExist(ARecordIndex, I) and
(GetDetailActiveRelationIndex(ARecordIndex) = I) then
begin
ADataController := GetDetailDataController(ARecordIndex, I);
ADataController.CheckFocusedRow;
Break;
end;
end;
function TcxCustomDataController.GetDetailDataControllerByLinkObject(ALinkObject: TObject): TcxCustomDataController;
begin
Result := nil;
end;
// Master-Detail: Grid Notifications
procedure TcxCustomDataController.ResetRelationByItem(AItem: TObject);
procedure FindAndReset(ADataController: TcxCustomDataController); // TODO: ref FindAndReset + FindAndRemove
var
I, J: Integer;
ARelation: TcxCustomDataRelation;
begin
with ADataController do
begin
ARelation := Relations.FindByItem(AItem);
if ARelation <> nil then
ClearDetails
else
begin
for I := 0 to DataStorage.RecordCount - 1 do
for J := 0 to Relations.Count - 1 do
begin
if IsDetailDataControllerExist(I, J) then
FindAndReset(GetDetailDataController(I, J));
end;
end;
end;
end;
begin
FindAndReset(Self);
end;
procedure TcxCustomDataController.SetMasterMode(AMasterRelation: TcxCustomDataRelation; AIsPattern: Boolean);
begin
BeginFullUpdate;
try
SetMasterRelation(AMasterRelation, -1);
IsPattern := AIsPattern;
finally
EndFullUpdate;
end;
end;
procedure TcxCustomDataController.SetMasterRelation(AMasterRelation: TcxCustomDataRelation;
AMasterRecordIndex: Integer);
begin
if (AMasterRelation = nil) and (FMasterRelation = nil) and
(AMasterRecordIndex = -1) and (FMasterRecordIndex = -1) then
Exit;
BeginUpdate;
try
if FMasterRelation <> nil then
begin
if (FMasterRelation.FDetailDataController = Self) and (AMasterRecordIndex = -1) then // It's Pattern
FMasterRelation.FDetailDataController := nil
else
CustomDataSource := nil;
if (FMasterRelation <> nil) and (FMasterRelation.DataController <> nil) and
(FMasterRelation.DataController.FCreatingDataController = Self) then
FMasterRelation.DataController.FCreatingDataController := nil;
Relations.Changed(nil); // Reset Details
end;
FMasterRelation := AMasterRelation;
if (FMasterRelation <> nil) and (AMasterRecordIndex = -1) then // It's Pattern
begin
FMasterRelation.FDetailDataController := Self;
FMasterRelation.Changed;
end;
if (FMasterRelation <> nil) and (AMasterRecordIndex <> -1) then // Isn't Pattern
begin
CustomDataSource := GetPatternDataController.CustomDataSource;
if (FMasterRelation <> nil) and (FMasterRelation.DataController <> nil) then
FMasterRelation.DataController.FCreatingDataController := Self;
end;
FMasterRecordIndex := AMasterRecordIndex;
LayoutChanged([lcData]);
finally
EndUpdate;
end;
end;
// Master-Detail: View Data
procedure TcxCustomDataController.ChangeDetailActiveRelationIndex(ARecordIndex: Integer; ARelationIndex: Integer);
var
ADetailObject: TcxDetailObject;
begin
if GetDetailActiveRelationIndex(ARecordIndex) <> ARelationIndex then
begin
CheckBrowseMode;
ADetailObject := Relations.GetDetailObject(ARecordIndex);
if Assigned(ADetailObject) then
begin
ADetailObject.ActiveRelationIndex := ARelationIndex;
Change([dccDetail]);
end;
end;
end;
function TcxCustomDataController.ChangeDetailExpanding(ARecordIndex: Integer;
AExpanded: Boolean): Boolean;
var
ADetailObject: TcxDetailObject;
begin
if GetDetailExpanding(ARecordIndex) <> AExpanded then
begin
Result := False;
FIsDetailExpanding := True;
try
CheckBrowseMode;
if (0 <= ARecordIndex) and (ARecordIndex < RecordCount) then
begin
ADetailObject := Relations.GetDetailObject(ARecordIndex);
if Assigned(ADetailObject) and CanChangeDetailExpanding(ARecordIndex, AExpanded) then
begin
ADetailObject.Expanded := AExpanded;
Result := True;
if (GetMasterDataController = nil) or (GetMasterDataController.LockCount = 0) then
Change([dccDetail]);
if AExpanded then
DoDetailExpanded(ARecordIndex)
else
DoDetailCollapsed(ARecordIndex);
end;
end;
finally
FIsDetailExpanding := False;
end;
end
else
Result := True;
end;
procedure TcxCustomDataController.ClearDetailLinkObject(ARecordIndex: Integer; ARelationIndex: Integer);
begin
if Relations.ClearDetailObject(ARecordIndex, ARelationIndex) then
Change([dccDetail]);
end;
procedure TcxCustomDataController.ClearDetails;
var
I: Integer;
begin
BeginUpdate;
try
for I := 0 to DataStorage.RecordCount - 1 do
ClearDetailLinkObject(I, -1);
finally
EndUpdate;
end;
end;
procedure TcxCustomDataController.CollapseDetails;
var
I: Integer;
ADetailObject: TcxDetailObject;
begin
CheckBrowseMode;
BeginUpdate;
try
for I := 0 to DataStorage.RecordCount - 1 do
begin
ClearDetailLinkObject(I, -1);
ADetailObject := Relations.GetDetailObject(I);
if Assigned(ADetailObject) then
begin
ADetailObject.Expanded := False;
Change([dccDetail]);
end;
end;
finally
EndUpdate;
end;
end;
procedure TcxCustomDataController.ForEachDetail(AMasterRelation: TcxCustomDataRelation;
AProc: TcxDataControllerEachDetailProc);
var
ARelationIndexes: TList;
procedure ForEach(ADataController: TcxCustomDataController; AIndex: Integer);
var
ARelationIndex, I: Integer;
ADetailDataController: TcxCustomDataController;
begin
ARelationIndex := Integer(ARelationIndexes[AIndex]);
with ADataController do
begin
for I := 0 to DataStorage.RecordCount - 1 do
begin
if IsDetailDataControllerExist(I, ARelationIndex) then
ADetailDataController := GetDetailDataController(I, ARelationIndex)
else
if FCreatingDataController <> nil then
ADetailDataController := FCreatingDataController
else
ADetailDataController := nil;
if ADetailDataController <> nil then
begin
if AIndex = (ARelationIndexes.Count - 1) then
AProc(ADetailDataController)
else
ForEach(ADetailDataController, AIndex + 1);
end;
end;
end;
end;
var
ARootDataController: TcxCustomDataController;
begin
ARelationIndexes := TList.Create;
try
ARootDataController := nil;
while AMasterRelation <> nil do
begin
ARootDataController := AMasterRelation.DataController;
ARelationIndexes.Insert(0, Pointer(AMasterRelation.Index));
AMasterRelation := ARootDataController.FMasterRelation;
end;
if ARootDataController <> nil then
ForEach(ARootDataController, 0);
finally
ARelationIndexes.Free;
end;
end;
function TcxCustomDataController.GetDetailActiveRelationIndex(ARecordIndex: Integer): Integer;
var
ADetailObject: TcxDetailObject;
begin
Result := -1;
if Relations.IsEmpty then Exit;
ADetailObject := Relations.GetDetailObject(ARecordIndex);
if Assigned(ADetailObject) then
Result := ADetailObject.ActiveRelationIndex;
end;
function TcxCustomDataController.GetDetailDataController(ARecordIndex: Integer; ARelationIndex: Integer): TcxCustomDataController;
begin
Result := GetDetailDataControllerByLinkObject(GetDetailLinkObject(ARecordIndex, ARelationIndex));
end;
function TcxCustomDataController.GetDetailExpanding(ARecordIndex: Integer): Boolean;
var
ADetailObject: TcxDetailObject;
begin
ADetailObject := Relations.GetDetailObject(ARecordIndex);
if Assigned(ADetailObject) then
Result := ADetailObject.Expanded
else
Result := False;
{
if Result then
if (Relations.Count > 0) and (Relations[0].DataController.GetPatternDataController.Relations[0].FDetailDataController = nil) then
begin
Result := False;
Exit;
end;
}
end;
function TcxCustomDataController.GetDetailHasChildren(ARecordIndex, ARelationIndex: Integer): Boolean;
var
AIsBound: Boolean;
ADetailObject: TcxDetailObject;
AInfoObject: TcxDetailInfoObject;
begin
// TODO: DB!!!
AIsBound := IsDataBound;
if IsDetailDataControllerExist(ARecordIndex, ARelationIndex) then
Result := GetDetailDataController(ARecordIndex, ARelationIndex).RecordCount <> 0
else
begin
Result := AIsBound;
if AIsBound then
begin
ADetailObject := Relations.GetDetailObject(ARecordIndex);
if Assigned(ADetailObject) then
begin
AInfoObject := ADetailObject.InfoObjects[ARelationIndex];
if AInfoObject = nil then
begin
AInfoObject := TcxDetailInfoObject.Create;
ADetailObject.InfoObjects[ARelationIndex] := AInfoObject;
end;
if not AInfoObject.HasChildrenAssigned then
begin
AInfoObject.HasChildren := Provider.GetDetailHasChildren(ARecordIndex, ARelationIndex);
AInfoObject.HasChildrenAssigned := True;
end;
Result := AInfoObject.HasChildren;
end;
end;
end;
end;
function TcxCustomDataController.GetDetailLinkObject(ARecordIndex: Integer; ARelationIndex: Integer): TObject;
function IsDetailObjectDestroyed(ADetailObject: TcxDetailObject): Boolean;
begin
// dataset in edit mode
Result := not ((0 <= ARecordIndex) and (ARecordIndex < RecordCount) and
(ADetailObject = Relations.GetDetailObject(ARecordIndex)));
end;
var
ADetailObject: TcxDetailObject;
begin
ADetailObject := Relations.GetDetailObject(ARecordIndex);
if Assigned(ADetailObject) then
begin
Result := ADetailObject.LinkObjects[ARelationIndex];
if Result = nil then
begin
if FCreatingLinkObject then Exit;
FCreatingLinkObject := True;
if GetPatternDataController <> nil then
GetPatternDataController.FCreatingLinkObject := True;
try
Result := CreateDetailLinkObject(Relations[ARelationIndex], ARecordIndex);
FCreatingDataController := nil; // !!!
if IsDetailObjectDestroyed(ADetailObject) then
begin
Result.Free;
Result := nil;
// DataNotify
end
else
ADetailObject.LinkObjects[ARelationIndex] := Result;
finally
if GetPatternDataController <> nil then
GetPatternDataController.FCreatingLinkObject := False;
FCreatingLinkObject := False;
end;
end;
if (Result = nil) and not FRecreatingLinkObject then // recreate DetailObject
begin
FRecreatingLinkObject := True;
try
Result := GetDetailLinkObject(ARecordIndex, ARelationIndex);
finally
FRecreatingLinkObject := False;
end;
end;
end
else
Result := nil;
end;
function TcxCustomDataController.IsDetailDataControllerExist(ARecordIndex: Integer; ARelationIndex: Integer): Boolean;
begin
Result := Relations.IsDetailObjectExist(ARecordIndex, ARelationIndex);
end;
procedure TcxCustomDataController.ResetHasChildrenFlag;
var
I: Integer;
ADetailObject: TcxDetailObject;
AChanged: Boolean;
begin
AChanged := False;
for I := 0 to RecordCount - 1 do
begin
ADetailObject := Relations.GetDetailObject(I);
if Assigned(ADetailObject) then
begin
if ADetailObject.ClearHasChildrenFlag then
AChanged := True;
end;
end;
if AChanged then
DataControllerInfo.RefreshView;
end;
// View Data
procedure TcxCustomDataController.ForEachRow(ASelectedRows: Boolean; AProc: TcxDataControllerEachRowProc);
var
I, J: Integer;
begin
if ASelectedRows then
begin
for I := 0 to GetSelectedCount - 1 do
begin
J := GetSelectedRowIndex(I);
AProc(J, GetRowInfo(J));
end;
end
else
begin
for I := 0 to GetRowCount - 1 do
AProc(I, GetRowInfo(I));
end;
end;
function TcxCustomDataController.GetNearestRowIndex(ARowIndex: Integer): Integer;
begin
Result := DataControllerInfo.GetNearestRowIndex(ARowIndex);
end;
function TcxCustomDataController.GetRowCount: Integer;
begin
Result := DataControllerInfo.GetRowCount;
end;
function TcxCustomDataController.GetRowIndexByRecordIndex(ARecordIndex: Integer;
AMakeVisible: Boolean): Integer;
begin
CheckRecordRange(ARecordIndex);
Result := DataControllerInfo.GetRowIndexByRecordIndex(ARecordIndex, AMakeVisible);
end;
function TcxCustomDataController.GetRowInfo(ARowIndex: Integer): TcxRowInfo;
begin
Result := DataControllerInfo.GetRowInfo(ARowIndex);
end;
function TcxCustomDataController.GetRowDisplayText(const ARowInfo: TcxRowInfo;
var AItemIndex: Integer): string;
begin
if (ARowInfo.RecordIndex <> NewItemRecordIndex) and (ARowInfo.Level < Groups.GetGroupingItemCount) then
AItemIndex := Groups.GetGroupingItemIndex(ARowInfo.Level);
Result := GetDisplayText(ARowInfo.RecordIndex, AItemIndex);
end;
function TcxCustomDataController.GetRowValue(const ARowInfo: TcxRowInfo; AItemIndex: Integer): Variant;
begin
if (ARowInfo.RecordIndex <> NewItemRecordIndex) and (ARowInfo.Level < Groups.GetGroupingItemCount) then
AItemIndex := Groups.GetGroupingItemIndex(ARowInfo.Level);
Result := GetValue(ARowInfo.RecordIndex, AItemIndex);
end;
procedure TcxCustomDataController.MakeRecordVisible(ARecordIndex: Integer);
begin
GetRowIndexByRecordIndex(ARecordIndex, True);
end;
function TcxCustomDataController.FocusSelectedRow(ASelectedIndex: Integer): Boolean;
var
ARowIndex: Integer;
begin
ARowIndex := GetSelectedRowIndex(ASelectedIndex);
FocusedRowIndex := ARowIndex;
Result := FocusedRowIndex = ARowIndex;
end;
procedure TcxCustomDataController.RestoreDataSetPos;
begin
Provider.RestorePos;
end;
procedure TcxCustomDataController.SaveDataSetPos;
begin
Provider.SavePos;
end;
// Navigation
procedure TcxCustomDataController.ChangeFocusedRecordIndex(ARecordIndex: Integer);
var
ARowIndex: Integer;
begin
ARowIndex := GetRowIndexByRecordIndex(ARecordIndex, True);
ChangeFocusedRowIndex(ARowIndex);
end;
function TcxCustomDataController.ChangeFocusedRowIndex(ARowIndex: Integer): Boolean;
var
AIsFocusedRowIndexChanging: Boolean;
begin
Result := False;
if Provider.IsSyncMode and not Provider.FInInserting and
(DataControllerInfo.FocusedRowIndex <> ARowIndex) and not CheckMasterBrowseMode then
Exit;
if DataControllerInfo.FocusedRowIndex <> ARowIndex then
CheckDetailsBrowseMode;
AIsFocusedRowIndexChanging := DataControllerInfo.FocusedRowIndex <> ARowIndex;
DataControllerInfo.FocusedRowIndex := ARowIndex;
// TODO: check
if AIsFocusedRowIndexChanging and Provider.IsSyncMode and SyncDetailsFocusWithMaster and
(FocusedRecordIndex <> -1) then
begin
FInFocusDetails := True;
try
FocusDetails(FocusedRecordIndex);
finally
FInFocusDetails := False;
end;
end;
Result := True;
end;
procedure TcxCustomDataController.CheckFocusedRow;
begin
if (FocusedRowIndex = -1) and not NewItemRowFocused then
FocusedRowIndex := 0;
end;
function TcxCustomDataController.GetFocusedRecordIndex: Integer;
begin
Result := DataControllerInfo.FocusedRecordIndex;
end;
function TcxCustomDataController.GetFocusedRowIndex: Integer;
begin
Result := DataControllerInfo.FocusedRowIndex;
end;
procedure TcxCustomDataController.GotoFirst;
begin
if IsGridMode then
Provider.First
else
ChangeFocusedRowIndex(0);
end;
procedure TcxCustomDataController.GotoLast;
begin
if IsGridMode then
Provider.Last
else
ChangeFocusedRowIndex(GetRowCount - 1);
end;
procedure TcxCustomDataController.GotoNext;
var
AFocusedRowIndex: Integer;
begin
if IsGridMode then
Provider.Next
else
begin
AFocusedRowIndex := GetFocusedRowIndex + 1;
ChangeFocusedRowIndex(AFocusedRowIndex);
end;
end;
procedure TcxCustomDataController.GotoPrev;
var
AFocusedRowIndex: Integer;
begin
if IsGridMode then
Provider.Prev
else
begin
AFocusedRowIndex := GetFocusedRowIndex - 1;
if AFocusedRowIndex < 0 then AFocusedRowIndex := 0;
ChangeFocusedRowIndex(AFocusedRowIndex);
end;
end;
function TcxCustomDataController.IsBOF: Boolean;
begin
if IsGridMode {and not Provider.IsInserting} then
Result := Provider.IsBOF
else
Result := (GetFocusedRowIndex = 0) or (GetRowCount = 0);
end;
function TcxCustomDataController.IsEOF: Boolean;
var
ARowCount: Integer;
begin
if IsGridMode then
Result := Provider.IsEOF
else
begin
ARowCount := GetRowCount;
Result := (ARowCount = 0) or
({(ARowCount > 1) and} (GetFocusedRowIndex = (ARowCount - 1)));
end;
end;
function TcxCustomDataController.IsGridMode: Boolean;
begin
Result := Provider.IsGridMode;
end;
procedure TcxCustomDataController.MoveBy(ADistance: Integer);
var
ARowIndex: Integer;
begin
if IsGridMode then
Provider.MoveBy(ADistance)
else
begin
ARowIndex := GetFocusedRowIndex + ADistance;
if ARowIndex < 0 then ARowIndex := 0;
ChangeFocusedRowIndex(ARowIndex);
end;
end;
procedure TcxCustomDataController.Scroll(ADistance: Integer);
begin
if IsGridMode then
Provider.Scroll(ADistance);
end;
procedure TcxCustomDataController.SetFocus;
begin
end;
procedure TcxCustomDataController.ClearBookmark;
var
APrevBookmarkAvailable: Boolean;
begin
APrevBookmarkAvailable := IsBookmarkAvailable;
InternalClearBookmark;
if IsBookmarkAvailable <> APrevBookmarkAvailable then
Change([dccBookmark]);
end;
procedure TcxCustomDataController.GotoBookmark;
begin
if IsBookmarkAvailable then
InternalGotoBookmark;
end;
function TcxCustomDataController.IsBookmarkAvailable: Boolean;
begin
Result := FBookmarkRecordIndex <> -1;
end;
function TcxCustomDataController.IsBookmarkRow(ARowIndex: Integer): Boolean;
begin
Result := IsBookmarkAvailable and
(GetRowInfo(ARowIndex).RecordIndex = FBookmarkRecordIndex);
end;
procedure TcxCustomDataController.SaveBookmark;
begin
if InternalSaveBookmark then
Change([dccBookmark]);
end;
// Filtering
function TcxCustomDataController.GetFilterDataValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant;
begin
Result := GetInternalValue(ARecordIndex, AField);
end;
function TcxCustomDataController.GetFilterItemFieldCaption(AItem: TObject): string;
begin
Result := '';
end;
function TcxCustomDataController.GetFilterItemFieldName(AItem: TObject): string;
begin
Result := GetFilterItemFieldCaption(AItem);
end;
// Search
function TcxCustomDataController.ChangeIncrementalFilterText(const AText: string): Boolean;
begin
Result := Assigned(FIncrementalFilterField) and (FIncrementalFilterText <> AText);
if Result then
SetIncrementalFilter(FIncrementalFilterField.Index, AText);
end;
function TcxCustomDataController.FindRecordIndexByText(AStartRecordIndex, AItemIndex: Integer;
const AText: string; APartialCompare, ACircular, AForward: Boolean): Integer;
var
AField: TcxCustomDataField;
ARecordCount: Integer;
function Find(AStartIndex, AEndIndex: Integer): Integer;
var
I: Integer;
S: string;
ARecordIndex: Integer;
begin
Result := -1;
I := AStartIndex;
while (AForward and (I <= AEndIndex)) or
(not AForward and (I >= AEndIndex)) do
begin
ARecordIndex := FilteredRecordIndex[I];
S := GetInternalDisplayText(ARecordIndex, AField);
if DataCompareText(S, AText, APartialCompare) then
begin
Result := ARecordIndex;
Break;
end;
if AForward then
Inc(I)
else
Dec(I);
end;
end;
begin
CheckItemRange(AItemIndex);
AField := Fields[AItemIndex];
ARecordCount := FilteredRecordCount;
Result := -1;
if (AStartRecordIndex < 0) or (AStartRecordIndex >= ARecordCount) then Exit;
if AForward then
begin
Result := Find(AStartRecordIndex, ARecordCount - 1);
if (Result = -1) and (AStartRecordIndex <> 0) and ACircular then
Result := Find(0, AStartRecordIndex - 1);
end
else
begin
Result := Find(AStartRecordIndex, 0);
if (Result = -1) and (AStartRecordIndex <> 0) and ACircular then
Result := Find(ARecordCount - 1, AStartRecordIndex + 1);
end;
end;
function TcxCustomDataController.GetIncrementalFilterText: string;
begin
Result := FIncrementalFilterText;
end;
function TcxCustomDataController.IsIncrementalFilter: Boolean;
begin
Result := Assigned(FIncrementalFilterField) and (FIncrementalFilterText <> '');
end;
procedure TcxCustomDataController.ResetIncrementalFilter;
var
AIsIncrementalFilter: Boolean;
begin
AIsIncrementalFilter := IsIncrementalFilter;
FIncrementalFilterField := nil;
FIncrementalFilterText := '';
if AIsIncrementalFilter and not (csDestroying in FOwner.ComponentState) then
Refresh;
end;
function TcxCustomDataController.SetIncrementalFilter(AItemIndex: Integer;
const AText: string): Integer;
begin
CheckItemRange(AItemIndex);
FIncrementalFilterField := Fields[AItemIndex];
FIncrementalFilterText := AText;
Refresh;
if FilteredRecordCount > 0 then
Result := FilteredRecordIndex[0]
else
Result := -1;
end;
// Sorting
procedure TcxCustomDataController.ChangeItemSortingIndex(AItemIndex: Integer; ASortingIndex: Integer);
begin
CheckItemRange(AItemIndex);
if GetItemSortingIndex(AItemIndex) <> ASortingIndex then
begin
BeginUpdate;
try
if (Groups.ItemGroupIndex[AItemIndex] <> -1) and
(GetItemSortOrder(AItemIndex) = soAscending) and
(ASortingIndex = -1) then
ChangeSorting(AItemIndex, soNone)
else
begin
if GetItemSortingIndex(AItemIndex) = -1 then // grouped
ChangeSorting(AItemIndex, GetItemSortOrder(AItemIndex));
if GetItemSortOrder(AItemIndex) <> soNone then
DataControllerInfo.ChangeSortIndex(Fields[AItemIndex], ASortingIndex);
end;
finally
EndUpdate;
end;
end;
end;
procedure TcxCustomDataController.ChangeSorting(AItemIndex: Integer; ASortOrder: TcxDataSortOrder);
begin
CheckItemRange(AItemIndex);
// if GetItemSortOrder(AItemIndex) <> ASortOrder then
if DataControllerInfo.SortingFieldList.SortOrderByField(Fields[AItemIndex]) <> ASortOrder then
begin
BeforeSorting;
FSortingChanged := True;
DataControllerInfo.ChangeSorting(Fields[AItemIndex], ASortOrder);
end;
end;
procedure TcxCustomDataController.ClearSorting(AKeepGroupedItems: Boolean);
begin
DataControllerInfo.ClearSorting(AKeepGroupedItems);
end;
function TcxCustomDataController.GetItemSortByDisplayText(AItemIndex: Integer;
ASortByDisplayText: Boolean): Boolean;
begin
Result := False;
end;
function TcxCustomDataController.GetItemSortOrder(AItemIndex: Integer): TcxDataSortOrder;
var
I: Integer;
begin
CheckItemRange(AItemIndex);
I := DataControllerInfo.SortingFieldList.SortIndexByField(Fields[AItemIndex]);
if I <> -1 then
Result := DataControllerInfo.SortingFieldList[I].SortOrder
else
begin
I := DataControllerInfo.GroupingFieldList.SortIndexByField(Fields[AItemIndex]);
if I <> -1 then
Result := DataControllerInfo.GroupingFieldList[I].SortOrder
else
Result := soNone;
end;
// Result := DataControllerInfo.TotalSortingFieldList.SortOrderByField(Fields[AItemIndex]);
end;
function TcxCustomDataController.GetItemSortingIndex(AItemIndex: Integer): Integer;
begin
CheckItemRange(AItemIndex);
Result := DataControllerInfo.SortingFieldList.SortIndexByField(Fields[AItemIndex]);
end;
function TcxCustomDataController.GetSortingItemCount: Integer;
begin
Result := DataControllerInfo.SortingFieldList.Count;
end;
function TcxCustomDataController.GetSortingItemIndex(Index: Integer): Integer;
begin
Result := DataControllerInfo.SortingFieldList[Index].Field.Index;
end;
// MultiSelect
procedure TcxCustomDataController.ChangeRowSelection(ARowIndex: Integer; ASelection: Boolean);
begin
if not MultiSelect then Exit;
DataControllerInfo.ChangeRowSelection(ARowIndex, ASelection);
end;
procedure TcxCustomDataController.CheckFocusedSelected;
var
ARowIndex: Integer;
begin
if not MultiSelect then Exit;
BeginUpdate;
try
ClearSelection;
ARowIndex := GetFocusedRowIndex;
if ARowIndex <> -1 then
ChangeRowSelection(ARowIndex, True);
finally
EndUpdate;
end;
end;
procedure TcxCustomDataController.ClearSelection;
begin
DataControllerInfo.ClearSelection;
end;
procedure TcxCustomDataController.ClearSelectionAnchor;
begin
DataControllerInfo.ClearSelectionAnchor;
end;
function TcxCustomDataController.GetRowId(ARowIndex: Integer): Variant;
begin
Result := ARowIndex;
end;
function TcxCustomDataController.GetSelectedCount: Integer;
begin
if IsFocusedSelectedMode then
begin
if FFocusedSelected and (GetFocusedRowIndex <> -1) then
Result := 1
else
Result := 0;
end
else
Result := DataControllerInfo.GetSelectedCount;
end;
function TcxCustomDataController.GetSelectedRowIndex(Index: Integer): Integer;
begin
if IsFocusedSelectedMode then
begin
if FFocusedSelected and (Index = 0) then
Result := GetFocusedRowIndex
else
Result := -1;
end
else
Result := DataControllerInfo.GetSelectedRowIndex(Index);
end;
function TcxCustomDataController.GetSelectionAnchorRowIndex: Integer;
begin
Result := DataControllerInfo.Selection.AnchorRowIndex;
end;
function TcxCustomDataController.IsRowSelected(ARowIndex: Integer): Boolean;
begin
if IsFocusedSelectedMode then
Result := FFocusedSelected and (GetFocusedRowIndex = ARowIndex)
else
Result := DataControllerInfo.IsRowSelected(ARowIndex);
end;
function TcxCustomDataController.IsSelectionAnchorExist: Boolean;
begin
Result := GetSelectionAnchorRowIndex <> -1;
end;
procedure TcxCustomDataController.SelectAll;
var
ARowCount: Integer;
begin
ARowCount := GetRowCount;
if ARowCount > 0 then
SelectRows(0, ARowCount - 1);
end;
procedure TcxCustomDataController.SelectRows(AStartRowIndex, AEndRowIndex: Integer);
var
I: Integer;
begin
if not MultiSelect then Exit;
if AStartRowIndex > AEndRowIndex then
begin
I := AEndRowIndex;
AEndRowIndex := AStartRowIndex;
AStartRowIndex := I;
end;
BeginUpdate;
try
for I := AStartRowIndex to AEndRowIndex do
ChangeRowSelection(I, True);
finally
EndUpdate;
end;
end;
procedure TcxCustomDataController.SelectFromAnchor(ARowIndex: Integer; AKeepSelection: Boolean);
var
ASelectionAnchorRowIndex: Integer;
begin
ASelectionAnchorRowIndex := GetSelectionAnchorRowIndex;
if ASelectionAnchorRowIndex <> -1 then
begin
BeginUpdate;
try
if not AKeepSelection then
ClearSelection;
SelectRows(ASelectionAnchorRowIndex, ARowIndex);
finally
EndUpdate;
end;
end;
end;
procedure TcxCustomDataController.SetSelectionAnchor(ARowIndex: Integer);
begin
// TODO: check?
DataControllerInfo.CheckRowIndex(ARowIndex);
DataControllerInfo.Selection.FAnchorRowIndex := ARowIndex;
end;
procedure TcxCustomDataController.SyncSelected(ASelected: Boolean);
var
AFocusedRowIndex: Integer;
begin
if not IsFocusedSelectedMode then Exit;
if FFocusedSelected <> ASelected then
begin
FFocusedSelected := ASelected;
AFocusedRowIndex := GetFocusedRowIndex;
if AFocusedRowIndex <> -1 then
begin
FPrevSelectionChangedInfo.SelectedCount := 1;
FPrevSelectionChangedInfo.RowIndex := AFocusedRowIndex;
Change([dccSelection]);
end;
end;
end;
procedure TcxCustomDataController.SyncSelectionFocusedRecord;
var
AFocusedRowIndex: Integer;
begin
AFocusedRowIndex := GetFocusedRowIndex;
if AFocusedRowIndex = -1 then
ClearSelection
else
begin
SetSelectionAnchor(AFocusedRowIndex);
SelectFromAnchor(AFocusedRowIndex, False);
end;
end;
// IUnknown
function TcxCustomDataController.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := S_OK
else
Result := E_NOINTERFACE;
end;
function TcxCustomDataController._AddRef: Integer;
begin
Result := -1; // -1 indicates no reference counting is taking Position
end;
function TcxCustomDataController._Release: Integer;
begin
Result := -1; // -1 indicates no reference counting is taking Position
end;
// Based
function TcxCustomDataController.AddInternalField: TcxCustomDataField;
begin
Result := GetFieldClass.Create(Fields);
Result.IsInternal := True;
Fields.Add(Result);
end;
function TcxCustomDataController.AddField: TcxCustomDataField;
begin
Result := GetFieldClass.Create(Fields);
Fields.Add(Result);
end;
function TcxCustomDataController.AppendInSmartLoad: Integer;
var
AValueDefReader: TcxValueDefReader;
begin
FInSmartLoad := True;
try
if IsProviderMode then
Result := LoadRecord(CustomDataSource.AppendRecord)
else
begin
AValueDefReader := Provider.GetValueDefReaderClass.Create;
try
Result := LoadRecord(AValueDefReader);
DataChanged(dcNew, -1, -1);
finally
AValueDefReader.Free;
end;
end;
finally
FInSmartLoad := False;
end;
end;
procedure TcxCustomDataController.BeforeSorting;
begin
if IsEditing then
Post; // !
end;
function TcxCustomDataController.CalcEditingRecordIndex: Integer;
begin
if NewItemRowFocused then
Result := NewItemRecordIndex
else
if GetFocusedRowIndex <> -1 then
Result := GetRowInfo(GetFocusedRowIndex).RecordIndex
else
Result := cxNullEditingRecordIndex;
end;
function TcxCustomDataController.CanChangeDetailExpanding(ARecordIndex: Integer;
AExpanded: Boolean): Boolean;
begin
Result := True;
if AExpanded then
DoDetailExpanding(ARecordIndex, Result)
else
DoDetailCollapsing(ARecordIndex, Result);
end;
function TcxCustomDataController.CanFocusRecord(ARecordIndex: Integer): Boolean;
begin
if not Provider.IsDataSource and (Provider.EditingRecordIndex <> ARecordIndex) then
Post;
Result := True;
end;
function TcxCustomDataController.CanSelectRow(ARowIndex: Integer): Boolean;
begin
Result := True; // override in grid for Event
end;
procedure TcxCustomDataController.CheckChanges;
begin
if LockCount = 0 then
begin
Unlocked;
if FChanges <> [] then
begin
if FStructureChanged then
begin
FStructureChanged := False;
LayoutChanged([lcStructure]);
end;
Update;
end;
end;
end;
procedure TcxCustomDataController.Change(AChanges: TcxDataControllerChanges);
begin
FChanges := FChanges + AChanges;
CheckChanges;
end;
procedure TcxCustomDataController.CheckBookmarkValid(ADeletedRecordIndex: Integer);
begin
if not IsBookmarkAvailable then Exit;
if InternalCheckBookmark(ADeletedRecordIndex) then
begin
InternalClearBookmark;
Change([dccBookmark]);
end;
end;
procedure TcxCustomDataController.CheckDataSetCurrent;
begin
end;
procedure TcxCustomDataController.CheckEditingRecordIndex(ARecordIndex: Integer);
begin
if ((ARecordIndex >= 0) and (ARecordIndex <= Provider.EditingRecordIndex)) or
(RecordCount <= Provider.EditingRecordIndex) then
Provider.ResetEditing;
end;
procedure TcxCustomDataController.CheckInternalRecordRange(ARecordIndex: Integer);
begin
if FNewItemRecordIndex = ARecordIndex then
FNewItemRecordIndex := 0;
end;
procedure TcxCustomDataController.CheckItemRange(AItemIndex: Integer);
begin
if not ((0 <= AItemIndex) and (AItemIndex < ItemCount)) then
InvalidOperation(cxSDataItemIndexError);
end;
function TcxCustomDataController.CheckDetailsBrowseMode: Boolean;
var
I, J: Integer;
ADataController: TcxCustomDataController;
begin
for I := 0 to RecordCount - 1 do
for J := 0 to Relations.Count - 1 do
begin
if IsDetailDataControllerExist(I, J) then
begin
ADataController := GetDetailDataController(I, J);
ADataController.CheckBrowseMode;
end;
end;
Result := True;
end;
function TcxCustomDataController.CheckMasterBrowseMode: Boolean;
begin
Result := True;
end;
procedure TcxCustomDataController.CheckMode;
var
APrevGridMode: Boolean;
begin
APrevGridMode := IsGridMode;
Provider.FLoadAllNeeded := Groups.GroupingItemCount > 0;
if APrevGridMode <> Provider.IsGridMode then
RestructData;
end;
procedure TcxCustomDataController.CheckNearestFocusRow;
var
ARecordIndex: Integer;
begin
if FNearestRecordIndex <> -1 then // !!!
begin
if FNearestRecordIndex < RecordCount then
ChangeFocusedRecordIndex(FNearestRecordIndex);
Exit;
end;
if (LockCount = 0) and (GetFocusedRecordIndex = -1) then
begin
ARecordIndex := GetLastRecordIndex;
ChangeFocusedRecordIndex(ARecordIndex);
end;
end;
procedure TcxCustomDataController.CheckRange(ARecordIndex, AItemIndex: Integer);
begin
CheckRecordRange(ARecordIndex);
CheckItemRange(AItemIndex);
end;
procedure TcxCustomDataController.CheckRecordRange(ARecordIndex: Integer);
begin
NewItemRecordIndex;
if not (((0 <= ARecordIndex) and (ARecordIndex < RecordCount)) or
(ARecordIndex = FNewItemRecordIndex) or (ARecordIndex = Provider.FSavedRecordIndex)) then
InvalidOperation(cxSDataRecordIndexError);
end;
procedure TcxCustomDataController.CheckSelectedCount(ADeletedRecordIndex: Integer);
begin
DataControllerInfo.TruncateSelection;
CheckBookmarkValid(ADeletedRecordIndex);
end;
procedure TcxCustomDataController.ClearDataChangedListeners;
var
I: Integer;
begin
// TODO: TcxEventList?
for I := 0 to FDataChangedListeners.Count - 1 do
TNotifyEventItem(FDataChangedListeners[I]).Free;
FDataChangedListeners.Clear;
end;
procedure TcxCustomDataController.ClearDetailsMasterRelation(ARelation: TcxCustomDataRelation);
var
ARelationIndex, I: Integer;
ADetailLinkObject: TObject;
begin
// Relation Destroying
ARelationIndex := ARelation.Index;
for I := 0 to DataStorage.RecordCount - 1 do
if IsDetailDataControllerExist(I, ARelationIndex) then
begin
ADetailLinkObject := GetDetailLinkObject(I, ARelationIndex);
if ADetailLinkObject <> nil then
GetDetailDataControllerByLinkObject(ADetailLinkObject).FMasterRelation := nil;
end;
end;
procedure TcxCustomDataController.ProviderValueDefSetProc(AValueDef: TcxValueDef;
AFromRecordIndex, AToRecordIndex: Integer; AValueDefReader: TcxValueDefReader);
var
AField: TcxCustomDataField;
begin
AField := TcxCustomDataField(AValueDef.LinkObject);
if AField.IsValueDefInternal then Exit;
Provider.SetValue(AToRecordIndex, AField, DataStorage.GetValue(AFromRecordIndex, AValueDef));
if AValueDef.TextStored then
Provider.SetDisplayText(AToRecordIndex, AField, DataStorage.GetDisplayText(AFromRecordIndex, AValueDef));
end;
procedure TcxCustomDataController.CopyRecord(AFromRecordIndex, AToRecordIndex: Integer);
var
AValueDefReader: TcxValueDefRecordReader;
begin
AValueDefReader := TcxValueDefRecordReader.Create(Self, AFromRecordIndex);
try
if IsProviderMode and (AToRecordIndex >= 0) then
DataStorage.ReadRecordFrom(AFromRecordIndex, AToRecordIndex, AValueDefReader, ProviderValueDefSetProc)
else
DataStorage.ReadRecord(AToRecordIndex, AValueDefReader);
finally
AValueDefReader.Free;
end;
if AToRecordIndex >= 0 then // not internal
DataChanged(dcRecord, -1, -1);
end;
procedure TcxCustomDataController.CorrectAfterInsert(ARecordIndex: Integer);
var
I, J: Integer;
begin
for I := ARecordIndex + 1 to DataStorage.RecordCount - 1 do
for J := 0 to Relations.Count - 1 do
if IsDetailDataControllerExist(I, J) then
Inc(GetDetailDataController(I, J).FMasterRecordIndex);
end;
procedure TcxCustomDataController.CorrectAfterDelete(ARecordIndex: Integer);
var
I, J: Integer;
begin
if ARecordIndex < 0 then Exit;
CheckEditingRecordIndex(ARecordIndex);
if Relations.Count > 0 then
begin
for I := ARecordIndex {+ 1} to DataStorage.RecordCount - 1 do
for J := 0 to Relations.Count - 1 do
if IsDetailDataControllerExist(I, J) then
Dec(GetDetailDataController(I, J).FMasterRecordIndex);
end;
end;
procedure TcxCustomDataController.CorrectPrevSelectionChangedInfo;
begin
if FPrevSelectionChangedInfo.SelectedCount = 1 then
begin
FPrevSelectionChangedInfo.SelectedCount := GetSelectedCount;
if FPrevSelectionChangedInfo.SelectedCount = 1 then
FPrevSelectionChangedInfo.RowIndex := GetSelectedRowIndex(0)
else
FPrevSelectionChangedInfo.RowIndex := -1;
// TODO: flag reset?
end;
end;
procedure TcxCustomDataController.DeleteInSmartLoad(ARecordIndex: Integer);
begin
if IsProviderMode then
Provider.DeleteRecord(ARecordIndex);
DataStorage.DeleteRecord(ARecordIndex);
end;
procedure TcxCustomDataController.Unlocked;
begin
end;
procedure TcxCustomDataController.DoAfterCancel;
begin
if Assigned(FOnAfterCancel) then
FOnAfterCancel(Self);
end;
procedure TcxCustomDataController.DoAfterDelete;
begin
if Assigned(FOnAfterDelete) then
FOnAfterDelete(Self);
end;
procedure TcxCustomDataController.DoAfterInsert;
begin
if Assigned(FOnAfterInsert) then
FOnAfterInsert(Self);
end;
procedure TcxCustomDataController.DoAfterPost;
begin
if Assigned(FOnAfterPost) then
FOnAfterPost(Self);
end;
procedure TcxCustomDataController.DoBeforeCancel;
begin
if Assigned(FOnBeforeCancel) then
FOnBeforeCancel(Self);
end;
procedure TcxCustomDataController.DoBeforeDelete(ARecordIndex: Integer);
begin
if Assigned(FOnBeforeDelete) then
FOnBeforeDelete(Self, ARecordIndex);
end;
procedure TcxCustomDataController.DoBeforeInsert;
begin
if Assigned(FOnBeforeInsert) then
FOnBeforeInsert(Self);
end;
procedure TcxCustomDataController.DoBeforePost;
begin
if Assigned(FOnBeforePost) then
FOnBeforePost(Self);
end;
function TcxCustomDataController.DoFilterRecord(ARecordIndex: Integer): Boolean;
begin
Result := True;
if Assigned(FOnFilterRecord) then
FOnFilterRecord(Self, ARecordIndex, Result);
end;
function TcxCustomDataController.IsFilterRecord: Boolean;
begin
Result := Assigned(FOnFilterRecord);
end;
procedure TcxCustomDataController.DoNewRecord(ARecordIndex: Integer);
begin
if Assigned(FOnNewRecord) then
FOnNewRecord(Self, ARecordIndex);
end;
procedure TcxCustomDataController.DoDataChanged;
begin
if not (csDestroying in FOwner.ComponentState) and IsDataChangedListenersExist then
begin
if IsLoading then
FDataChangedFlag := True
else
begin
FDataChangedFlag := False;
NotifyDataChangedListeners;
end;
end;
end;
procedure TcxCustomDataController.DoDetailExpanding(ARecordIndex: Integer; var AAllow: Boolean);
begin
if Assigned(FOnDetailExpanding) then
FOnDetailExpanding(Self, ARecordIndex, AAllow);
end;
procedure TcxCustomDataController.DoDetailExpanded(ARecordIndex: Integer);
begin
if Assigned(FOnDetailExpanded) then
FOnDetailExpanded(Self, ARecordIndex);
end;
procedure TcxCustomDataController.DoDetailCollapsing(ARecordIndex: Integer; var AAllow: Boolean);
begin
if Assigned(FOnDetailCollapsing) then
FOnDetailCollapsing(Self, ARecordIndex, AAllow);
end;
procedure TcxCustomDataController.DoDetailCollapsed(ARecordIndex: Integer);
begin
if Assigned(FOnDetailCollapsed) then
FOnDetailCollapsed(Self, ARecordIndex);
end;
function TcxCustomDataController.DoIncrementalFilterRecord(ARecordIndex: Integer): Boolean;
var
S: string;
begin
S := GetInternalDisplayText(ARecordIndex, FIncrementalFilterField);
Result := DataCompareText(S, FIncrementalFilterText, True);
end;
procedure TcxCustomDataController.DoReadRecord(ARecordIndex: Integer);
begin
Provider.CorrectRecordIndex(ARecordIndex);
end;
function TcxCustomDataController.DoSearchInGridMode(const ASubText: string;
AForward, ANext: Boolean): Boolean;
begin
Result := False;
end;
function TcxCustomDataController.FindItemByInternalID(AID: Integer): TObject;
var
I: Integer;
begin
for I := 0 to ItemCount - 1 do
begin
Result := GetItem(I);
if GetItemID(Result) = AID then
Exit;
end;
Result := nil;
end;
function TcxCustomDataController.FindItemByName(const AName: string): TObject;
var
I: Integer;
begin
for I := 0 to ItemCount - 1 do
begin
Result := GetItem(I);
if SameText(GetItemName(Result), AName) then
Exit;
end;
Result := nil;
end;
function TcxCustomDataController.FindProperItemLink(AItemLink: TObject): TObject;
begin
Result := FindItemByInternalID(GetItemID(AItemLink));
end;
function TcxCustomDataController.GetActiveRecordIndex: Integer;
begin
Result := GetFocusedRecordIndex;
end;
function TcxCustomDataController.GetDataProviderClass: TcxCustomDataProviderClass;
begin
Result := TcxCustomDataProvider;
end;
function TcxCustomDataController.GetDataSelectionClass: TcxDataSelectionClass;
begin
Result := TcxDataSelection;
end;
function TcxCustomDataController.GetEditOperations: TcxDataControllerEditOperations;
begin
Result := [];
if Provider.CanAppend then
Result := Result + [dceoAppend];
if Provider.CanDelete then
Result := Result + [dceoDelete];
if Provider.CanModify then
Result := Result + [dceoEdit];
if Provider.CanInsert then
Result := Result + [dceoInsert];
if not Provider.IsDataSource or Provider.IsSyncMode then
Result := Result + [dceoShowEdit];
end;
function TcxCustomDataController.GetEditState: TcxDataControllerEditState;
begin
Result := [];
if Provider.IsInserting then
Result := Result + [dceInsert]
else
if Provider.IsEditing then
Result := Result + [dceEdit];
if (Result <> []) and Provider.IsChanging then
Result := Result + [dceChanging];
if (Result <> []) and Provider.IsModified then
Result := Result + [dceModified];
end;
function TcxCustomDataController.GetDefaultActiveRelationIndex: Integer;
begin
Result := 0;
end;
function TcxCustomDataController.GetFieldClass: TcxCustomDataFieldClass;
begin
Result := TcxCustomDataField;
end;
function TcxCustomDataController.GetFilterCriteriaClass: TcxDataFilterCriteriaClass;
begin
Result := TcxDataFilterCriteria;
end;
function TcxCustomDataController.GetInternalDisplayText(ARecordIndex: Integer;
AField: TcxCustomDataField): string;
begin
if (ARecordIndex >= 0) and IsProviderMode and IsDataField(AField) then
Result := Provider.GetDisplayText(ARecordIndex, AField)
else
Result := GetStoredDisplayText(ARecordIndex, AField);
end;
function TcxCustomDataController.GetItemID(AItem: TObject): Integer;
begin
Result := -1;
end;
function TcxCustomDataController.GetItemName(AItem: TObject): string;
begin
if AItem is TComponent then
Result := TComponent(AItem).Name
else
Result := '';
end;
function TcxCustomDataController.GetInternalRecordId(ARecordIndex: Integer;
AFieldList: TList): Variant;
var
I: Integer;
begin
if AFieldList.Count > 0 then
begin
if AFieldList.Count > 1 then
begin
Result := VarArrayCreate([0, AFieldList.Count - 1], varVariant);
for I := 0 to AFieldList.Count - 1 do
Result[I] := GetInternalValue(ARecordIndex, TcxCustomDataField(AFieldList[I]));
end
else
Result := GetInternalValue(ARecordIndex, TcxCustomDataField(AFieldList[0]));
end
else
begin
if IsProviderMode then
Result := Provider.GetRecordId(ARecordIndex)
else
Result := Null;
end;
end;
function TcxCustomDataController.GetInternalValue(ARecordIndex: Integer; AField: TcxCustomDataField): Variant;
begin
if (ARecordIndex >= 0) and IsProviderMode and IsDataField(AField) then
Result := Provider.GetValue(ARecordIndex, AField)
else
Result := GetStoredValue(ARecordIndex, AField);
end;
procedure TcxCustomDataController.GetKeyFields(AList: TList);
begin
end;
function TcxCustomDataController.GetLastRecordIndex: Integer;
var
ARowIndex: Integer;
begin
Result := -1;
ARowIndex := GetRowCount - 1;
if ARowIndex <> -1 then
Result := GetRowInfo(ARowIndex).RecordIndex;
end;
function TcxCustomDataController.GetRecordIndex: Integer;
begin
if Provider.IsDataSource and Provider.IsSyncMode then
Result := Provider.GetRecordIndex
else
Result := DataControllerInfo.FocusedRecordIndex;
end;
function TcxCustomDataController.GetRelationClass: TcxCustomDataRelationClass;
begin
Result := TcxCustomDataRelation;
end;
function TcxCustomDataController.GetSearchClass: TcxDataControllerSearchClass;
begin
Result := TcxDataControllerSearch;
end;
function TcxCustomDataController.GetStoredDisplayText(ARecordIndex: Integer;
AField: TcxCustomDataField): string;
begin
// if not AField.IsInternal and IsGridMode then
if IsDataField(AField) and IsGridMode then
Result := Provider.GetExternalDataDisplayText(ARecordIndex, AField)
else
if AField.ValueDef <> nil then
Result := DataStorage.GetDisplayText(ARecordIndex, AField.ValueDef)
else
Result := '';
end;
function TcxCustomDataController.GetStoredValue(ARecordIndex: Integer;
AField: TcxCustomDataField): Variant;
begin
// if not AField.IsInternal and IsGridMode then
if IsDataField(AField) and IsGridMode then
Result := Provider.GetExternalDataValue(ARecordIndex, AField)
else
if AField.ValueDef <> nil then
Result := DataStorage.GetValue(ARecordIndex, AField.ValueDef)
else
Result := Null;
end;
function TcxCustomDataController.GetSummaryClass: TcxDataSummaryClass;
begin
Result := TcxDataSummary;
end;
function TcxCustomDataController.GetSummaryItemClass: TcxDataSummaryItemClass;
begin
Result := TcxDataSummaryItem;
end;
function TcxCustomDataController.GetSummaryGroupItemLinkClass: TcxDataSummaryGroupItemLinkClass;
begin
Result := TcxDataSummaryGroupItemLink;
end;
function TcxCustomDataController.InternalCheckBookmark(ADeletedRecordIndex: Integer): Boolean;
begin
if (ADeletedRecordIndex <> -1) and (FBookmarkRecordIndex = ADeletedRecordIndex) then
Result := True
else
Result := GetFilteredIndexByRecordIndex(FBookmarkRecordIndex) = -1;
end;
procedure TcxCustomDataController.InternalClearBookmark;
begin
FBookmarkRecordIndex := -1;
end;
procedure TcxCustomDataController.InternalGotoBookmark;
begin
FocusedRecordIndex := FBookmarkRecordIndex;
end;
function TcxCustomDataController.InternalSaveBookmark: Boolean;
var
ARecordIndex: Integer;
begin
Result := False;
ARecordIndex := FocusedRecordIndex;
if FBookmarkRecordIndex <> ARecordIndex then
begin
FBookmarkRecordIndex := ARecordIndex;
Result := True;
end;
end;
function TcxCustomDataController.IsDataBound: Boolean;
begin
Result := IsProviderMode or Provider.IsDataSource;
end;
function TcxCustomDataController.IsDataChangedListenersExist: Boolean;
begin
Result := Assigned(FOnDataChanged) or Assigned(FOnRecordChanged) or
(FDataChangedListeners.Count > 0);
end;
function TcxCustomDataController.IsDataField(AField: TcxCustomDataField): Boolean;
begin
Result := not AField.IsInternal or
(Assigned(AField.FReferenceField) and not AField.FReferenceField.IsInternal);
end;
function TcxCustomDataController.IsDestroying: Boolean;
begin
Result := csDestroying in FOwner.ComponentState;
end;
function TcxCustomDataController.IsFocusedSelectedMode: Boolean;
begin
Result := not MultiSelect{ and FFocusedSelected}; // TODO:?
end;
function TcxCustomDataController.IsImmediatePost: Boolean;
begin
Result := (dcoImmediatePost in Options) or Provider.IsUnboundColumnMode;
end;
function TcxCustomDataController.IsKeyNavigation: Boolean;
begin
if IsProviderMode then
Result := Provider.IsRecordIdSupported
else
Result := False;
end;
function TcxCustomDataController.IsLoading: Boolean;
begin
Result := csLoading in FOwner.ComponentState;
end;
function TcxCustomDataController.IsProviderDataSource: Boolean;
begin
Result := IsProviderMode or FStructureRecreated;
end;
function TcxCustomDataController.IsProviderMode: Boolean;
begin
Result := Provider.CustomDataSource <> nil;
end;
function TcxCustomDataController.IsRecordID: Boolean;
begin
Result := IsUnboundMode and UseRecordID;
end;
function TcxCustomDataController.IsSmartLoad: Boolean;
begin
Result := False;
end;
function TcxCustomDataController.IsSmartRefresh: Boolean;
begin
Result := False;
end;
function TcxCustomDataController.IsUnboundMode: Boolean;
begin
Result := Provider.IsCustomDataSourceSupported and not IsProviderMode; // ?
end;
procedure TcxCustomDataController.LoadStorage;
procedure LoadData;
var
AValueDefReader: TcxValueDefReader;
ARecordIndex: Integer;
begin
with Provider do
begin
if IsGridMode then
begin
DataStorage.BeginLoad;
try
LoadDataBuffer;
finally
DataStorage.EndLoad;
end;
end
else
begin
Freeze;
try
SavePos;
First; // possible to recreate Fields in dbX and Query Reopen
if RecreatedFieldsAfterFirst then
UpdateFields;
DataStorage.BeginLoad;
try
if IsProviderMode then
LoadRecordHandles
else
begin
AValueDefReader := GetValueDefReaderClass.Create;
try
while not IsEOF do
begin
ARecordIndex := DataStorage.AppendRecord;
DataStorage.ReadRecord(ARecordIndex, AValueDefReader);
Self.DoReadRecord(ARecordIndex);
Next;
end;
finally
AValueDefReader.Free;
end;
end;
finally
DataStorage.EndLoad;
end;
FLoadedStorage := Provider.ActiveChanging;
RestorePos;
finally
Unfreeze;
end;
end;
end;
end;
procedure CheckAfterLoad;
begin
FInLoadStorage := True;
try
CheckSelectedCount(-1);
finally
FInLoadStorage := False;
end;
UpdateFocused;
end;
begin
if not (IsSmartLoad and FInSmartLoad) then
DataStorage.Clear(Provider.FDataChangedLocked);
if Provider.IsActive and not IsSmartLoad then
begin
if DetailMode <> dcdmPattern then
LoadData;
CheckAfterLoad;
end;
Change([dccData]);
SyncMasterPos;
end;
function TcxCustomDataController.IsNewItemRecordIndex(ARecordIndex: Integer): Boolean;
begin
Result := UseNewItemRowForEditing and (ARecordIndex = NewItemRecordIndex);
end;
function TcxCustomDataController.LockOnAfterSummary: Boolean;
begin
Result := IsGridMode and FStructureChanged;
end;
procedure TcxCustomDataController.NotifyDataChangedListeners;
var
I: Integer;
E: TNotifyEventItem;
begin
if not IsPattern then
begin
if (FDataChangeInfo.Kind in [dcField, dcRecord]) and
((FDataChangeInfo.RecordIndex <> -1) or IsNewItemRecordIndex(FDataChangeInfo.RecordIndex)) then
begin
if Assigned(FOnRecordChanged) then
FOnRecordChanged(Self, FDataChangeInfo.RecordIndex, FDataChangeInfo.ItemIndex);
end;
if Assigned(FOnDataChanged) then
FOnDataChanged(Self);
end;
if IsGridMode and (Provider.LocateCount <> 0) then Exit; // TODO: !!!
for I := 0 to FDataChangedListeners.Count - 1 do
begin
E := TNotifyEventItem(FDataChangedListeners[I]);
E.Event(Self);
end;
end;
procedure TcxCustomDataController.NotifyDataControllers;
begin
end;
procedure TcxCustomDataController.PrepareField(AField: TcxCustomDataField);
begin
end;
procedure TcxCustomDataController.RemoveNotification(AComponent: TComponent);
begin
Fields.RemoveNotification(AComponent);
end;
procedure TcxCustomDataController.ResetMasterHasChildrenFlag;
var
AMasterDataController: TcxCustomDataController;
begin
if IsDataBound then
begin
AMasterDataController := GetMasterDataController;
if (AMasterDataController <> nil) and (EditState * [dceInsert, dceEdit] = []) then
AMasterDataController.ResetHasChildrenFlag;
end;
end;
procedure TcxCustomDataController.ResetNewItemRowFocused;
begin
if FNewItemRowFocused then
DataControllerInfo.FPrevFocusingInfo.FChangedFlag := True;
FNewItemRowFocused := False;
end;
procedure TcxCustomDataController.RestructData;
begin
//BeginUpdate;
BeginFullUpdate;
try
Provider.ResetEditing;
// if not IsUnboundMode then
if not IsUnboundMode or (FInSetCustomDataSource and (CustomDataSource = nil)) then
DataStorage.Clear(False);
InternalClearBookmark;
CheckSelectedCount(-1); // <- Clear Selection
DataStorage.StoredValuesOnly := IsProviderMode or IsGridMode;
UpdateUseRecordIDState;
if CustomDataSource <> nil then
begin
if FRecordHandlesField = nil then
begin
FRecordHandlesField := AddInternalField;
FRecordHandlesField.ValueTypeClass := TcxIntegerValueType;
end;
end
else
begin
FRecordHandlesField.Free; // FRecordHandlesField <- nil (in RemoveDataField method)
end;
FStructureRecreated := True;
LayoutChanged([lcData]);
finally
EndFullUpdate;
//EndUpdate;
end;
end;
procedure TcxCustomDataController.SetStoredValue(ARecordIndex: Integer;
AField: TcxCustomDataField; const Value: Variant);
begin
if AField.ValueDef <> nil then
DataStorage.SetValue(ARecordIndex, AField.ValueDef, Value);
end;
function TcxCustomDataController.SyncDetailsFocusWithMaster: Boolean;
begin
Result := False;
end;
procedure TcxCustomDataController.SyncMasterPos;
begin
if DetailMode = dcdmClone then
Change([dccSyncMasterPos]);
NotifyDataControllers;
end;
procedure TcxCustomDataController.UpdateFields;
var
I: Integer;
AField: TcxCustomDataField;
APreparedAll: Boolean;
begin
for I := 0 to Fields.Count - 1 do
Fields[I].FPrepared := False;
repeat
Fields.FFieldDestroyed := False;
for I := 0 to Fields.Count - 1 do
begin
AField := Fields[I];
if AField.FPrepared then Continue;
PrepareField(AField);
AField.FPrepared := True;
if Fields.FFieldDestroyed then Break;
end;
// check Finish
APreparedAll := True;
for I := 0 to Fields.Count - 1 do
if not Fields[I].FPrepared then
begin
APreparedAll := False;
Break;
end;
until APreparedAll;
// for I := 0 to Fields.Count - 1 do
// PrepareField(Fields[I]);
end;
procedure TcxCustomDataController.UpdateFocused;
begin
if (Provider.LocateCount = 0) or not DataControllerInfo.FFocusingFlag then
DataControllerInfo.RefreshFocused;
end;
procedure TcxCustomDataController.UpdateRelations(ARelation: TcxCustomDataRelation);
var
ARootDataController: TcxCustomDataController;
begin
if FDestroying then Exit;
if DetailMode = dcdmPattern then
begin
ARootDataController := GetRootDataController;
if FMasterRelation <> nil then
ARootDataController.ResetRelationByItem(FMasterRelation.Item);
end
else
Change([dccData{dccDetail}]);
end;
procedure TcxCustomDataController.UpdateUseRecordIDState;
begin
DataStorage.UseRecordID := IsRecordID;
end;
procedure TcxCustomDataController.UpdateStorage(AUpdateFields: Boolean);
begin
BeginFullUpdate; // !!! see also 22299
try
if IsProviderDataSource then
begin
if AUpdateFields or Provider.IsActiveDataSet then
begin
if LockUpdateFieldsCount <> 0 then
FUpdateFieldsFlag := True
else
begin
if Provider.IsGridModeUpdating and
((EditState * [dceInsert] <> []) or // appending record
FFields.FFieldDestroyed) then // Filter.OnBeforeChange
// do nothing
else
UpdateFields;
end;
end;
if not FStructureChanged then
LoadStorage;
end;
DataControllerInfo.Refresh;
FUpdateItems := False;
FStructureRecreated := False;
finally
EndFullUpdate;
end;
end;
function TcxCustomDataController.UseRecordID: Boolean;
begin
Result := False;
end;
class function TcxCustomDataController.AddListenerLink(ADataController: TcxCustomDataController): TcxDataListenerLink;
begin
Result := TcxDataListenerLink.Create;
Result.Ref := ADataController;
ADataController.FListenerLinks.Add(Result);
end;
class procedure TcxCustomDataController.RemoveListenerLink(ALink: TcxDataListenerLink);
begin
if ALink.Ref <> nil then
ALink.Ref.FListenerLinks.Remove(ALink);
ALink.Free;
end;
procedure TcxCustomDataController.DataStorageClearInternalRecords(Sender: TObject);
begin
Provider.FSavedRecordIndex := 0;
FNewItemRecordIndex := 0;
end;
procedure TcxCustomDataController.RemoveField(ADataField: TcxCustomDataField);
begin
if FRecordHandlesField = ADataField then
FRecordHandlesField := nil;
if Assigned(FRelations) then
Relations.RemoveDataField(ADataField);
if Assigned(FFilters) then
FFilters.RemoveItemByField(ADataField);
if Assigned(FFilter) then
Filter.RemoveItemByField(ADataField);
if Assigned(FSummary) then
Summary.RemoveItemByField(ADataField);
if FIncrementalFilterField = ADataField then
ResetIncrementalFilter;
if FSortingBySummaryDataItemField = ADataField then
SortingBySummaryDataItemIndex := -1;
if FIncrementalSearchField = ADataField then
begin
Search.Cancel;
FIncrementalSearchField := nil;
end;
end;
procedure TcxCustomDataController.FilterChanged;
begin
// !!! filter custom!
FCheckFocusingAfterFilterNeeded := True;
Cancel;
Refresh;
CheckBookmarkValid(-1);
CorrectPrevSelectionChangedInfo;
end;
procedure TcxCustomDataController.SummaryChanged(ARedrawOnly: Boolean);
begin
DataControllerInfo.RefreshSummary(ARedrawOnly);
end;
procedure TcxCustomDataController.ActiveChanged(AActive: Boolean);
begin
FActive := AActive;
UpdateItems(Provider.IsActive);
ResetMasterHasChildrenFlag;
end;
procedure TcxCustomDataController.DataChanged(ADataChange: TcxDataChange;
AItemIndex, ARecordIndex: Integer);
begin
if ((FDataChangeInfo.RecordIndex <> -1) or IsNewItemRecordIndex(FDataChangeInfo.RecordIndex)) and
(FDataChangeInfo.RecordIndex <> ARecordIndex) then
FDataChangeInfo.Kind := dcTotal
else
if (ADataChange = dcField) and (FDataChangeInfo.ItemIndex <> -1) and
(FDataChangeInfo.ItemIndex <> AItemIndex) then
begin
FDataChangeInfo.Kind := dcRecord;
AItemIndex := -1;
end
else
FDataChangeInfo.Kind := ADataChange;
FDataChangeInfo.ItemIndex := AItemIndex;
FDataChangeInfo.RecordIndex := ARecordIndex;
if (ADataChange = dcTotal) then
UpdateStorage(False)
else
begin
if ADataChange in [dcNew, dcDeleted] then
DataControllerInfo.FocusedRecordChanged(False);
DataControllerInfo.Refresh;
end;
ResetMasterHasChildrenFlag;
end;
procedure TcxCustomDataController.DataScrolled(ADistance: Integer);
begin
UpdateFocused;
end;
procedure TcxCustomDataController.LayoutChanged(ADataLayoutChanges: TcxDataLayoutChanges);
begin
UpdateItems(Provider.IsActive);
end;
procedure TcxCustomDataController.CustomDataSourceChanged;
begin
end;
procedure TcxCustomDataController.DoBeforeFocusedRowChange(ARowIndex: Integer);
begin
end;
procedure TcxCustomDataController.DoValueTypeClassChanged(AItemIndex: Integer);
begin
end;
procedure TcxCustomDataController.UpdateControl(AInfo: TcxUpdateControlInfo);
begin
if Assigned(FOnUpdateControl) then FOnUpdateControl(AInfo);
end;
function TcxCustomDataController.GetIncrementalSearchText(ARecordIndex, AItemIndex: Integer): string;
begin
Result := GetDisplayText(ARecordIndex, AItemIndex);
end;
function TcxCustomDataController.GetFilterDisplayText(ARecordIndex, AItemIndex: Integer): string;
begin
Result := GetDisplayText(ARecordIndex, AItemIndex);
end;
// Compare
function TcxCustomDataController.CompareByField(ARecordIndex1, ARecordIndex2: Integer;
AField: TcxCustomDataField; AMode: TcxDataControllerComparisonMode): Integer;
{ function CompareStrEx(const S1, S2: string): Integer;
begin
if S1 = S2 then
Result := 0
else
if S1 < S2 then
Result := -1
else
Result := 1;
end;}
function StringCompare(const V1, V2: Variant): Integer;
begin
if dcoAnsiSort in Options then
begin
if dcoCaseInsensitive in Options then
Result := AnsiCompareText(V1, V2)
else
Result := AnsiCompareStr(V1, V2);
end
else
begin
if dcoCaseInsensitive in Options then
//TODO: Option -> CompareText(V1, V2);
// Result := CompareText(AnsiUpperCase(V1), AnsiUpperCase(V2))
Result := CompareStr(AnsiUpperCase(V1), AnsiUpperCase(V2))
// Result := CompareStrEx(AnsiUpperCase(V1), AnsiUpperCase(V2))
else
Result := VarCompare(V1, V2);
end;
end;
var
V1, V2: Variant;
begin
if (Assigned(FOnCompare) and not AField.IsInternal) or IsConversionNeededForCompare(AField) then
begin
if not Assigned(FOnCompare) and IsProviderMode and
Provider.CustomDataSource.IsNativeCompareFunc and
(ARecordIndex1 >= 0) and (ARecordIndex2 >= 0) then
begin
Result := Provider.NativeCompareFunc(ARecordIndex1, ARecordIndex2, AField);
end
else
begin
V1 := GetComparedValue(ARecordIndex1, AField);
V2 := GetComparedValue(ARecordIndex2, AField);
if Assigned(FOnCompare) then
begin
Result := 0;
FOnCompare(Self, ARecordIndex1, ARecordIndex2, AField.Index, V1, V2, Result);
end
else
begin
if VarIsStr(V1) and VarIsStr(V2) then
Result := StringCompare(VarToStr(V1), VarToStr(V2))
else
Result := VarCompare(V1, V2);
// 2 Result := StringCompare(VarToStr(V1), VarToStr(V2));
{ 1
if IsStringConversionNeeded(AField) then
Result := StringCompare(VarToStr(V1), VarToStr(V2))
else
Result := VarCompare(V1, V2);
}
end;
end;
end
else
Result := NativeCompare(ARecordIndex1, ARecordIndex2, AField);
end;
function TcxCustomDataController.CompareEqualRecords(ARecordIndex1, ARecordIndex2: Integer): Integer;
begin
Result := ARecordIndex1 - ARecordIndex2;
end;
function TcxCustomDataController.CompareRecords(ARecordIndex1, ARecordIndex2: Integer;
ASortInfo: TcxDataSortInfo; AMode: TcxDataControllerComparisonMode = dccmOther): Integer;
begin
Result := CompareByField(ARecordIndex1, ARecordIndex2, ASortInfo.Field, AMode);
if ASortInfo.SortOrder = soDescending then
Result := -Result;
end;
function TcxCustomDataController.FindGroupRecord(ABufferRecordIndex: Integer;
AGroupItemCount: Integer; AIsGroupDataSorted: Boolean): Integer;
function Compare(ARecordListIndex: Integer): Integer;
var
ARecordIndex, I: Integer;
begin
ARecordIndex := DataControllerInfo.GetInternalRecordIndex(ARecordListIndex);
for I := 0 to AGroupItemCount - 1 do
begin
Result := DataControllerInfo.CompareGroupRecords(ARecordIndex, ABufferRecordIndex, I);
if Result <> 0 then
Exit;
end;
Result := 0;
end;
function FindGroupRecordInSortedGroupData: Integer;
var
L, H, I, C: Integer;
begin
Result := -1;
L := 0;
H := DataControllerInfo.GetInternalRecordCount - 1;
if L <= H then
repeat
I := (L + H) div 2;
C := Compare(I);
if C = 0 then
begin
Result := DataControllerInfo.GetInternalRecordIndex(I);
Break;
end
else
if C < 0 then
L := I + 1
else
H := I - 1;
if L > H then
Break;
until False;
end;
function FindGroupRecordInUnsortedGroupData: Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to DataControllerInfo.GetInternalRecordCount - 1 do
if Compare(I) = 0 then
begin
Result := DataControllerInfo.GetInternalRecordIndex(I);
Break;
end;
end;
begin
DataControllerInfo.PrepareSorting;
try
if AIsGroupDataSorted then
Result := FindGroupRecordInSortedGroupData
else
Result := FindGroupRecordInUnsortedGroupData;
finally
DataControllerInfo.UnprepareSorting;
end;
end;
function TcxCustomDataController.FindRecordByFields(ABufferRecordIndex: Integer;
AFields: TList): Integer;
function IsEqual(ARecordIndex: Integer): Boolean;
var
I: Integer;
begin
Result := AFields.Count > 0;
for I := 0 to AFields.Count - 1 do
if CompareByField(ARecordIndex, ABufferRecordIndex, TcxCustomDataField(AFields[I]), dccmOther) <> 0 then
begin
Result := False;
Break;
end;
end;
var
I: Integer;
begin
Result := -1;
DataControllerInfo.PrepareSorting;
try
for I := 0 to RecordCount - 1 do
if IsEqual(I) then
begin
Result := I;
Break;
end;
finally
DataControllerInfo.UnprepareSorting;
end;
end;
function TcxCustomDataController.GetComparedValue(ARecordIndex: Integer;
AField: TcxCustomDataField): Variant;
begin
if (AField.FNeedConversionFlag = 2) then
Result := GetDisplayText(ARecordIndex, AField.Index)
else
Result := GetInternalValue(ARecordIndex, AField);
end;
function TcxCustomDataController.IsConversionNeededForCompare(AField: TcxCustomDataField): Boolean;
begin
if AField.FNeedConversionFlag <> 0 then
begin
Result := AField.FNeedConversionFlag >= 1;
Exit;
end;
Result := AField.NeedConversion or IsStringConversionNeeded(AField) or
IsSortByDisplayTextNeeded(AField);
if not Result and IsProviderMode then
Result := not Provider.CustomDataSource.IsNativeCompare;
end;
function TcxCustomDataController.IsFilterItemSortByDisplayText(AItemIndex: Integer): Boolean;
var
AField: TcxCustomDataField;
begin
Result := GetItemSortByDisplayText(AItemIndex, True);
if not Result and (Options * [dcoAnsiSort] <> []) then
begin
AField := Fields[AItemIndex];
Result := Assigned(AField.ValueDef) and AField.ValueDef.IsNeedConversion;
end;
end;
function TcxCustomDataController.IsStringConversionNeeded(AField: TcxCustomDataField): Boolean;
begin
Result := (Options * [dcoAnsiSort, dcoCaseInsensitive] <> []) and
Assigned(AField.ValueDef) and AField.ValueDef.IsNeedConversion;
end;
function TcxCustomDataController.IsSortByDisplayTextNeeded(AField: TcxCustomDataField): Boolean;
begin
Result := not FInternalFindRecord and not AField.IsInternal and
GetItemSortByDisplayText(AField.Index, dcoSortByDisplayText in Options);
end;
function TcxCustomDataController.NativeCompare(ARecordIndex1, ARecordIndex2: Integer;
AField: TcxCustomDataField): Integer;
begin
if Assigned(AField.ValueDef) then
begin
if not IsProviderMode then
Result := DataStorage.CompareRecords(ARecordIndex1, ARecordIndex2, AField.ValueDef)
else
Result := Provider.NativeCompare(ARecordIndex1, ARecordIndex2, AField);
end
else
Result := 0;
end;
procedure TcxCustomDataController.DeleteFocusedRecord;
begin
if FNearestRecordIndex >= FocusedRecordIndex then
Dec(FNearestRecordIndex);
try
Provider.Delete;
except
FNearestRecordIndex := FocusedRecordIndex;
raise;
end;
end;
procedure TcxCustomDataController.DeleteRecords(AList: TList);
var
I, J, AOldNearestRecordIndex: Integer;
begin
// Prepare List - sort and delete duplicates
AList.Sort(CompareIntegers);
for I := AList.Count - 2 downto 0 do
begin
J := I + 1;
if AList[I] = AList[J] then
AList.Delete(J);
end;
AOldNearestRecordIndex := FNearestRecordIndex;
if AOldNearestRecordIndex <> -1 then
begin
for I := 0 to AList.Count - 1 do
begin
if AOldNearestRecordIndex >= Integer(AList[I]) then
Dec(FNearestRecordIndex);
end;
end;
try
Provider.DeleteRecords(AList);
except
FNearestRecordIndex := FocusedRecordIndex;
raise;
end;
end;
procedure TcxCustomDataController.BeginSmartLoad;
begin
with Provider do
begin
Freeze;
SavePos;
DataStorage.BeginLoad;
end;
end;
procedure TcxCustomDataController.EndSmartLoad;
begin
with Provider do
begin
DataStorage.EndLoad;
RestorePos;
Unfreeze;
end;
UpdateFocused;
DataControllerInfo.Refresh;
end;
function TcxCustomDataController.LoadRecord(AData: Pointer): Integer;
begin
if IsProviderMode then
Result := Provider.AddRecordHandle(AData)
else
begin
Result := DataStorage.AppendRecord;
DataStorage.ReadRecord(Result, TcxValueDefReader(AData));
DoReadRecord(Result);
end;
end;
procedure TcxCustomDataController.BeginReadRecord;
begin
end;
procedure TcxCustomDataController.EndReadRecord;
begin
end;
function TcxCustomDataController.GetSortingBySummaryEngineClass: TcxSortingBySummaryEngineClass;
begin
Result := nil;
end;
function TcxCustomDataController.GetCustomDataSource: TcxCustomDataSource;
begin
Result := Provider.CustomDataSource;
end;
function TcxCustomDataController.GetDetailMode: TcxDataControllerDetailMode;
begin
if FMasterRelation <> nil then
begin
if FMasterRecordIndex <> -1 then
Result := dcdmClone
else
Result := dcdmPattern;
end
else
if FIsPattern then
Result := dcdmPattern
else
Result := dcdmNone;
end;
function TcxCustomDataController.GetFilteredIndexByRecordIndex(Index: Integer): Integer;
begin
Result := DataControllerInfo.GetInternalRecordListIndex(Index);
end;
function TcxCustomDataController.GetFilteredRecordCount: Integer;
begin
Result := DataControllerInfo.GetInternalRecordCount;
end;
function TcxCustomDataController.GetFilteredRecordIndex(Index: Integer): Integer;
begin
Result := DataControllerInfo.GetInternalRecordIndex(Index);
end;
function TcxCustomDataController.GetIsEditing: Boolean;
begin
Result := EditState * [dceInsert, dceEdit] <> [];
Result := Result and (Provider.FEditingRecordIndex <> cxNullEditingRecordIndex);
end;
function TcxCustomDataController.GetIsPattern: Boolean;
begin
Result := DetailMode = dcdmPattern;
end;
function TcxCustomDataController.GetIsRowInfoValid: Boolean;
begin
Result := not DataControllerInfo.FInfoCalculation;
end;
function TcxCustomDataController.GetLockCount: Integer;
begin
Result := DataControllerInfo.LockCount;
end;
function TcxCustomDataController.GetNewItemRowFocused: Boolean;
begin
Result := FNewItemRowFocused and UseNewItemRowForEditing and
(DataControllerInfo.FocusedRecordIndex = -1);
end;
function TcxCustomDataController.GetOptions: TcxDataControllerOptions;
begin
Result := FOptions;
end;
function TcxCustomDataController.GetRelations: TcxCustomDataRelationList;
begin
if FRelations = nil then
FRelations := TcxCustomDataRelationList.Create(Self);
Result := FRelations;
end;
function TcxCustomDataController.GetSortingBySummaryDataItemIndex: Integer;
begin
if FSortingBySummaryDataItemField <> nil then
Result := FSortingBySummaryDataItemField.Index
else
Result := -1;
end;
procedure TcxCustomDataController.SetCustomDataSource(Value: TcxCustomDataSource);
begin
if CustomDataSource <> Value then
begin
Provider.CustomDataSource := Value;
CustomDataSourceChanged;
end;
end;
procedure TcxCustomDataController.SetIsPattern(Value: Boolean);
var
APrevIsPattern: Boolean;
begin
if Value and (FDataChangeRefCount <> 0) and (FMasterRelation <> nil) then
raise EdxException.Create('!');
APrevIsPattern := IsPattern;
FIsPatternSave := Value;
FIsPattern := FIsPatternSave and (FDataChangeRefCount = 0);
if IsPattern <> APrevIsPattern then
if not (csDestroying in FOwner.ComponentState) then
RestructData;
end;
procedure TcxCustomDataController.SetFilter(Value: TcxDataFilterCriteria);
begin
FFilter.Assign(Value);
end;
procedure TcxCustomDataController.SetFocusedRowIndex(Value: Integer);
begin
ChangeFocusedRowIndex(Value);
end;
procedure TcxCustomDataController.SetMultiSelect(Value: Boolean);
begin
if FMultiSelect <> Value then
begin
FMultiSelect := Value;
FFocusedSelected := True; // reset
BeginUpdate;
try
if not Value then
ClearSelection
else
if FocusedRowIndex <> -1 then
ChangeRowSelection(FocusedRowIndex, True);
if [soSelectedRecords, soMultipleSelectedRecords] * Summary.Options <> [] then
Summary.Changed(False);
finally
EndUpdate;
end;
end;
end;
procedure TcxCustomDataController.SetNewItemRowFocused(Value: Boolean);
begin
if FNewItemRowFocused <> Value then
begin
BeginUpdate;
try
if FNewItemRowFocused then
begin
if not (csDestroying in FOwner.ComponentState) then
Post;
end
else
ChangeFocusedRowIndex(-1);
DataControllerInfo.FocusedRecordChanged(True); //?
FNewItemRowFocused := Value; // Notification
finally
EndUpdate;
end;
if (dcoInsertOnNewItemRowFocusing in Options) and
NewItemRowFocused and not Provider.IsInserting then
Insert;
end;
end;
procedure TcxCustomDataController.SetOnFilterRecord(Value: TcxDataFilterRecordEvent);
begin
if @FOnFilterRecord <> @Value then
begin
FOnFilterRecord := Value;
Filter.Changed;
end;
end;
procedure TcxCustomDataController.SetOptions(Value: TcxDataControllerOptions);
begin
if FOptions <> Value then
begin
FOptions := Value;
Refresh;
end;
end;
procedure TcxCustomDataController.SetSummary(Value: TcxDataSummary);
begin
FSummary.Assign(Value);
end;
procedure TcxCustomDataController.SetUseNewItemRowForEditing(Value: Boolean);
begin
if FUseNewItemRowForEditing <> Value then
begin
Cancel;
ClearSelection;
FUseNewItemRowForEditing := Value;
Change([dccData]);
end;
end;
procedure TcxCustomDataController.SetSortingBySummaryDataItemIndex(Value: Integer);
begin
if GetSortingBySummaryDataItemIndex <> Value then
begin
if Value = -1 then
FSortingBySummaryDataItemField := nil
else
begin
CheckItemRange(Value);
FSortingBySummaryDataItemField := Fields[Value];
end;
DoSortingBySummaryChanged;
end;
end;
procedure TcxCustomDataController.Update;
var
AChanges: TcxDataControllerChanges;
begin
AChanges := FChanges;
FChanges := [];
if dccData in AChanges then
DataNotification
else
if AChanges * [dccLayout, dccDetail, dccSummary] <> [] then
LayoutNotification
else
if dccUpdateRecord in AChanges then
UpdateRecordNotification;
if dccFocus in AChanges then
FocusedNotification;
if dccSelection in AChanges then
SelectionNotification;
if dccSearch in AChanges then
SearchNotification;
if dccBookmark in AChanges then
BookmarkNotification;
// events
if dccGrouping in AChanges then
DoGroupingChanged;
if dccSorting in AChanges then
DoSortingChanged;
// master-detail sync
if dccSyncMasterPos in AChanges then
begin
if DetailMode = dcdmClone then
GetPatternDataController.UpdateFocused;
end;
end;
procedure TcxCustomDataController.BeforeGroupingNotification;
begin
NotifyControl(TcxGroupingChangingInfo.Create);
end;
procedure TcxCustomDataController.BookmarkNotification;
begin
NotifyControl(TcxBookmarkChangedInfo.Create);
end;
procedure TcxCustomDataController.DataNotification;
var
ADataChangedInfo: TcxDataChangedInfo;
begin
try
ADataChangedInfo := TcxDataChangedInfo.Create;
if (FDataChangeInfo.Kind = dcField) and (FDataChangeInfo.ItemIndex = -1) then
FDataChangeInfo.Kind := dcRecord;
if (FDataChangeInfo.Kind in [dcField, dcRecord]) and
((FDataChangeInfo.RecordIndex = -1) and not IsNewItemRecordIndex(FDataChangeInfo.RecordIndex)) then
FDataChangeInfo.Kind := dcTotal;
ADataChangedInfo.Kind := FDataChangeInfo.Kind;
ADataChangedInfo.ItemIndex := FDataChangeInfo.ItemIndex;
ADataChangedInfo.RecordIndex := FDataChangeInfo.RecordIndex;
NotifyControl(ADataChangedInfo);
DoDataChanged;
finally
ResetDataChangeInfo;
end;
end;
procedure TcxCustomDataController.DoGroupingChanged;
begin
if [csReading{csLoading}] * FOwner.ComponentState <> [] then Exit; // TODO: collection -> lookup grid // // TODO: method IsLoading/Destroying
if Assigned(FOnGroupingChanged) then FOnGroupingChanged(Self);
end;
procedure TcxCustomDataController.DoSortingBySummaryChanged;
begin
if IsDestroying then
Exit;
FSortingBySummaryChanged := True;
Change([dccSorting{dccGrouping}]);
end;
procedure TcxCustomDataController.DoSortingChanged;
begin
if [csReading{csLoading}] * FOwner.ComponentState <> [] then Exit; // TODO: collection -> lookup grid // // TODO: method IsLoading/Destroying
if FInOnSortingChanged then Exit;
FInOnSortingChanged := True;
try
if Assigned(FOnSortingChanged) then FOnSortingChanged(Self);
finally
FInOnSortingChanged := False;
end;
end;
procedure TcxCustomDataController.FocusedNotification;
begin
if DataControllerInfo.PrevFocusedRowIndex <> DataControllerInfo.FocusedRowIndex then
NotifyControl(TcxFocusedRowChangedInfo.Create(DataControllerInfo.PrevFocusedRowIndex,
DataControllerInfo.FocusedRowIndex));
if (DataControllerInfo.PrevFocusedRecordIndex >= 0) or
(DataControllerInfo.FocusedRecordIndex >= 0) or
DataControllerInfo.FPrevFocusingInfo.FChangedFlag then
begin
// !!!
// if (DataControllerInfo.PrevFocusedRecordIndex <> DataControllerInfo.FocusedRecordIndex) and
// not DataControllerInfo.FPrevFocusingInfo.FChangedFlag then
NotifyControl(TcxFocusedRecordChangedInfo.Create(DataControllerInfo.PrevFocusedRecordIndex,
DataControllerInfo.FocusedRecordIndex, DataControllerInfo.PrevFocusedRowIndex,
DataControllerInfo.FocusedRowIndex, DataControllerInfo.NewItemRowFocusingChanged));
Search.Cancel;
end;
DataControllerInfo.FPrevFocusingInfo.Assign(DataControllerInfo.FFocusingInfo);
end;
procedure TcxCustomDataController.LayoutNotification;
begin
NotifyControl(TcxLayoutChangedInfo.Create);
end;
procedure TcxCustomDataController.UpdateRecordNotification;
begin
NotifyControl(TcxUpdateRecordInfo.Create(FocusedRecordIndex));
Search.Cancel;
end;
procedure TcxCustomDataController.SearchNotification;
begin
NotifyControl(TcxSearchChangedInfo.Create);
end;
procedure TcxCustomDataController.SelectionNotification;
var
AInfo: TcxSelectionChangedInfo;
ARowIndex1, ARowIndex2: Integer;
begin
ARowIndex1 := -1;
ARowIndex2 := -1;
FSelectionChangedInfo.SelectedCount := GetSelectedCount;
if FSelectionChangedInfo.SelectedCount <= 1 then
begin
if FSelectionChangedInfo.SelectedCount = 1 then
FSelectionChangedInfo.RowIndex := GetSelectedRowIndex(0)
else
FSelectionChangedInfo.RowIndex := -1;
if FPrevSelectionChangedInfo.SelectedCount <= 1 then
begin
if FPrevSelectionChangedInfo.SelectedCount = 1 then
ARowIndex1 := FPrevSelectionChangedInfo.RowIndex
else
ARowIndex1 := -1;
ARowIndex2 := FSelectionChangedInfo.RowIndex;
end;
end;
AInfo := TcxSelectionChangedInfo.CreateEx(ARowIndex1, ARowIndex2);
NotifyControl(AInfo);
FPrevSelectionChangedInfo := FSelectionChangedInfo;
end;
procedure TcxCustomDataController.NotifyControl(AUpdateControlInfo: TcxUpdateControlInfo);
begin
FInNotifyControl := True;
try
UpdateControl(AUpdateControlInfo);
finally
AUpdateControlInfo.Free;
FInNotifyControl := False;
end;
end;
procedure TcxCustomDataController.NotifyListenerLinks;
var
I: Integer;
ALink: TcxDataListenerLink;
begin
for I := FListenerLinks.Count - 1 downto 0 do
begin
ALink := TcxDataListenerLink(FListenerLinks[I]);
ALink.Ref := nil;
FListenerLinks.Delete(I);
end;
end;
procedure TcxCustomDataController.ResetDataChangeInfo;
begin
FDataChangeInfo.Kind := dcTotal;
FDataChangeInfo.ItemIndex := -1;
FDataChangeInfo.RecordIndex := -1;
end;
{ TcxCustomDataHelper }
class function TcxCustomDataHelper.GetValueDef(AField: TcxCustomDataField): TcxValueDef;
begin
Result := AField.ValueDef;
end;
class procedure TcxCustomDataHelper.SetTextStored(AField: TcxCustomDataField; ATextStored: Boolean);
begin
AField.FTextStored := ATextStored;
end;
end.