{*******************************************************************} { } { Developer Express Visual Component Library } { ExpressMasterView main components } { } { Copyright (c) 1999-2008 Developer Express Inc. } { ALL RIGHTS RESERVED } { } { The entire contents of this file is protected by U.S. and } { International Copyright Laws. Unauthorized reproduction, } { reverse-engineering, and distribution of all or any portion of } { the code contained in this file is strictly prohibited and may } { result in severe civil and criminal penalties and will be } { prosecuted to the maximum extent possible under the law. } { } { RESTRICTIONS } { } { THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES } { (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE } { SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS } { LICENSED TO DISTRIBUTE THE EXPRESSMASTERVIEW 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 dxMasterView; {$I cxVer.inc} interface uses Windows, Messages, Classes,{$IFDEF DELPHI6} Variants,{$ENDIF} Controls, DB, Graphics, Forms; const dxMVCustomizationFormRowCount = 12; dxMVPreviewLeftIndent = 20; dxMVPreviewMaxLineCount = 3; dxMVPreviewRightIndent = 5; dxMVColumnDefaultMinWidth = 20; dxMVColumnDefaultMaxWidth = 10000; dxMVColumnDefaultMaxRowCount = 100; type PBoolArray = ^TBoolArray; TBoolArray = array[0..MaxInt div SizeOf(Boolean) - 1] of Boolean; PBRUSH = ^HBRUSH; {$IFNDEF DELPHI5} PColor = ^TColor; {$ENDIF} PFont = ^TFont; PIntArray = ^TIntArray; TIntArray = array[0..MaxInt div SizeOf(Integer) - 1] of Integer; TdxMasterViewStyle = class; TdxMasterViewStyleClass = class of TdxMasterViewStyle; TdxMasterViewColumn = class; TdxMasterViewColumnClass = class of TdxMasterViewColumn; TdxMasterViewLayout = class; TdxMasterViewLevel = class; TdxMasterViewNode = class; TdxMasterView = class; TdxMasterViewNodeType = (ntData, ntCaption, ntGroup); TdxMasterViewNodeTypes = set of TdxMasterViewNodeType; TdxMVDesignerRefreshCode = (rcName, rcLevels, rcColumns, rcData, rcStyles, rcEnables); TdxMVDesignerRefreshCodes = set of TdxMVDesignerRefreshCode; TCurDesigner = {$IFDEF DELPHI6}IUnknown{$ELSE}{$IFDEF DELPHI4}IDesigner{$ELSE}TDesigner{$ENDIF}{$ENDIF}; TdxMVDesigner = class public procedure Changed(Control: TdxMasterView; RefreshCodes: TdxMVDesignerRefreshCodes); virtual; abstract; procedure Hide(Control: TdxMasterView); virtual; abstract; function UniqueName(Control: TdxMasterView; const BaseName: string): string; virtual; abstract; procedure Show(Control: TdxMasterView; ADesigner: TCurDesigner); virtual; abstract; end; TdxMasterViewDataLink = class(TDataLink) private FFreezeCount: Integer; FLevel: TdxMasterViewLevel; function GetFrozen: Boolean; protected procedure ActiveChanged; override; procedure DataSetChanged; override; procedure DataSetScrolled(Distance: Integer); override; procedure EditingChanged; override; procedure LayoutChanged; override; procedure RecordChanged(Field: TField); override; public constructor Create(ALevel: TdxMasterViewLevel); function Freeze: Integer; procedure Unfreeze; property FreezeCount: Integer read FFreezeCount; property Frozen: Boolean read GetFrozen; end; TdxMasterViewStyleValue = (svAnotherColor, svColor, svFont); TdxMasterViewStyleValues = set of TdxMasterViewStyleValue; TdxMasterViewStyleConsumeType = (sctHeader, sctContent, sctFooter, sctPreview, sctCaption, sctGroup, sctGroupByBox, sctHighlight, sctInactive); TdxMasterViewStyleConsumeTypes = set of TdxMasterViewStyleConsumeType; PdxMasterViewStyleConsumerRec = ^TdxMasterViewStyleConsumerRec; TdxMasterViewStyleConsumerRec = record Consumer: TComponent; ConsumeTypes: TdxMasterViewStyleConsumeTypes; end; TdxMasterViewStyle = class(TComponent) private FAnotherBrush: HBRUSH; FAnotherColor: TColor; FAssignedValues: TdxMasterViewStyleValues; FBrush: HBRUSH; FColor: TColor; FControl: TdxMasterView; FConsumers: TList; FFont: TFont; function GetConsumerRec(Index: Integer): PdxMasterViewStyleConsumerRec; function GetConsumerCount: Integer; function GetIndex: Integer; procedure SetAnotherColor(Value: TColor); procedure SetAssignedValues(Value: TdxMasterViewStyleValues); procedure SetColor(Value: TColor); procedure SetFont(Value: TFont); procedure SetIndex(Value: Integer); function IsAnotherColorStored: Boolean; function IsColorStored: Boolean; function IsFontStored: Boolean; procedure FontChanged(Sender: TObject); protected procedure SetName(const NewName: TComponentName); override; procedure SetParentComponent(AParent: TComponent); override; procedure AddConsumeType(AConsumer: TComponent; AConsumeType: TdxMasterViewStyleConsumeType); procedure Changed(Values: TdxMasterViewStyleValues); function IndexOfConsumer(Consumer: TComponent): Integer; procedure RemoveConsumeType(AConsumer: TComponent; AConsumeType: TdxMasterViewStyleConsumeType); procedure SysColorChanged; virtual; property ConsumerCount: Integer read GetConsumerCount; property ConsumerRecs[Index: Integer]: PdxMasterViewStyleConsumerRec read GetConsumerRec; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; function GetParentComponent: TComponent; override; function HasParent: Boolean; override; property AnotherBrush: HBRUSH read FAnotherBrush; property Brush: HBRUSH read FBrush; property Control: TdxMasterView read FControl; property Index: Integer read GetIndex write SetIndex; published property AnotherColor: TColor read FAnotherColor write SetAnotherColor stored IsAnotherColorStored; property Color: TColor read FColor write SetColor stored IsColorStored; property Font: TFont read FFont write SetFont stored IsFontStored; property AssignedValues: TdxMasterViewStyleValues read FAssignedValues write SetAssignedValues default []; // must be loaded after all end; TdxMasterViewColumnAssignedValue = (cvAlignment, cvCaption, cvFooterAlignment, cvHeaderAlignment, cvSummaryFieldName, cvWidth); TdxMasterViewColumnAssignedValues = set of TdxMasterViewColumnAssignedValue; TdxMasterViewHPart = (hpOne, hpAll); TdxMasterViewVPart = (vpGroupByBox, vpHeader, vpContent, vpFooter, vpAll, vpAllAndBelow); TdxMasterViewSortOrder = (soNone, soAscending, soDescending); TdxMasterViewSummaryType = (stNone, stSum, stMin, stMax, stCount, stAverage); TdxMasterViewColumnOption = (coGrouping, coHidden, coHorSizing, coMoving, coSorting, coVerSizing, coShowCaption); TdxMasterViewColumnOptions = set of TdxMasterViewColumnOption; TdxMVColumnGetStyleEvent = procedure(Sender: TdxMasterViewColumn; Node: TdxMasterViewNode; var NewStyle: TdxMasterViewStyle) of object; TdxMVSummaryEvent = procedure(Sender: TdxMasterViewColumn; Node: TdxMasterViewNode; DataSet: TDataSet; var Value: Extended) of object; TdxMasterViewColumn = class(TComponent) private FAddInWidth: Integer; FAlignment: TAlignment; FAssignedValues: TdxMasterViewColumnAssignedValues; FCaption: string; FColIndex: Integer; FContentStyle: TdxMasterViewStyle; FField: TField; FFieldName: string; FFooterAlignment: TAlignment; FFooterStyle: TdxMasterViewStyle; FHeaderAlignment: TAlignment; FHeaderGlyph: TBitmap; FHeaderGlyphAlignment: TAlignment; FHeaderStyle: TdxMasterViewStyle; FLeftColumns: TList; FLevel: TdxMasterViewLevel; FLoadedGroupIndex: Integer; FLoadedSortIndex: Integer; FLogicalOffset: Integer; FMaxRowCount: Integer; FMaxWidth: Integer; FMinRowCount: Integer; FMinWidth: Integer; FMultiLine: Boolean; FOptions: TdxMasterViewColumnOptions; FPressed: Boolean; FRightColumns: TList; FRowCount: Integer; FRowIndex: Integer; FLeaveSortOrder: Boolean; FSortOrder: TdxMasterViewSortOrder; FSummaryField: TField; FSummaryFieldName: string; FSummaryFormat: string; FSummaryType: TdxMasterViewSummaryType; FVisible: Boolean; FVisibleWidth: Integer; FWidth: Integer; FOnAfterCalcSummary: TdxMVSummaryEvent; FOnBeforeCalcSummary: TdxMVSummaryEvent; FOnCalcSummary: TdxMVSummaryEvent; FOnGetContentStyle: TdxMVColumnGetStyleEvent; FOnGetFooterStyle: TdxMVColumnGetStyleEvent; FOnGetHeaderStyle: TdxMVColumnGetStyleEvent; function GetAlignment: TAlignment; function GetCaption: string; function GetColIndex: Integer; function GetContentAnotherBrush: HBRUSH; function GetContentAnotherColor: TColor; function GetContentBrush: HBRUSH; function GetContentColor: TColor; function GetContentFont: TFont; function GetControl: TdxMasterView; function GetFooterAlignment: TAlignment; function GetFooterBrush: HBRUSH; function GetFooterColor: TColor; function GetFooterFont: TFont; function GetGroupIndex: Integer; function GetHeaderAlignment: TAlignment; function GetHeaderBrush: HBRUSH; function GetHeaderColor: TColor; function GetHeaderFont: TFont; function GetHidden: Boolean; function GetIndex: Integer; function GetIsDestroying: Boolean; function GetIsLoading: Boolean; function GetLastInRow: Boolean; function GetLogicalOffset: Integer; // function GetMaxLogicalOffset: Integer; function GetMinLogicalOffset: Integer; function GetNonScaledLogicalOffset: Integer; // function GetNonScaledOffset: Integer; function GetNonScaledWidth: Integer; function GetOffset: Integer; function GetRowIndex: Integer; function GetSortIndex: Integer; function GetSummaryField: TField; function GetSummaryFieldName: string; function GetSummaryIndex: Integer; function GetVisibleIndex: Integer; function GetVisibleMaxWidth: Integer; function GetVisibleMinWidth: Integer; function GetVisibleWidth: Integer; procedure SetAlignment(Value: TAlignment); procedure SetAssignedValues(Value: TdxMasterViewColumnAssignedValues); procedure SetCaption(Value: string); procedure SetColIndex(Value: Integer); procedure SetContentStyle(Value: TdxMasterViewStyle); procedure SetField(Value: TField); procedure SetFieldInternally(Value: TField); procedure SetFieldName(Value: string); procedure SetFooterAlignment(Value: TAlignment); procedure SetFooterStyle(Value: TdxMasterViewStyle); procedure SetGroupIndex(Value: Integer); procedure SetHeaderAlignment(Value: TAlignment); procedure SetHeaderGlyph(Value: TBitmap); procedure SetHeaderGlyphAlignment(Value: TAlignment); procedure SetHeaderStyle(Value: TdxMasterViewStyle); procedure SetIndex(Value: Integer); procedure SetInternalVisibleWidth(Value: Integer); procedure SetMaxRowCount(Value: Integer); procedure SetMaxWidth(Value: Integer); procedure SetMinRowCount(Value: Integer); procedure SetMinWidth(Value: Integer); procedure SetMultiLine(Value: Boolean); procedure SetOptions(Value: TdxMasterViewColumnOptions); procedure SetPressed(Value: Boolean); procedure SetRowCount(Value: Integer); procedure SetRowIndex(Value: Integer); procedure SetSortIndex(Value: Integer); procedure SetSortOrder(Value: TdxMasterViewSortOrder); procedure SetSummaryField(Value: TField); procedure SetSummaryFieldInternally(Value: TField); procedure SetSummaryFieldName(Value: string); procedure SetSummaryFormat(Value: string); procedure SetSummaryType(Value: TdxMasterViewSummaryType); procedure SetVisible(Value: Boolean); procedure SetVisibleWidth(Value: Integer); procedure SetWidth(Value: Integer); procedure CheckRowCount(var Value: Integer); procedure CheckVisibleWidthValue(var Value: Integer); procedure CheckWidthValue(var Value: Integer); function FindField: TField; function FindSummaryField: TField; procedure HeaderGlyphChanged(Sender: TObject); function IsAlignmentStored: Boolean; function IsCaptionStored: Boolean; function IsFooterAlignmentStored: Boolean; function IsHeaderAlignmentStored: Boolean; function IsSummaryFieldNameStored: Boolean; function IsWidthLinked: Boolean; function IsWidthStored: Boolean; procedure ReadLeaveSortOrder(Reader: TReader); procedure WriteLeaveSortOrder(Writer: TWriter); protected procedure DefineProperties(Filer: TFiler); override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure SetName(const NewName: TComponentName); override; procedure SetParentComponent(AParent: TComponent); override; function CanGrouping(Index: Integer): Boolean; function CanHiding: Boolean; function CanMoving: Boolean; function CanHorSizing: Boolean; function CanSorting: Boolean; function CanVerSizing: Boolean; procedure ColumnChanged(HardRefresh: Boolean; HPart: TdxMasterViewHPart; VPart: TdxMasterViewVPart); procedure ContentStyleChanged(Values: TdxMasterViewStyleValues); function DefaultAlignment: TAlignment; function DefaultCaption: string; function DefaultHeaderAlignment: TAlignment; function DefaultWidth: Integer; procedure DoAfterCalcSummary(Node: TdxMasterViewNode; DataSet: TDataSet; var Value: Extended); virtual; procedure DoBeforeCalcSummary(Node: TdxMasterViewNode; DataSet: TDataSet; var Value: Extended); virtual; procedure DoCalcSummary(Node: TdxMasterViewNode; DataSet: TDataSet; var Value: Extended); virtual; procedure DrawSortMark(DC: HDC; var ARect: TRect); procedure DrawHeader(DC: HDC; var ARect: TRect; Node: TdxMasterViewNode); virtual; procedure DrawContent(DC: HDC; var ARect: TRect; Node: TdxMasterViewNode); virtual; procedure DrawFooter(DC: HDC; var ARect: TRect; Node: TdxMasterViewNode); virtual; procedure FooterStyleChanged(Values: TdxMasterViewStyleValues); function GetFooterText(Node: TdxMasterViewNode): string; function GetGroupByBoxBounds(Node: TdxMasterViewNode): TRect; function GetGroupDisplayText(Node: TdxMasterViewNode): string; virtual; function GetHeaderBestFitWidth(DC: HDC; CheckSortOrderMark: Boolean): Integer; function GetHeaderBounds(Node: TdxMasterViewNode): TRect; function GetHeaderHeight: Integer; function GetContentBounds(Node: TdxMasterViewNode): TRect; function GetFooterBounds(Node: TdxMasterViewNode): TRect; procedure HeaderStyleChanged(Values: TdxMasterViewStyleValues); property AddInWidth: Integer read FAddInWidth; property Hidden: Boolean read GetHidden; property InternalVisibleWidth: Integer read FVisibleWidth write SetInternalVisibleWidth; property IsDestroying: Boolean read GetIsDestroying; property IsLoading: Boolean read GetIsLoading; property LastInRow: Boolean read GetLastInRow; property LogicalOffset: Integer read GetLogicalOffset; // property MaxLogicalOffset: Integer read GetMaxLogicalOffset; property MinLogicalOffset: Integer read GetMinLogicalOffset; property NonScaledLogicalOffset: Integer read GetNonScaledLogicalOffset; // property NonScaledOffset: Integer read GetNonScaledOffset; property NonScaledWidth: Integer read GetNonScaledWidth; property Offset: Integer read GetOffset; property Pressed: Boolean read FPressed write SetPressed; property SummaryIndex: Integer read GetSummaryIndex; property VisibleMaxWidth: Integer read GetVisibleMaxWidth; property VisibleMinWidth: Integer read GetVisibleMinWidth; property HeaderBrush: HBRUSH read GetHeaderBrush; property HeaderColor: TColor read GetHeaderColor; property HeaderFont: TFont read GetHeaderFont; property ContentAnotherBrush: HBRUSH read GetContentAnotherBrush; property ContentAnotherColor: TColor read GetContentAnotherColor; property ContentBrush: HBRUSH read GetContentBrush; property ContentColor: TColor read GetContentColor; property ContentFont: TFont read GetContentFont; property FooterBrush: HBRUSH read GetFooterBrush; property FooterColor: TColor read GetFooterColor; property FooterFont: TFont read GetFooterFont; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; procedure ChangeWidth(Value: Integer; AdjustLinkedColumns: Boolean); function GetParentComponent: TComponent; override; function HasParent: Boolean; override; procedure ApplyBestFit; function GetBestFitWidth: Integer; function GetFooterValue(Node: TdxMasterViewNode): Extended; property AssignedValues: TdxMasterViewColumnAssignedValues read FAssignedValues write SetAssignedValues; property Control: TdxMasterView read GetControl; property Field: TField read FField write SetField; property Index: Integer read GetIndex write SetIndex; property Level: TdxMasterViewLevel read FLevel; property SummaryField: TField read GetSummaryField write SetSummaryField; property VisibleIndex: Integer read GetVisibleIndex; property VisibleWidth: Integer read GetVisibleWidth write SetVisibleWidth; published property Alignment: TAlignment read GetAlignment write SetAlignment stored IsAlignmentStored; property Caption: string read GetCaption write SetCaption stored IsCaptionStored; property ContentStyle: TdxMasterViewStyle read FContentStyle write SetContentStyle; property FieldName: string read FFieldName write SetFieldName; property FooterAlignment: TAlignment read GetFooterAlignment write SetFooterAlignment stored IsFooterAlignmentStored; property FooterStyle: TdxMasterViewStyle read FFooterStyle write SetFooterStyle; property GroupIndex: Integer read GetGroupIndex write SetGroupIndex default -1; property HeaderAlignment: TAlignment read GetHeaderAlignment write SetHeaderAlignment stored IsHeaderAlignmentStored; property HeaderGlyph: TBitmap read FHeaderGlyph write SetHeaderGlyph; property HeaderGlyphAlignment: TAlignment read FHeaderGlyphAlignment write SetHeaderGlyphAlignment default taLeftJustify; property HeaderStyle: TdxMasterViewStyle read FHeaderStyle write SetHeaderStyle; property MaxRowCount: Integer read FMaxRowCount write SetMaxRowCount default dxMVColumnDefaultMaxRowCount; property MaxWidth: Integer read FMaxWidth write SetMaxWidth default dxMVColumnDefaultMaxWidth; property MinRowCount: Integer read FMinRowCount write SetMinRowCount default 1; property MinWidth: Integer read FMinWidth write SetMinWidth default dxMVColumnDefaultMinWidth; property MultiLine: Boolean read FMultiLine write SetMultiLine default False; property Options: TdxMasterViewColumnOptions read FOptions write SetOptions default [coGrouping, coHorSizing, coMoving, coSorting, coVerSizing, coShowCaption]; property RowIndex: Integer read GetRowIndex write SetRowIndex; // must be before RowCount property ColIndex: Integer read GetColIndex write SetColIndex; property RowCount: Integer read FRowCount write SetRowCount default 1; property SortIndex: Integer read GetSortIndex write SetSortIndex default -1; property SortOrder: TdxMasterViewSortOrder read FSortOrder write SetSortOrder default soNone; property SummaryFieldName: string read GetSummaryFieldName write SetSummaryFieldName stored IsSummaryFieldNameStored; property SummaryFormat: string read FSummaryFormat write SetSummaryFormat; property SummaryType: TdxMasterViewSummaryType read FSummaryType write SetSummaryType default stNone; property Visible: Boolean read FVisible write SetVisible default True; property Width: Integer read FWidth write SetWidth stored IsWidthStored; property OnAfterCalcSummary: TdxMVSummaryEvent read FOnAfterCalcSummary write FOnAfterCalcSummary; property OnBeforeCalcSummary: TdxMVSummaryEvent read FOnBeforeCalcSummary write FOnBeforeCalcSummary; property OnCalcSummary: TdxMVSummaryEvent read FOnCalcSummary write FOnCalcSummary; property OnGetContentStyle: TdxMVColumnGetStyleEvent read FOnGetContentStyle write FOnGetContentStyle; property OnGetFooterStyle: TdxMVColumnGetStyleEvent read FOnGetFooterStyle write FOnGetFooterStyle; property OnGetHeaderStyle: TdxMVColumnGetStyleEvent read FOnGetHeaderStyle write FOnGetHeaderStyle; end; TdxMasterViewLayout = class private FContentNonScaledWidths: PIntArray; FContentWidths: PIntArray; FHeaderWidthAssigned: PBoolArray; FHeaderWidths: PIntArray; FHorizontal: Boolean; FLevel: TdxMasterViewLevel; FLines: TList; FRowCount: Integer; FUpdateLockCount: Integer; FVisibleWidth: Integer; function GetColLogicalOffset(ACol: Integer): Integer; function GetColOffset(ACol: Integer): Integer; function GetColumn(ARow, ACol: Integer): TdxMasterViewColumn; function GetColWidth(ACol: Integer): Integer; function GetContentDynamicMaxWidth(ACol: Integer): Integer; function GetContentDynamicMinWidth(ACol: Integer): Integer; function GetContentMaxWidth(ACol: Integer): Integer; function GetContentMinWidth(ACol: Integer): Integer; function GetContentNonScaledWidth(ACol: Integer): Integer; function GetContentWidth(ACol: Integer): Integer; function GetCount: Integer; function GetHeaderDynamicMaxWidth(ACol: Integer): Integer; function GetHeaderDynamicMinWidth(ACol: Integer): Integer; function GetHeaderMaxWidth(ACol: Integer): Integer; function GetHeaderMinWidth(ACol: Integer): Integer; function GetHeaderWidth(ACol: Integer): Integer; function GetHeaderWidthAssigned(ACol: Integer): Boolean; function GetItem(Index: Integer): TList; // function GetMaxWidth: Integer; function GetMinWidth: Integer; function GetNonScaledWidth: Integer; function GetVisibleWidth: Integer; procedure SetContentInternalVisibleWidth(ACol, Value: Integer); procedure SetContentWidth(ACol, Value: Integer); procedure SetCount(Value: Integer); procedure SetHeaderWidth(ACol, Value: Integer); procedure SetHeaderWidthAssigned(ACol: Integer; Value: Boolean); protected function AreNeighbours(Column1, Column2: TdxMasterViewColumn; ExactResult: Boolean): Boolean; procedure CalcAddInWidths; procedure CalcColWidths; procedure CalcLogicalOffsets(Mode: Byte); procedure CalcNeighbours; procedure CalcOptimizedValues; procedure CalcVisibleWidth; procedure CheckColumnsWidths(FixedColumn: TdxMasterViewColumn); procedure CheckCount; procedure Clear; procedure ClearOptimizedValues; function ColFromX(X: Integer): Integer; procedure CreateColArrays; procedure Delete(Index: Integer); procedure DestroyColArrays; function HasLeftNeighbours(AColumn: TdxMasterViewColumn; AList: TList; ExactResult: Boolean): Boolean; // function HasLinkedColumns(AColumn: TdxMasterViewColumn; AList: TList): Boolean; function HasRightNeighbours(AColumn: TdxMasterViewColumn; AList: TList; ExactResult: Boolean): Boolean; procedure HeaderWidthChanged; procedure Insert(Index, ACount: Integer); procedure InsertAtPos(ARow, APos: Integer; AColumn: TdxMasterViewColumn); procedure InsertColumn(ARow, ACol: Integer; AColumn: TdxMasterViewColumn); procedure InsertColumnAtPos(ARow, APos: Integer; AColumn: TdxMasterViewColumn); procedure InternalRemoveFreeSpace; procedure RemoveColumn(AColumn: TdxMasterViewColumn); procedure RetrieveLastColumns(AList: TList); function GetNearestCol(ARow, AOffset: Integer): Integer; function GetNearestRow(ACol, ARow: Integer): Integer; procedure RequireCount(ACount: Integer); procedure ViewModeChanged; property ContentInternalVisibleWidths[ACol: Integer]: Integer write SetContentInternalVisibleWidth; public constructor Create(ALevel: TdxMasterViewLevel); destructor Destroy; override; procedure BeginUpdate; procedure CancelUpdate; procedure ChangeCol(AColumn: TdxMasterViewColumn; ACol: Integer); procedure ChangeHeaderWidth(ACol: Integer; Value: Integer; FixCol: Boolean); procedure ChangeRow(AColumn: TdxMasterViewColumn; ARow: Integer; FixedPos: Boolean); procedure ChangeRowAndRowCount(AColumn: TdxMasterViewColumn; ARow, ARowCount: Integer; FixedPos: Boolean); procedure ChangeRowCount(AColumn: TdxMasterViewColumn; ARowCount: Integer); procedure EndUpdate; function GetBestFitWidth(ACol: Integer): Integer; function IndexOf(AColumn: TdxMasterViewColumn; ALine: Integer): Integer; function IsColumnFirst(AColumn: TdxMasterViewColumn): Boolean; function IsColumnLast(AColumn: TdxMasterViewColumn): Boolean; function OneOnLine(AColumn: TdxMasterViewColumn): Boolean; procedure RemoveFreeSpace; property ColLogicalOffsets[ACol: Integer]: Integer read GetColLogicalOffset; property ColOffsets[ACol: Integer]: Integer read GetColOffset; property Columns[ARow, ACol: Integer]: TdxMasterViewColumn read GetColumn; property ColWidths[ACol: Integer]: Integer read GetColWidth; property ContentDynamicMaxWidths[ACol: Integer]: Integer read GetContentDynamicMaxWidth; property ContentDynamicMinWidths[ACol: Integer]: Integer read GetContentDynamicMinWidth; property ContentMaxWidths[ACol: Integer]: Integer read GetContentMaxWidth; property ContentMinWidths[ACol: Integer]: Integer read GetContentMinWidth; property ContentNonScaledWidths[ACol: Integer]: Integer read GetContentNonScaledWidth; property ContentWidths[ACol: Integer]: Integer read GetContentWidth write SetContentWidth; property Count: Integer read GetCount write SetCount; property HeaderDynamicMaxWidths[ACol: Integer]: Integer read GetHeaderDynamicMaxWidth; property HeaderDynamicMinWidths[ACol: Integer]: Integer read GetHeaderDynamicMinWidth; property HeaderMaxWidths[ACol: Integer]: Integer read GetHeaderMaxWidth; property HeaderMinWidths[ACol: Integer]: Integer read GetHeaderMinWidth; property HeaderWidthAssigned[ACol: Integer]: Boolean read GetHeaderWidthAssigned write SetHeaderWidthAssigned; property HeaderWidths[ACol: Integer]: Integer read GetHeaderWidth write SetHeaderWidth; property Horizontal: Boolean read FHorizontal; property Items[Index: Integer]: TList read GetItem; default; // property MaxWidth: Integer read GetMaxWidth; property MinWidth: Integer read GetMinWidth; property NonScaledWidth: Integer read GetNonScaledWidth; property RowCount: Integer read FRowCount; property VisibleWidth: Integer read FVisibleWidth; end; TdxMasterViewLevelOptionBehavior = (lobDblClkExpanding); TdxMasterViewLevelOptionsBehavior = set of TdxMasterViewLevelOptionBehavior; TdxMasterViewLevelOptionCustomize = (locColumnMoving, locColumnHiding, locColumnHorSizing, locColumnVerSizing, locColumnSorting, locColumnGrouping, locHideColumnOnGrouping, locShowColumnOnUngrouping); TdxMasterViewLevelOptionsCustomize = set of TdxMasterViewLevelOptionCustomize; TdxMasterViewLevelOptionCustomizeBox = (loxCaption, loxColumns, loxFooter, loxGrid, loxGridWithPreview, loxGroupByBox, loxHeader, loxHeaderOptions, loxOccupyRestSpace, loxPreview, loxViewMode); TdxMasterViewLevelOptionsCustomizeBox = set of TdxMasterViewLevelOptionCustomizeBox; TdxMasterViewLevelOptionDB = (lodAllowDelete, lodAllowInsert, lodConfirmDelete, lodDeleteOnlyChildless, lodDontFilterRecords, lodSmartLoad, lodSmartRefresh, lodSmartReload); TdxMasterViewLevelOptionsDB = set of TdxMasterViewLevelOptionDB; TdxMasterViewLevelOptionHeader = (lohForFirstNode, lohForFirstVisibleNode, lohAfterExpandedNode); TdxMasterViewLevelOptionsHeader = set of TdxMasterViewLevelOptionHeader; TdxMasterViewLevelOptionView = (lovCaption, lovFooter, lovGrid, lovGridWithPreview, lovGroupByBox, lovHeader, lovNoButtonsWhenNoChildren, lovOccupyRestSpace, lovPreview); TdxMasterViewLevelOptionsView = set of TdxMasterViewLevelOptionView; PdxMVLevels = ^TdxMVLevels; TdxMVLevels = array[0..MaxInt div SizeOf(TdxMasterViewLevel) - 1] of {$IFDEF CBUILDER3}TComponent{$ELSE}TdxMasterViewLevel{$ENDIF}; TdxMasterViewViewMode = (vmHorizontal, vmVertical); TdxMVChangeGroupingEvent = procedure(Sender: TdxMasterViewLevel; Column: TdxMasterViewColumn; GroupIndex: Integer; var Allow: Boolean) of object; TdxMVColumnEvent = procedure(Sender: TdxMasterViewLevel; Column: TdxMasterViewColumn) of object; TdxMVColumnAllowEvent = procedure(Sender: TdxMasterViewLevel; Column: TdxMasterViewColumn; var Allow: Boolean) of object; TdxMVFilterRecordEvent = procedure(Sender: TdxMasterViewLevel; const AID, AKeyValue: Variant; var Accept: Boolean) of object; TdxMVGetFooterCellTextEvent = procedure(Sender: TdxMasterViewLevel; Node: TdxMasterViewNode; Column: TdxMasterViewColumn; var Text: string) of object; TdxMVGetPreviewLineCountEvent = procedure(Sender: TdxMasterViewLevel; Node: TdxMasterViewNode; var LineCount: Integer) of object; TdxMVGetPreviewTextEvent = procedure(Sender: TdxMasterViewLevel; Node: TdxMasterViewNode; var Text: string) of object; TdxMVLevelGetStyleEvent = procedure(Sender: TdxMasterViewLevel; Node: TdxMasterViewNode; Column: TdxMasterViewColumn; var NewStyle: TdxMasterViewStyle) of object; TdxMVNodeEvent = procedure(Sender: TdxMasterViewLevel; Node: TdxMasterViewNode) of object; TdxMVNodeAllowEvent = procedure(Sender: TdxMasterViewLevel; Node: TdxMasterViewNode; var Allow: Boolean) of object; TdxMasterViewLevel = class(TComponent) private FAbsoluteIndex: Integer; FAssignWidthsLockCount: Integer; FBeforeLoadActiveRecord: Integer; FBeforeLoadBOF: Boolean; FBeforeLoadBookmark: Pointer; FBeforeLoadEOF: Boolean; FCanUseSmartReload: Boolean; FCaption: string; FCaptionStyle: TdxMasterViewStyle; FColumns: TList; FContentStyle: TdxMasterViewStyle; FControl: TdxMasterView; FCursorBeforeLoading: TCursor; FDataChanging: Boolean; FDataChangingLockCount: Integer; FDataLink: TdxMasterViewDataLink; FDeleteConfirmCaptionText: string; FDeleteConfirmText: string; FDetailKey: string; FDetailKeyFields: TList; FEditingNode: TdxMasterViewNode; FFirstVisibleNode: TdxMasterViewNode; FFirstVisibleNodeWithData: TdxMasterViewNode; FFooterStyle: TdxMasterViewStyle; FGridLinesBrush: HBRUSH; FGridLinesPen: HPEN; FGridLinesColor: TColor; FGroupByBoxStyle: TdxMasterViewStyle; FGroupColumns: TList; FGroupStyle: TdxMasterViewStyle; FHeaderStyle: TdxMasterViewStyle; FHorizontal: Boolean; FID: string; FIDFields: TList; FIndex: Integer; FIsInsertMode: Boolean; FItems: TList; FLayout: TdxMasterViewLayout; FLevelSeparatorBrush: HBRUSH; FLevelSeparatorColor: TColor; FLevelSeparatorWidth: Integer; FLoadingLockCount: Integer; FLoadLockCount: Integer; FMasterKey: string; FMasterKeyFields: TList; FMultipleDeleteConfirmText: string; FOptionsBehavior: TdxMasterViewLevelOptionsBehavior; FOptionsCustomize: TdxMasterViewLevelOptionsCustomize; FOptionsCustomizeBox: TdxMasterViewLevelOptionsCustomizeBox; FOptionsDB: TdxMasterViewLevelOptionsDB; FOptionsHeader: TdxMasterViewLevelOptionsHeader; FOptionsView: TdxMasterViewLevelOptionsView; FParent: TdxMasterViewLevel; FParentCount: Integer; FParents: PdxMVLevels; FPreviewDC: HDC; FPreviewField: TField; FPreviewFieldName: string; FPreviewLeftIndent: Integer; FPreviewLineCount: Integer; FPreviewMaxLength: Integer; FPreviewMaxLineCount: Integer; FPreviewPrevFont: HFONT; FPreviewRightIndent: Integer; FPreviewStyle: TdxMasterViewStyle; FRowSeparatorBrush: HBRUSH; FRowSeparatorColor: TColor; FRowSeparatorWidth: Integer; FSortedColumns: TList; FSortingLockCount: Integer; FSummaryColumns: TList; FViewMode: TdxMasterViewViewMode; FVisible: Boolean; FVisibleColumns: TList; FHeaderHeight, FContentHeight, FFooterHeight, FHeaderRealHeight, FContentRealHeight, FCaptionHeight, FGroupHeight, FGroupByBoxTextHeight: Integer; FLineWidth, FExtLineWidth: Integer; // for GetParentNode function GPNGroupValue, GPNFindingValue, GPNCurValue: Variant; GPNDisplayText, GPNS1, GPNS2: string; GPNCanUseAnsiCompareStrOnGrouping, GPNCanUseCaseInsensitiveGrouping, GPNFindingValueIsNull, GPNCurValueIsNull, GPNReverseOrder: Boolean; GPNParentNode: TdxMasterViewNode; FOnChangeGrouping: TdxMVChangeGroupingEvent; FOnCollapsed: TdxMVNodeEvent; FOnCollapsing: TdxMVNodeAllowEvent; FOnColumnSorted: TdxMVColumnEvent; FOnColumnSorting: TdxMVColumnAllowEvent; FOnDeleteNode: TdxMVNodeEvent; FOnExpanded: TdxMVNodeEvent; FOnExpanding: TdxMVNodeAllowEvent; FOnFilterRecord: TdxMVFilterRecordEvent; FOnGetCaptionStyle: TdxMVLevelGetStyleEvent; FOnGetContentStyle: TdxMVLevelGetStyleEvent; FOnGetFooterCellText: TdxMVGetFooterCellTextEvent; FOnGetFooterStyle: TdxMVLevelGetStyleEvent; FOnGetGroupByBoxStyle: TdxMVLevelGetStyleEvent; FOnGetGroupStyle: TdxMVLevelGetStyleEvent; FOnGetHeaderStyle: TdxMVLevelGetStyleEvent; FOnGetPreviewLineCount: TdxMVGetPreviewLineCountEvent; FOnGetPreviewStyle: TdxMVLevelGetStyleEvent; FOnGetPreviewText: TdxMVGetPreviewTextEvent; FOnHideColumn: TdxMVColumnEvent; FOnShowColumn: TdxMVColumnEvent; FOnWidthChanged: TNotifyEvent; function GetActive: Boolean; function GetCaptionBrush: HBRUSH; function GetCaptionColor: TColor; function GetCaptionFont: TFont; function GetColumn(Index: Integer): TdxMasterViewColumn; function GetColumnCount: Integer; function GetContentAnotherBrush: HBRUSH; function GetContentAnotherColor: TColor; function GetContentBrush: HBRUSH; function GetContentColor: TColor; function GetContentFont: TFont; function GetCount: Integer; function GetDataSet: TDataSet; function GetDataSource: TDataSource; function GetDetailKeyField(Index: Integer): TField; function GetDetailKeyFieldCount: Integer; function GetDontFilterRecords: Boolean; function GetFooterBrush: HBRUSH; function GetFooterColor: TColor; function GetFooterFont: TFont; function GetGroupBrush: HBRUSH; function GetGroupByBoxBrush: HBRUSH; function GetGroupByBoxColor: TColor; function GetGroupByBoxFont: TFont; function GetGroupByBoxFontColor: TColor; function GetGroupByBoxHeight: Integer; function GetGroupColor: TColor; function GetGroupColumn(Index: Integer): TdxMasterViewColumn; function GetGroupColumnCount: Integer; function GetGroupFont: TFont; function GetHasChildren: Boolean; function GetHeaderBrush: HBRUSH; function GetHeaderColor: TColor; function GetHeaderFont: TFont; function GetHeaderRestSpaceBrush: HBRUSH; function GetHeaderRestSpaceColor: TColor; function GetIDField(Index: Integer): TField; function GetIDFieldCount: Integer; function GetIndent: Integer; function GetIsDestroying: Boolean; function GetIsFirst: Boolean; function GetIsLast: Boolean; function GetIsLoading: Boolean; function GetIsTop: Boolean; function GetItem(Index: Integer): TdxMasterViewLevel; function GetMasterKeyField(Index: Integer): TField; function GetMasterKeyFieldCount: Integer; function GetNonScaledWidth: Integer; function GetOccupyRestSpace: Boolean; {$IFDEF CBUILDER3} function GetParent(Index: Integer): TdxMasterViewLevel; {$ENDIF} function GetPreviewAnotherBrush: HBRUSH; function GetPreviewAnotherColor: TColor; function GetPreviewBrush: HBRUSH; function GetPreviewColor: TColor; function GetPreviewColorAssigned: Boolean; function GetPreviewFont: TFont; function GetPreviewFontAssigned: Boolean; function GetPreviewFontColor: TColor; function GetRowCount: Integer; function GetShowCaption: Boolean; function GetShowFooter: Boolean; function GetShowGrid: Boolean; function GetShowGridWithPreview: Boolean; function GetShowGroupByBox: Boolean; function GetShowHeader: Boolean; function GetShowPreview: Boolean; function GetSmartLoad: Boolean; function GetSmartReload: Boolean; function GetSortedColumn(Index: Integer): TdxMasterViewColumn; function GetSortedColumnCount: Integer; function GetVisibleColumn(Index: Integer): TdxMasterViewColumn; function GetVisibleColumnCount: Integer; function GetVisibleRowCount: Integer; function GetVisibleWidth: Integer; procedure SetCaption(const Value: string); procedure SetCaptionStyle(Value: TdxMasterViewStyle); procedure SetColumn(Index: Integer; Value: TdxMasterViewColumn); procedure SetContentStyle(Value: TdxMasterViewStyle); procedure SetDataSource(Value: TDataSource); procedure SetDetailKey(const Value: string); procedure SetFooterStyle(Value: TdxMasterViewStyle); procedure SetGridLinesColor(Value: TColor); procedure SetGroupByBoxStyle(Value: TdxMasterViewStyle); procedure SetGroupStyle(Value: TdxMasterViewStyle); procedure SetHeaderStyle(Value: TdxMasterViewStyle); procedure SetID(Value: string); procedure SetMasterKey(const Value: string); procedure SetOptionsDB(Value: TdxMasterViewLevelOptionsDB); procedure SetOptionsHeader(Value: TdxMasterViewLevelOptionsHeader); procedure SetOptionsView(Value: TdxMasterViewLevelOptionsView); procedure SetPreviewField(Value: TField); procedure SetPreviewFieldInternally(Value: TField); procedure SetPreviewFieldName(Value: string); procedure SetPreviewLeftIndent(Value: Integer); procedure SetPreviewLineCount(Value: Integer); procedure SetPreviewMaxLength(Value: Integer); procedure SetPreviewMaxLineCount(Value: Integer); procedure SetPreviewRightIndent(Value: Integer); procedure SetPreviewStyle(Value: TdxMasterViewStyle); procedure SetRowSeparatorColor(Value: TColor); procedure SetRowSeparatorWidth(Value: Integer); procedure SetLevelSeparatorColor(Value: TColor); procedure SetLevelSeparatorWidth(Value: Integer); procedure SetViewMode(Value: TdxMasterViewViewMode); procedure SetVisibleColumn(Index: Integer; Value: TdxMasterViewColumn); function IsDeleteConfirmCaptionTextStored: Boolean; function IsDeleteConfirmTextStored: Boolean; function IsMultipleDeleteConfirmTextStored: Boolean; procedure ReadHeaderWidths(Reader: TReader); procedure WriteHeaderWidths(Writer: TWriter); procedure AddColumn(AColumn: TdxMasterViewColumn); procedure RemoveColumn(AColumn: TdxMasterViewColumn); procedure AddItem(Value: TdxMasterViewLevel); procedure RemoveItem(Value: TdxMasterViewLevel); function FindPreviewField: TField; procedure FontsChanged; procedure DeleteNodes; procedure RefreshDetailKeyFieldsList; procedure RefreshIDFieldsList; procedure RefreshMasterKeyFieldsList; procedure RefreshVisibleColumnsList; procedure SummaryColumnsChanged(AColumn: TdxMasterViewColumn; Operation: TOperation); protected procedure DefineProperties(Filer: TFiler); override; procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override; procedure Loaded; override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure SetName(const NewName: TComponentName); override; procedure SetParentComponent(AParent: TComponent); override; procedure ActiveChanged; procedure AfterPaint; procedure AssignColumnWidths; procedure BeforePaint; procedure BeginLoad; procedure BeginLoading; procedure CalcDefaultWidths(Column: TdxMasterViewColumn); procedure CalcLineWidth; procedure CalcParents; procedure CalcRealHeights; procedure DestroySummaries(NodeTypes: TdxMasterViewNodeTypes); procedure EndLoad(RecalcInfo: Boolean); procedure EndLoading; procedure LoadingComplete; procedure CalcPreviewLineCount(DC: HDC; const S: string; Width: Integer; Node: TdxMasterViewNode; var Height: Integer); function CanDelete: Boolean; function CanGrouping(Column: TdxMasterViewColumn; Index: Integer): Boolean; function CanHorSizing: Boolean; function CanInsert: Boolean; function CanSmartRefresh: Boolean; function CanSorting(Column: TdxMasterViewColumn): Boolean; procedure CaptionStyleChanged(Values: TdxMasterViewStyleValues); procedure ChangeGrouping(Column: TdxMasterViewColumn; AIndex: Integer); procedure ChangeSorting(Column: TdxMasterViewColumn; ASortOrder: TdxMasterViewSortOrder; ASortIndex: Integer); procedure ContentStyleChanged(Values: TdxMasterViewStyleValues); procedure DataChanged; procedure DoAfterCollapse(Node: TdxMasterViewNode); procedure DoAfterExpand(Node: TdxMasterViewNode); function DoBeforeCollapse(Node: TdxMasterViewNode): Boolean; function DoBeforeExpand(Node: TdxMasterViewNode): Boolean; procedure DoGetContentStyle(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; var NewStyle: TdxMasterViewStyle); virtual; procedure DoGetFooterCellText(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; var Text: string); procedure DoHideColumn(Column: TdxMasterViewColumn); procedure DoNodeDeleted(Node: TdxMasterViewNode); procedure DoShowColumn(Column: TdxMasterViewColumn); procedure DoSorting; procedure DrawIndent(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); procedure DrawGroupByBox(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); procedure DrawHeader(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); procedure DrawContent(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); procedure DrawFooter(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); procedure EditingChanged; procedure FooterStyleChanged(Values: TdxMasterViewStyleValues); function GetCurDetailKeyValue: Variant; function GetCurIDValue: Variant; function GetCurKeyValue: Variant; function GetCurMasterKeyValue: Variant; function GetCurPreviewText(Node: TdxMasterViewNode): string; function GetParentNode(Root, Node: TdxMasterViewNode; FirstIndex, AvailIndex: PInteger; RestItems: TList; CheckRoot: Boolean): TdxMasterViewNode; procedure GroupByBoxStyleChanged(Values: TdxMasterViewStyleValues); procedure GroupStyleChanged(Values: TdxMasterViewStyleValues); function HasExpandButton: Boolean; function HasGrid: Boolean; //function HasAsChild(Level: TdxMasterViewLevel): Boolean; function HasAsParent(Level: TdxMasterViewLevel): Boolean; procedure HeaderStyleChanged(Values: TdxMasterViewStyleValues); function IndexOfParent(Level: TdxMasterViewLevel): Integer; procedure LayoutChanged; procedure LevelChanged(HardRefresh: Boolean); function LoadDataOnExpand: Boolean; procedure MakeVisibleColumnsSortedList(List: TList); procedure PreviewChanged; procedure PreviewStyleChanged(Values: TdxMasterViewStyleValues); procedure RecordChanged(Field: TField); procedure RemoveFreeSpace; procedure RestoreVisibleColumnsPlaces(var List: TList); procedure SaveVisibleColumnsPlaces(var List: TList); procedure FreeVisibleColumnsPlaces(var List: TList); procedure SyncPos; procedure UncheckTriedToExpand; procedure WidthChanged; procedure WidthChangedEx; property Active: Boolean read GetActive; property DataLink: TdxMasterViewDataLink read FDataLink; property IsLoading: Boolean read GetIsLoading; property CaptionHeight: Integer read FCaptionHeight; property ContentHeight: Integer read FContentHeight; property ContentRealHeight: Integer read FContentRealHeight; property ExtLineWidth: Integer read FExtLineWidth; property FooterHeight: Integer read FFooterHeight; property GroupByBoxHeight: Integer read GetGroupByBoxHeight; property GroupByBoxTextHeight: Integer read FGroupByBoxTextHeight; property GroupHeight: Integer read FGroupHeight; property HeaderHeight: Integer read FHeaderHeight; property HeaderRealHeight: Integer read FHeaderRealHeight; property Indent: Integer read GetIndent; property IsDestroying: Boolean read GetIsDestroying; property LineWidth: Integer read FLineWidth; property NonScaledWidth: Integer read GetNonScaledWidth; property VisibleWidth: Integer read GetVisibleWidth; property Horizontal: Boolean read FHorizontal; property ParentCount: Integer read FParentCount; {$IFDEF CBUILDER3} property Parents[Index: Integer]: TdxMasterViewLevel read GetParent; {$ELSE} property Parents: PdxMVLevels read FParents; {$ENDIF} property RowCount: Integer read GetRowCount; property VisibleRowCount: Integer read GetVisibleRowCount; property GridLinesBrush: HBRUSH read FGridLinesBrush; property GridLinesPen: HPEN read FGridLinesPen; property LevelSeparatorBrush: HBRUSH read FLevelSeparatorBrush; property RowSeparatorBrush: HBRUSH read FRowSeparatorBrush; property DontFilterRecords: Boolean read GetDontFilterRecords; property OccupyRestSpace: Boolean read GetOccupyRestSpace; property ShowCaption: Boolean read GetShowCaption; property ShowFooter: Boolean read GetShowFooter; property ShowGrid: Boolean read GetShowGrid; property ShowGridWithPreview: Boolean read GetShowGridWithPreview; property ShowGroupByBox: Boolean read GetShowGroupByBox; property ShowHeader: Boolean read GetShowHeader; property ShowPreview: Boolean read GetShowPreview; property SmartLoad: Boolean read GetSmartLoad; property SmartReload: Boolean read GetSmartReload; property HeaderBrush: HBRUSH read GetHeaderBrush; property HeaderColor: TColor read GetHeaderColor; property HeaderFont: TFont read GetHeaderFont; property HeaderRestSpaceBrush: HBRUSH read GetHeaderRestSpaceBrush; property HeaderRestSpaceColor: TColor read GetHeaderRestSpaceColor; property ContentAnotherBrush: HBRUSH read GetContentAnotherBrush; property ContentAnotherColor: TColor read GetContentAnotherColor; property ContentBrush: HBRUSH read GetContentBrush; property ContentColor: TColor read GetContentColor; property ContentFont: TFont read GetContentFont; property FooterBrush: HBRUSH read GetFooterBrush; property FooterColor: TColor read GetFooterColor; property FooterFont: TFont read GetFooterFont; property PreviewAnotherBrush: HBRUSH read GetPreviewAnotherBrush; property PreviewAnotherColor: TColor read GetPreviewAnotherColor; property PreviewBrush: HBRUSH read GetPreviewBrush; property PreviewColor: TColor read GetPreviewColor; property PreviewColorAssigned: Boolean read GetPreviewColorAssigned; property PreviewFont: TFont read GetPreviewFont; property PreviewFontAssigned: Boolean read GetPreviewFontAssigned; property PreviewFontColor: TColor read GetPreviewFontColor; property CaptionBrush: HBRUSH read GetCaptionBrush; property CaptionColor: TColor read GetCaptionColor; property CaptionFont: TFont read GetCaptionFont; property GroupBrush: HBRUSH read GetGroupBrush; property GroupColor: TColor read GetGroupColor; property GroupFont: TFont read GetGroupFont; property GroupByBoxBrush: HBRUSH read GetGroupByBoxBrush; property GroupByBoxColor: TColor read GetGroupByBoxColor; property GroupByBoxFont: TFont read GetGroupByBoxFont; property GroupByBoxFontColor: TColor read GetGroupByBoxFontColor; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; function GetParentComponent: TComponent; override; function HasParent: Boolean; override; function Add: TdxMasterViewLevel; procedure BeginAssignWidths; procedure BeginDataChanging; procedure BeginLayout; procedure BeginSorting; procedure CancelAssignWidths; procedure CancelDataChanging; procedure CancelLayout; procedure CancelSorting; procedure Clear; procedure ClearSorting(ExceptionalColumn: TdxMasterViewColumn); function ColumnByFieldName(const AFieldName: string): TdxMasterViewColumn; function ColumnByName(const AName: string): TdxMasterViewColumn; procedure CreateAllColumns; function CreateColumn(AColumnClass: TdxMasterViewColumnClass): TdxMasterViewColumn; procedure DestroyColumns; procedure EndAssignWidths; procedure EndDataChanging; procedure EndLayout; procedure EndSorting; property AbsoluteIndex: Integer read FAbsoluteIndex; property CanUseSmartReload: Boolean read FCanUseSmartReload write FCanUseSmartReload; property ColumnCount: Integer read GetColumnCount; property Columns[Index: Integer]: TdxMasterViewColumn read GetColumn write SetColumn; property Control: TdxMasterView read FControl; property Count: Integer read GetCount; property DataSet: TDataSet read GetDataSet; property DetailKeyFieldCount: Integer read GetDetailKeyFieldCount; property DetailKeyFields[Index: Integer]: TField read GetDetailKeyField; property GroupColumnCount: Integer read GetGroupColumnCount; property GroupColumns[Index: Integer]: TdxMasterViewColumn read GetGroupColumn; property HasChildren: Boolean read GetHasChildren; property IDFieldCount: Integer read GetIDFieldCount; property IDFields[Index: Integer]: TField read GetIDField; property Index: Integer read FIndex; property IsFirst: Boolean read GetIsFirst; property IsLast: Boolean read GetIsLast; property IsTop: Boolean read GetIsTop; property Items[Index: Integer]: TdxMasterViewLevel read GetItem; default; property Layout: TdxMasterViewLayout read FLayout; property MasterKeyFieldCount: Integer read GetMasterKeyFieldCount; property MasterKeyFields[Index: Integer]: TField read GetMasterKeyField; property Parent: TdxMasterViewLevel read FParent; property PreviewField: TField read FPreviewField write SetPreviewField; property SortedColumnCount: Integer read GetSortedColumnCount; property SortedColumns[Index: Integer]: TdxMasterViewColumn read GetSortedColumn; property Visible: Boolean read FVisible; property VisibleColumnCount: Integer read GetVisibleColumnCount; property VisibleColumns[Index: Integer]: TdxMasterViewColumn read GetVisibleColumn write SetVisibleColumn; procedure GetContentParams(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; ABrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont; CheckSelected: Boolean); procedure GetFooterParams(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; ABrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont); procedure GetGroupByBoxParams(Node: TdxMasterViewNode; ABrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont); procedure GetHeaderParams(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; ABrush, ARestSpaceBrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont); procedure GetPreviewParams(Node: TdxMasterViewNode; ABrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont); published property Caption: string read FCaption write SetCaption; property CaptionStyle: TdxMasterViewStyle read FCaptionStyle write SetCaptionStyle; property ContentStyle: TdxMasterViewStyle read FContentStyle write SetContentStyle; property DataSource: TDataSource read GetDataSource write SetDataSource; property DeleteConfirmCaptionText: string read FDeleteConfirmCaptionText write FDeleteConfirmCaptionText stored IsDeleteConfirmCaptionTextStored; property DeleteConfirmText: string read FDeleteConfirmText write FDeleteConfirmText stored IsDeleteConfirmTextStored; property DetailKey: string read FDetailKey write SetDetailKey; property FooterStyle: TdxMasterViewStyle read FFooterStyle write SetFooterStyle; property GridLinesColor: TColor read FGridLinesColor write SetGridLinesColor default clBtnShadow; property GroupByBoxStyle: TdxMasterViewStyle read FGroupByBoxStyle write SetGroupByBoxStyle; property GroupStyle: TdxMasterViewStyle read FGroupStyle write SetGroupStyle; property HeaderStyle: TdxMasterViewStyle read FHeaderStyle write SetHeaderStyle; property ID: string read FID write SetID; property LevelSeparatorColor: TColor read FLevelSeparatorColor write SetLevelSeparatorColor default clWindowText{!}; property LevelSeparatorWidth: Integer read FLevelSeparatorWidth write SetLevelSeparatorWidth default 0; property MasterKey: string read FMasterKey write SetMasterKey; property MultipleDeleteConfirmText: string read FMultipleDeleteConfirmText write FMultipleDeleteConfirmText stored IsMultipleDeleteConfirmTextStored; property OptionsBehavior: TdxMasterViewLevelOptionsBehavior read FOptionsBehavior write FOptionsBehavior default [lobDblClkExpanding]; property OptionsCustomize: TdxMasterViewLevelOptionsCustomize read FOptionsCustomize write FOptionsCustomize default [locColumnMoving, locColumnHorSizing, locColumnVerSizing, locColumnSorting, locColumnGrouping, locShowColumnOnUngrouping]; property OptionsCustomizeBox: TdxMasterViewLevelOptionsCustomizeBox read FOptionsCustomizeBox write FOptionsCustomizeBox default [loxCaption, loxColumns, loxGrid, loxGroupByBox, loxHeader, loxViewMode]; property OptionsDB: TdxMasterViewLevelOptionsDB read FOptionsDB write SetOptionsDB default [lodConfirmDelete, lodSmartLoad, lodSmartReload]; property OptionsHeader: TdxMasterViewLevelOptionsHeader read FOptionsHeader write SetOptionsHeader default [lohForFirstNode, lohForFirstVisibleNode, lohAfterExpandedNode]; property OptionsView: TdxMasterViewLevelOptionsView read FOptionsView write SetOptionsView default [lovGrid, lovGridWithPreview, lovHeader, lovOccupyRestSpace]; property PreviewFieldName: string read FPreviewFieldName write SetPreviewFieldName; property PreviewLeftIndent: Integer read FPreviewLeftIndent write SetPreviewLeftIndent default dxMVPreviewLeftIndent; property PreviewLineCount: Integer read FPreviewLineCount write SetPreviewLineCount default 0; property PreviewMaxLength: Integer read FPreviewMaxLength write SetPreviewMaxLength default 0; property PreviewMaxLineCount: Integer read FPreviewMaxLineCount write SetPreviewMaxLineCount default dxMVPreviewMaxLineCount; property PreviewRightIndent: Integer read FPreviewRightIndent write SetPreviewRightIndent default dxMVPreviewRightIndent; property PreviewStyle: TdxMasterViewStyle read FPreviewStyle write SetPreviewStyle; property RowSeparatorColor: TColor read FRowSeparatorColor write SetRowSeparatorColor default clBtnShadow; property RowSeparatorWidth: Integer read FRowSeparatorWidth write SetRowSeparatorWidth default 0; property ViewMode: TdxMasterViewViewMode read FViewMode write SetViewMode default vmHorizontal; property OnChangeGrouping: TdxMVChangeGroupingEvent read FOnChangeGrouping write FOnChangeGrouping; property OnCollapsed: TdxMVNodeEvent read FOnCollapsed write FOnCollapsed; property OnCollapsing: TdxMVNodeAllowEvent read FOnCollapsing write FOnCollapsing; property OnColumnSorted: TdxMVColumnEvent read FOnColumnSorted write FOnColumnSorted; property OnColumnSorting: TdxMVColumnAllowEvent read FOnColumnSorting write FOnColumnSorting; property OnDeleteNode: TdxMVNodeEvent read FOnDeleteNode write FOnDeleteNode; property OnExpanded: TdxMVNodeEvent read FOnExpanded write FOnExpanded; property OnExpanding: TdxMVNodeAllowEvent read FOnExpanding write FOnExpanding; property OnFilterRecord: TdxMVFilterRecordEvent read FOnFilterRecord write FOnFilterRecord; property OnGetCaptionStyle: TdxMVLevelGetStyleEvent read FOnGetCaptionStyle write FOnGetCaptionStyle; property OnGetContentStyle: TdxMVLevelGetStyleEvent read FOnGetContentStyle write FOnGetContentStyle; property OnGetFooterCellText: TdxMVGetFooterCellTextEvent read FOnGetFooterCellText write FOnGetFooterCellText; property OnGetFooterStyle: TdxMVLevelGetStyleEvent read FOnGetFooterStyle write FOnGetFooterStyle; property OnGetGroupByBoxStyle: TdxMVLevelGetStyleEvent read FOnGetGroupByBoxStyle write FOnGetGroupByBoxStyle; property OnGetGroupStyle: TdxMVLevelGetStyleEvent read FOnGetGroupStyle write FOnGetGroupStyle; property OnGetHeaderStyle: TdxMVLevelGetStyleEvent read FOnGetHeaderStyle write FOnGetHeaderStyle; property OnGetPreviewLineCount: TdxMVGetPreviewLineCountEvent read FOnGetPreviewLineCount write FOnGetPreviewLineCount; property OnGetPreviewStyle: TdxMVLevelGetStyleEvent read FOnGetPreviewStyle write FOnGetPreviewStyle; property OnGetPreviewText: TdxMVGetPreviewTextEvent read FOnGetPreviewText write FOnGetPreviewText; property OnHideColumn: TdxMVColumnEvent read FOnHideColumn write FOnHideColumn; property OnShowColumn: TdxMVColumnEvent read FOnShowColumn write FOnShowColumn; property OnWidthChanged: TNotifyEvent read FOnWidthChanged write FOnWidthChanged; end; PdxMasterViewNodeList = ^TdxMasterViewNodeList; TdxMasterViewNodeList = array[0..MaxListSize - 1] of TdxMasterViewNode; TdxMasterViewListSortCompare = function (Item1, Item2: TdxMasterViewNode): Integer; TdxMasterViewListMode = (lmItems, lmAbsoluteItems, lmSelectedItems); TdxMasterViewList = class private FCapacity: Integer; FCount: Integer; FDeletionLockCount: Integer; FInsertionLockCount: Integer; FList: PdxMasterViewNodeList; FMode: TdxMasterViewListMode; FNewCount, FPrevCount: Integer; FOwner: TdxMasterViewNode; function Get(Index: Integer): TdxMasterViewNode; procedure Put(Index: Integer; Item: TdxMasterViewNode); procedure SetCapacity(NewCapacity: Integer); procedure SetCount(NewCount: Integer); protected procedure Grow; public constructor Create(AOwner: TdxMasterViewNode; AMode: TdxMasterViewListMode); destructor Destroy; override; function Add(Item: TdxMasterViewNode): Integer; procedure BeginDeletion; // these two optimization's methods must not intersect procedure BeginInsertion; procedure Clear; procedure CopyTo(List: TList); procedure Delete(Index: Integer); procedure EndDeletion; procedure EndInsertion; class procedure Error(const Msg: string; Data: Integer); function First: TdxMasterViewNode; function IndexOf(Item: TdxMasterViewNode): Integer; procedure Insert(Index: Integer; Item: TdxMasterViewNode); function Last: TdxMasterViewNode; procedure Move(CurIndex, NewIndex: Integer); function Remove(Item: TdxMasterViewNode): Integer; procedure Sort(Compare: TdxMasterViewListSortCompare; TopBound, BottomBound: Integer); property Capacity: Integer read FCapacity write SetCapacity; property Count: Integer read FCount write SetCount; property Items[Index: Integer]: TdxMasterViewNode read Get write Put; default; property List: PdxMasterViewNodeList read FList; property Owner: TdxMasterViewNode read FOwner; end; TdxMasterViewNodeViewInfoOption = (vioClipped); TdxMasterViewNodeViewInfoOptions = set of TdxMasterViewNodeViewInfoOption; TdxMasterViewNodeViewInfo = record Bounds: TRect; Width: Integer; GroupByBoxSize: Integer; HeaderSize: Integer; PreviewSize: Integer; FooterSize: Integer; EndLevelCount: Integer; EndLevelData: Integer; Options: TdxMasterViewNodeViewInfoOptions; LevelIndex: Integer; end; PdxMVSortingValues = ^TdxMVSortingValues; TdxMVSortingValues = array[0..MaxInt div SizeOf(PVariant) - 1] of PVariant; PdxMVSortingData = ^TdxMVSortingData; TdxMVSortingData = array[0..MaxInt div SizeOf(PdxMVSortingValues) - 1] of PdxMVSortingValues; PdxMVSummaryValues = ^TdxMVSummaryValues; TdxMVSummaryValues = array[0..MaxInt div SizeOf(Extended) - 1] of Extended; PdxMVSummaries = ^TdxMVSummaries; TdxMVSummaries = array[0..MaxInt div SizeOf(PdxMVSummaryValues) - 1] of PdxMVSummaryValues; TdxMasterViewNode = class private FAbsoluteIndex: Integer; FControl: TdxMasterView; //change to FLevel.Control? (4 times slower) FData: Pointer; FExpanded: Boolean; FGroupIndex: Integer; FID: Variant; FIndex: Integer; FItems: TdxMasterViewList; FKeyValue: Variant; FLevel: TdxMasterViewLevel; FNodeType: TdxMasterViewNodeType; FParentNode: TdxMasterViewNode; FPreviewText: string; FSelectedIndex: Integer; FSummaries: PdxMVSummaries; FSummariesInitialized: Boolean; FTriedToExpand: Boolean; FValues: TStrings; FViewInfo: TdxMasterViewNodeViewInfo; function GetBounds: TRect; function GetContentBounds: TRect; function GetContentBoundsWithoutPreview: TRect; function GetCount: Integer; function GetCountInLevel(ALevel: TdxMasterViewLevel): Integer; function GetFirst: TdxMasterViewNode; function GetFirstInLevel(ALevel: TdxMasterViewLevel): TdxMasterViewNode; function GetFocused: Boolean; function GetFooterBounds: TRect; function GetFullBounds: TRect; function GetFullContentBounds: TRect; function GetFullFooterBounds: TRect; function GetFullGroupByBoxBounds: TRect; function GetFullHeaderBounds: TRect; function GetGroupByBoxBounds: TRect; function GetGroupByBoxColumnBounds(Index: Integer): TRect; function GetGroupIndex: Integer; function GetHasChildren: Boolean; function GetHeaderBounds: TRect; function GetHeaderColBounds(Index: Integer): TRect; function GetHeaderRowBounds(Index: Integer): TRect; function GetHorizontal: Boolean; function GetIsEditing: Boolean; function GetIsFirst: Boolean; function GetIsFirstInLevel: Boolean; function GetIsLast: Boolean; function GetIsLastInLevel: Boolean; function GetItem(Index: Integer): TdxMasterViewNode; function GetLast: TdxMasterViewNode; function GetLastInLevel(ALevel: TdxMasterViewLevel): TdxMasterViewNode; function GetLeftSpaceBounds: TRect; function GetNodeWithSummaryData(ALevel: TdxMasterViewLevel): TdxMasterViewNode; function GetParentNodes(Index: Integer): TdxMasterViewNode; function GetParentNodeWithData: TdxMasterViewNode; function GetPreviewBounds: TRect; function GetRightSpaceBounds: TRect; function GetSelected: Boolean; function GetString(Index: Integer): string; function GetSubFooterBounds(AParentIndex: Integer): TRect; function GetValue(Index: Integer): Variant; function GetValueCount: Integer; function GetVisible: Boolean; procedure SetExpanded(Value: Boolean); procedure SetFocused(Value: Boolean); procedure SetSelected(Value: Boolean); procedure AddItem(AIndex: Integer; Value: TdxMasterViewNode); procedure RemoveItem(Value: TdxMasterViewNode); procedure SetHasLevelSeparator(var EndLevelData: Integer; ALevelParentIndex: Integer); procedure SetHasRowSeparator(var EndLevelData: Integer; ALevelParentIndex: Integer); protected function Add(ALevel: TdxMasterViewLevel; AIndex: Integer; ANodeType: TdxMasterViewNodeType; const AID, AKeyValue: Variant): TdxMasterViewNode; procedure AddToList(ALevel: TdxMasterViewLevel; OtherNodesList, GroupNodesList: TList; ProcessRoot: Boolean); procedure AddVisibleToList(List: TdxMasterViewList); function CanDelete: Boolean; function CanExpand: Boolean; procedure ChangeExpanded(Value: Boolean); procedure CheckChildNodes(ALevel: TdxMasterViewLevel; NeedCheckParentNodes: Boolean); procedure CheckExpanded; procedure CheckParentNodes; procedure Clear; procedure ClearValues; function ContainsDataInChildren(ALevel: TdxMasterViewLevel): Boolean; procedure DoSmartLoadChanged(ALevel: TdxMasterViewLevel); procedure DoSorting(ALevel: TdxMasterViewLevel); procedure Draw(DC: HDC); function GetAvailIndex(AChildLevelIndex: Integer): Integer; function GetLastSubChild: TdxMasterViewNode; function GetLevelIndex: Integer; function HasDataInChildren(ALevel: TdxMasterViewLevel): Boolean; function InSelectedPeriod: Boolean; procedure Invalidate(Column: TdxMasterViewColumn; VPart: TdxMasterViewVPart); procedure MoveData(ALevel: TdxMasterViewLevel; FromIndex, ToIndex: Integer); procedure NodeChanged(HardRefresh: Boolean); procedure Sync; procedure SyncPos; procedure UpdateData(UpdateSelf, UpdateChildren, UpdateOther: Boolean); procedure UpdateDataForLevel(ALevel: TdxMasterViewLevel); procedure CreateSummaries; procedure DestroySummaries; procedure CheckSummaries; procedure InitializeSummaries(ALevel: TdxMasterViewLevel); procedure CalcSummaries(UseRealData: Boolean); procedure FinalizeSummaries(ALevel: TdxMasterViewLevel); function BeginLevel(var Size: Integer): Boolean; function EndLevel(var Size, EndLevelCount, EndLevelData: Integer): Boolean; function HasExpandButton: Boolean; function HasFooter(AParentIndex: Integer): Boolean; function HasGroupByBox(var Size: Integer): Boolean; function HasHeader(ATopIndex: Integer; var Size: Integer): Boolean; function HasLevelSeparator(ALevelParentIndex: Integer): Boolean; function HasPreview: Boolean; function HasRowSeparator(ALevelParentIndex: Integer): Boolean; function HasSummariesData: Boolean; function CalcViewInfo(const ATopIndex: Integer; const R: TRect; const AIndent, AWidth: Integer; var AFooterSize: Integer; const StoreInfo: Boolean): Integer; procedure ClearViewInfo; function ContentColIndexFromX(X: Integer): Integer; function ContentRowIndexFromY(Y: Integer): Integer; function FooterColIndexFromX(AParentIndex: Integer; X: Integer): Integer; function FooterRowIndexFromY(AParentIndex: Integer; Y: Integer): Integer; function HeaderColIndexFromX(X: Integer): Integer; function HeaderRowIndexFromY(Y: Integer): Integer; function InternalHeaderRowIndexFromY(Y: Integer): Integer; function ShowExpandButton: Boolean; property NodesWithSummaryData[ALevel: TdxMasterViewLevel]: TdxMasterViewNode read GetNodeWithSummaryData; property Bounds: TRect read GetBounds; property ContentBounds: TRect read GetContentBounds; property ContentBoundsWithoutPreview: TRect read GetContentBoundsWithoutPreview; property FooterBounds: TRect read GetFooterBounds; property FullBounds: TRect read GetFullBounds; property FullContentBounds: TRect read GetFullContentBounds; property FullFooterBounds: TRect read GetFullFooterBounds; property FullGroupByBoxBounds: TRect read GetFullGroupByBoxBounds; property FullHeaderBounds: TRect read GetFullHeaderBounds; property GroupByBoxBounds: TRect read GetGroupByBoxBounds; property GroupByBoxColumnBounds[Index: Integer]: TRect read GetGroupByBoxColumnBounds; property HeaderBounds: TRect read GetHeaderBounds; property HeaderColBounds[Index: Integer]: TRect read GetHeaderColBounds; property HeaderRowBounds[Index: Integer]: TRect read GetHeaderRowBounds; property LeftSpaceBounds: TRect read GetLeftSpaceBounds; property PreviewBounds: TRect read GetPreviewBounds; property RightSpaceBounds: TRect read GetRightSpaceBounds; property SubFooterBounds[AParentIndex: Integer]: TRect read GetSubFooterBounds; property ViewInfo: TdxMasterViewNodeViewInfo read FViewInfo; public constructor Create(AControl: TdxMasterView; ALevel: TdxMasterViewLevel; AParentNode: TdxMasterViewNode; AIndex: Integer; ANodeType: TdxMasterViewNodeType; const AID, AKeyValue: Variant); destructor Destroy; override; procedure Collapse(Recurse: Boolean); procedure Delete; procedure Expand(Recurse: Boolean); function FindChildren: Boolean; function GetParentNode(ALevel: TdxMasterViewLevel; OnlyDataNodes: Boolean): TdxMasterViewNode; procedure MakeVisible; procedure LoadChildren(Recurse: Boolean); function NodeFromID(ALevel: TdxMasterViewLevel; const AID: Variant): TdxMasterViewNode; function NodeFromKeyValue(ALevel: TdxMasterViewLevel; AChildIndex: Integer; const AKeyValue: Variant): TdxMasterViewNode; property AbsoluteIndex: Integer read FAbsoluteIndex; property Control: TdxMasterView read FControl; property Count: Integer read GetCount; property CountInLevel[ALevel: TdxMasterViewLevel]: Integer read GetCountInLevel; property Data: Pointer read FData write FData; property Expanded: Boolean read FExpanded write SetExpanded; property First: TdxMasterViewNode read GetFirst; property FirstInLevel[ALevel: TdxMasterViewLevel]: TdxMasterViewNode read GetFirstInLevel; property Focused: Boolean read GetFocused write SetFocused; property GroupIndex: Integer read FGroupIndex; property GroupValue: Variant read FKeyValue; property GroupValueText: Variant read FID; property HasChildren: Boolean read GetHasChildren; property Horizontal: Boolean read GetHorizontal; property ID: Variant read FID; property Index: Integer read FIndex; property IsEditing: Boolean read GetIsEditing; property IsFirst: Boolean read GetIsFirst; property IsFirstInLevel: Boolean read GetIsFirstInLevel; property IsLast: Boolean read GetIsLast; property IsLastInLevel: Boolean read GetIsLastInLevel; property Items[Index: Integer]: TdxMasterViewNode read GetItem; default; property ItemsList: TdxMasterViewList read FItems; property KeyValue: Variant read FKeyValue; property Last: TdxMasterViewNode read GetLast; property LastInLevel[ALevel: TdxMasterViewLevel]: TdxMasterViewNode read GetLastInLevel; property Level: TdxMasterViewLevel read FLevel; property NodeType: TdxMasterViewNodeType read FNodeType; property ParentNode: TdxMasterViewNode read FParentNode; property ParentNodes[Index: Integer]: TdxMasterViewNode read GetParentNodes; property ParentNodeWithData: TdxMasterViewNode read GetParentNodeWithData; property PreviewText: string read FPreviewText write FPreviewText; property Selected: Boolean read GetSelected write SetSelected; property SelectedIndex: Integer read FSelectedIndex; property StringCount: Integer read GetValueCount; property Strings[Index: Integer]: string read GetString; property ValueCount: Integer read GetValueCount; property Values[Index: Integer]: Variant read GetValue; property Visible: Boolean read GetVisible; end; TdxMasterViewDragDetect = (ddNone, ddDrag, ddCancel); TdxMasterViewDragSource = (dsHeader, dsGroupByBox, dsCustomizingForm); TdxMasterViewDirection = (dirNone, dirLeft, dirUp, dirRight, dirDown); TdxMasterViewHitTestCode = (htNone, htLeftSpace, htRightSpace, htIndent, htGroupByBox, htHeader, htHeaderLeftEdge, htHeaderRightEdge, htHeaderTopEdge, htHeaderBottomEdge, htContent, htExpandButton, htContentLeftEdge, htContentRightEdge, htPreview, htFooter, htCustomizationForm, htOther); TdxMasterViewState = (mvsCtrlClick, mvsIndentClick, mvsAnchorSelected, mvsSelectPeriod, mvsColumnSizing, mvsColumnMoving, mvsScrolling); TdxMasterViewStates = set of TdxMasterViewState; TdxMasterViewOptionBehavior = (mobAnsiCompareStrOnGrouping, mobAnsiCompareStrOnSorting, mobCaseInsensitiveGrouping, mobCaseInsensitiveSorting, mobMultiSelect, mobShowHourGlassCursor, mobStoreInIniFile, mobStoreInRegistry, mobUseIndent); TdxMasterViewOptionsBehavior = set of TdxMasterViewOptionBehavior; TdxMasterViewOptionDB = (modSyncMove); TdxMasterViewOptionsDB = set of TdxMasterViewOptionDB; TdxMasterViewOptionView = (movAnimation, movAutoColumnWidth, movDrawEndEllipsis, movHideFocusRect, movHideSelection, movKeepColumnWidths, movTransparentDragAndDrop, movUseBitmap, movUseBitmapToDrawPreview); TdxMasterViewOptionsView = set of TdxMasterViewOptionView; TdxMasterViewRegIniFileMode = (riIniFile, riRegistry); TdxMVScrollBars = (sbAuto, sbHorizontal, sbVertical, sbBoth, sbNone); TdxMVColumnMovingEvent = procedure(Sender: TdxMasterView; Column: TdxMasterViewColumn; P: TPoint; RowIndex, ColIndex: Integer; InsertIntoNewRow: Boolean; var Accept: Boolean) of object; TdxMVCustomizingEvent = procedure(Sender: TdxMasterView; Showing: Boolean) of object; TdxMVFocusNodeEvent = procedure(Sender: TdxMasterView; PrevNode, CurNode: TdxMasterViewNode) of object; TdxMVLevelEvent = procedure(Sender: TdxMasterView; Level: TdxMasterViewLevel) of object; TdxMVSelectNodeEvent = procedure(Sender: TdxMasterView; Node: TdxMasterViewNode; var Allow: Boolean) of object; TdxMasterView = class(TCustomControl) private FAbsoluteItems: TdxMasterViewList; FAbsoluteLevels: TList; FAnimation: Boolean; FArrowsColor: TColor; FBitmap: TBitmap; FBtnHighlightPen: HPEN; FBtnShadowPen: HPEN; FCalculatingScrollableWidth: Boolean; FCustomizationForm: TWinControl; FCustomizationFormPos: TPoint; FCustomizationFormRowCount: Integer; FCustomizing: Boolean; FDontInvalidate: Boolean; FFocusedNode: TdxMasterViewNode; FFrozen: Boolean; FHighlightStyle: TdxMasterViewStyle; FInactiveStyle: TdxMasterViewStyle; FIniFileName: string; FItems: TdxMasterViewNode; FLastMousePos: TPoint; FLastPartVisibleItemCount: Integer; FLastSelectedNode: TdxMasterViewNode; FLastVisibleItemCount: Integer; FLayoutLockCount: Integer; FLeftPos: Integer; FLevels: TdxMasterViewLevel; FMovingColumn: TdxMasterViewColumn; FMovingColumnNode: TdxMasterViewNode; FOptionsBehavior: TdxMasterViewOptionsBehavior; FOptionsDB: TdxMasterViewOptionsDB; FOptionsView: TdxMasterViewOptionsView; FPainting: Boolean; FPrevTopNode: TdxMasterViewNode; FRegistryPath: string; FScrollableWidth: Integer; FScrollBars: TdxMVScrollBars; FScrollTimer: UINT; FSelectedItems: TdxMasterViewList; FSelectionAnchor: TdxMasterViewNode; FSelectionLockCount: Integer; FSizingColumn: TdxMasterViewColumn; FSortingColumnCount: Integer; FSortingData: PdxMVSortingData; FSortingDesc: PBoolArray; FState: TdxMasterViewStates; FStyles: TList; FTopNode: TdxMasterViewNode; FTopNodeChangeLockCount: Integer; FUpdateLockCount: Integer; FUpdatingData: Boolean; // for sorting SortV1, SortV2: PVariant; SortS1, SortS2: string; FOnAfterColumnMoving: TdxMVColumnMovingEvent; FOnAfterUpdateData: TdxMVLevelEvent; FOnBeforeColumnMoving: TdxMVColumnMovingEvent; FOnBeforeFocusNode: TdxMVSelectNodeEvent; FOnBeforeUpdateData: TdxMVLevelEvent; FOnColumnMoving: TdxMVColumnMovingEvent; FOnCustomizing: TdxMVCustomizingEvent; FOnFocusNode: TdxMVFocusNodeEvent; FOnLeftPosChanged: TNotifyEvent; FOnSelectNode: TdxMVSelectNodeEvent; FOnSelectionChanged: TNotifyEvent; FOnTopNodeChanged: TNotifyEvent; function GetAbsoluteItemCount: Integer; function GetAbsoluteItem(Index: Integer): TdxMasterViewNode; function GetAbsoluteLevelCount: Integer; function GetAbsoluteLevel(Index: Integer): TdxMasterViewLevel; function GetAnimation: Boolean; function GetAutoColumnWidth: Boolean; function GetCustomizationFormLevel: TdxMasterViewLevel; function GetFocusedIndex: Integer; function GetHasFocus: Boolean; function GetHighlightBrush: HBRUSH; function GetHighlightColor: TColor; function GetHighlightFontColor: TColor; function GetInactiveBrush: HBRUSH; function GetInactiveColor: TColor; function GetInactiveFocusBrush: HBRUSH; function GetInactiveFocusColor: TColor; function GetInactiveFontColor: TColor; function GetIsDesigning: Boolean; function GetIsDestroying: Boolean; function GetIsLoading: Boolean; function GetMultiSelect: Boolean; function GetPartVisibleItemCount: Integer; function GetSelectedItem(Index: Integer): TdxMasterViewNode; function GetSelectedItemCount: Integer; function GetShowHourGlassCursor: Boolean; function GetStyle(Index: Integer): TdxMasterViewStyle; function GetStyleCount: Integer; function GetSyncMove: Boolean; function GetTopIndex: Integer; function GetTransparentDragAndDrop: Boolean; function GetUseIndent: Boolean; function GetVisibleItemCount: Integer; function GetWidthForColumns: Integer; procedure SetCanUseSmartReload(Value: Boolean); procedure SetCustomizationFormLevel(Value: TdxMasterViewLevel); procedure SetCustomizationFormRowCount(Value: Integer); procedure SetCustomizing(Value: Boolean); procedure SetFocusedIndex(Value: Integer); procedure SetFocusedNode(Value: TdxMasterViewNode); procedure SetHighlightStyle(Value: TdxMasterViewStyle); procedure SetInactiveStyle(Value: TdxMasterViewStyle); procedure SetLeftPos(Value: Integer); procedure SetOptionsBehavior(Value: TdxMasterViewOptionsBehavior); procedure SetOptionsDB(Value: TdxMasterViewOptionsDB); procedure SetOptionsView(Value: TdxMasterViewOptionsView); procedure SetScrollBars(Value: TdxMVScrollBars); procedure SetTopIndex(Value: Integer); procedure SetTopNode(Value: TdxMasterViewNode); procedure AddStyle(AStyle: TdxMasterViewStyle); procedure RemoveStyle(AStyle: TdxMasterViewStyle); procedure AddSelectedItem(ANode: TdxMasterViewNode); procedure RemoveSelectedItem(ANode: TdxMasterViewNode); procedure ClearSelectedItems; procedure CreateScrollTimer(Direction: TdxMasterViewDirection); procedure DestroyScrollTimer; procedure WMCaptureChanged(var Message: TMessage); message WM_CAPTURECHANGED; procedure WMDestroy(var Message: TMessage); message WM_DESTROY; procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND; procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE; procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL; procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS; procedure WMMouseActivate(var Message: TWMMouseActivate); message WM_MOUSEACTIVATE; procedure WMMouseWheel(var Message: TMessage); message WM_MOUSEWHEEL; procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE; procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST; procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT; procedure WMSetCursor(var Message: TWMSetCursor); message WM_SETCURSOR; procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS; procedure WMSize(var Message: TWMSize); message WM_SIZE; procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL; procedure WMWindowPosChanged(var Message: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED; procedure CMDesignHitTest(var Message: TCMDesignHitTest); message CM_DESIGNHITTEST; procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED; procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE; protected procedure CreateParams(var Params: TCreateParams); override; procedure CreateWnd; override; procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override; procedure KeyDown(var Key: Word; Shift: TShiftState); override; procedure Loaded; override; procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; procedure MouseMove(Shift: TShiftState; X, Y: Integer); override; procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure Paint; override; procedure SetName(const NewName: TComponentName); override; procedure WndProc(var Message: TMessage); override; procedure BeginAnimation; procedure EndAnimation; procedure BeforePaint; procedure AfterPaint; procedure BeginTopNodeChange; procedure EndTopNodeChange; property BtnHighlightPen: HPEN read FBtnHighlightPen; property BtnShadowPen: HPEN read FBtnShadowPen; procedure CalcScrollableWidth(CallingLevel: TdxMasterViewLevel); procedure CalcViewInfo(ATopIndex: Integer; AHeight: Integer; var AVisibleItemCount, APartVisibleItemCount: Integer; CalcNodesViewInfo: Boolean); procedure AssignColumnWidths; function GetAnimatableHeight: Integer; procedure SetScrollBarsInfo(var AVisibleItemCount, APartVisibleItemCount: Integer); procedure WidthChanged; function CanScroll(Direction: TdxMasterViewDirection): Boolean; function CheckLeftPos(Value: Integer): Integer; function CheckTopIndex(Value: Integer): Integer; procedure CheckFocusedNode; procedure DestroyStyles; procedure DoAfterUpdateData(Level: TdxMasterViewLevel); virtual; procedure DoBeforeUpdateData(Level: TdxMasterViewLevel); virtual; procedure DoColumnMoving(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; const InitialP: TPoint; DragSource: TdxMasterViewDragSource); procedure DoColumnHorSizing(Column: TdxMasterViewColumn; ContentSizing: Boolean); procedure DoColumnVerSizing(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; Edge: TdxMasterViewHitTestCode); procedure DoNodeDeleted(Node: TdxMasterViewNode); virtual; procedure DoScrolling; procedure DoSelectionChanged; virtual; function DoSelectNode(Node: TdxMasterViewNode): Boolean; procedure DoSorting; procedure DoTopNodeChanged; virtual; procedure HighlightStyleChanged(Values: TdxMasterViewStyleValues); procedure InactiveStyleChanged(Values: TdxMasterViewStyleValues); function CanFocusNode(Node: TdxMasterViewNode): Boolean; procedure ChangeFocusedIndex(Value: Integer); function GetFirstNodeFromVisible(Level: TdxMasterViewLevel): TdxMasterViewNode; function GetLastNodeFromVisible(Level: TdxMasterViewLevel): TdxMasterViewNode; function GetNextNode(Value: TdxMasterViewNode; OnSameLevel: Boolean): TdxMasterViewNode; function GetPrevNode(Value: TdxMasterViewNode; OnSameLevel: Boolean): TdxMasterViewNode; procedure ShowPrevPage(CurTopIndex: Integer; OnSameLevel: Boolean); procedure ShowNextPage(CurBottomIndex: Integer; OnSameLevel: Boolean); procedure Scroll(Direction: TdxMasterViewDirection); procedure RefreshAbsoluteItems; procedure RefreshAbsoluteLevels; procedure SelectPeriod(ToNode: TdxMasterViewNode); procedure InvalidateSelection; procedure Modified; procedure Freeze; function FreezeDataSet(ALevel: TdxMasterViewLevel): Integer; procedure Unfreeze; procedure UnfreezeDataSet(ALevel: TdxMasterViewLevel); property Frozen: Boolean read FFrozen; property Animation: Boolean read GetAnimation; property AutoColumnWidth: Boolean read GetAutoColumnWidth; property HasFocus: Boolean read GetHasFocus; property IsDesigning: Boolean read GetIsDesigning; property IsDestroying: Boolean read GetIsDestroying; property IsLoading: Boolean read GetIsLoading; property MultiSelect: Boolean read GetMultiSelect; property ScrollableWidth: Integer read FScrollableWidth; property ShowHourGlassCursor: Boolean read GetShowHourGlassCursor; property SyncMove: Boolean read GetSyncMove; property TransparentDragAndDrop: Boolean read GetTransparentDragAndDrop; property UseIndent: Boolean read GetUseIndent; property WidthForColumns: Integer read GetWidthForColumns; property HighlightBrush: HBRUSH read GetHighlightBrush; property HighlightColor: TColor read GetHighlightColor; property HighlightFontColor: TColor read GetHighlightFontColor; property InactiveBrush: HBRUSH read GetInactiveBrush; property InactiveColor: TColor read GetInactiveColor; property InactiveFocusColor: TColor read GetInactiveFocusColor; property InactiveFocusBrush: HBRUSH read GetInactiveFocusBrush; property InactiveFontColor: TColor read GetInactiveFontColor; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Invalidate; override; procedure DrawText(const DrawDC: HDC; var ARect: TRect; const Font: TFont; const Brush: HBRUSH; const TextColor, BkColor: TColor; const Alignment: TAlignment; const Text: string; const FillBackground, MakeRightSpace, AllowUseBitmap, MultiLine: Boolean); function GetSelectionParams(Node: TdxMasterViewNode; ABrush: PBRUSH; ATextColor, ABkColor: PColor): Boolean; procedure PrepareBitmap(const AWidth, AHeight: Integer); property AbsoluteItemsList: TdxMasterViewList read FAbsoluteItems; property Bitmap: TBitmap read FBitmap write FBitmap; property SelectedItemsList: TdxMasterViewList read FSelectedItems; procedure ApplyBestFit(Level: TdxMasterViewLevel; Column: TdxMasterViewColumn); procedure BeginLayout; procedure BeginSelection; procedure BeginUpdate; procedure CancelLayout; procedure CancelUpdate; procedure ClearSelection; function CreateStyle(AStyleClass: TdxMasterViewStyleClass): TdxMasterViewStyle; procedure DeleteSelection; procedure EndLayout; procedure EndSelection; procedure EndUpdate; procedure FocusNode(Node: TdxMasterViewNode; ChangeSelection: Boolean); procedure FullCollapse; procedure FullExpand; function GetHitTestInfo(const P: TPoint; var Node: TdxMasterViewNode; var Column: TdxMasterViewColumn; var RowIndex, ColIndex: Integer): TdxMasterViewHitTestCode; procedure LoadFromRegIniFile(const AFileName: string; AMode: TdxMasterViewRegIniFileMode); function NodeFromID(ALevel: TdxMasterViewLevel; const AID: Variant): TdxMasterViewNode; function NodeFromKeyValue(ALevel: TdxMasterViewLevel; AChildIndex: Integer; const AKeyValue: Variant): TdxMasterViewNode; procedure SaveToRegIniFile(const AFileName: string; AMode: TdxMasterViewRegIniFileMode); procedure SelectItems(FromIndex, ToIndex: Integer; Select: Boolean); procedure ShowFocusedNode; procedure UpdateData; property AbsoluteItemCount: Integer read GetAbsoluteItemCount; property AbsoluteItems[Index: Integer]: TdxMasterViewNode read GetAbsoluteItem; property AbsoluteLevelCount: Integer read GetAbsoluteLevelCount; property AbsoluteLevels[Index: Integer]: TdxMasterViewLevel read GetAbsoluteLevel; property CanUseSmartReload: Boolean write SetCanUseSmartReload; property CustomizationForm: TWinControl read FCustomizationForm; property CustomizationFormLevel: TdxMasterViewLevel read GetCustomizationFormLevel write SetCustomizationFormLevel; property CustomizationFormPos: TPoint read FCustomizationFormPos write FCustomizationFormPos; property Customizing: Boolean read FCustomizing write SetCustomizing; property FocusedIndex: Integer read GetFocusedIndex write SetFocusedIndex; property FocusedNode: TdxMasterViewNode read FFocusedNode write SetFocusedNode; property Items: TdxMasterViewNode read FItems; property LeftPos: Integer read FLeftPos write SetLeftPos; property Levels: TdxMasterViewLevel read FLevels; property MovingColumn: TdxMasterViewColumn read FMovingColumn; property MovingColumnNode: TdxMasterViewNode read FMovingColumnNode; property PartVisibleItemCount: Integer read GetPartVisibleItemCount; property SelectedItemCount: Integer read GetSelectedItemCount; property SelectedItems[Index: Integer]: TdxMasterViewNode read GetSelectedItem; property State: TdxMasterViewStates read FState; property StyleCount: Integer read GetStyleCount; property Styles[Index: Integer]: TdxMasterViewStyle read GetStyle; property TopIndex: Integer read GetTopIndex write SetTopIndex; property TopNode: TdxMasterViewNode read FTopNode write SetTopNode; property SizingColumn: TdxMasterViewColumn read FSizingColumn; property VisibleItemCount: Integer read GetVisibleItemCount; published property Align; property Color; property DragCursor; property DragMode; property Enabled; property Font; property ParentColor default False; property ParentFont; property ParentShowHint; property PopupMenu; property ShowHint; property TabOrder; property TabStop default True; property Visible; {$IFDEF DELPHI4} property Anchors; property Constraints; property DragKind; {$ENDIF} property ArrowsColor: TColor read FArrowsColor write FArrowsColor default clLime; property CustomizationFormRowCount: Integer read FCustomizationFormRowCount write SetCustomizationFormRowCount default dxMVCustomizationFormRowCount; property HighlightStyle: TdxMasterViewStyle read FHighlightStyle write SetHighlightStyle; property InactiveStyle: TdxMasterViewStyle read FInactiveStyle write SetInactiveStyle; property IniFileName: string read FIniFileName write FIniFileName; property OptionsBehavior: TdxMasterViewOptionsBehavior read FOptionsBehavior write SetOptionsBehavior default [mobUseIndent]; property OptionsDB: TdxMasterViewOptionsDB read FOptionsDB write SetOptionsDB default [modSyncMove]; property OptionsView: TdxMasterViewOptionsView read FOptionsView write SetOptionsView default [movAnimation, movHideFocusRect, movKeepColumnWidths, movTransparentDragAndDrop, movUseBitmapToDrawPreview]; property RegistryPath: string read FRegistryPath write FRegistryPath; property ScrollBars: TdxMVScrollBars read FScrollBars write SetScrollBars default sbAuto; property OnClick; property OnDblClick; property OnDragDrop; property OnDragOver; property OnEndDrag; property OnEnter; property OnExit; property OnKeyDown; property OnKeyPress; property OnKeyUp; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnStartDrag; {$IFDEF DELPHI4} property OnEndDock; property OnStartDock; {$ENDIF} {$IFDEF DELPHI5} property OnContextPopup; {$ENDIF} property OnAfterColumnMoving: TdxMVColumnMovingEvent read FOnAfterColumnMoving write FOnAfterColumnMoving; //property OnAfterUpdateData: TdxMVLevelEvent read FOnAfterUpdateData write FOnAfterUpdateData; property OnBeforeColumnMoving: TdxMVColumnMovingEvent read FOnBeforeColumnMoving write FOnBeforeColumnMoving; property OnBeforeFocusNode: TdxMVSelectNodeEvent read FOnBeforeFocusNode write FOnBeforeFocusNode; //property OnBeforeUpdateData: TdxMVLevelEvent read FOnBeforeUpdateData // write FOnBeforeUpdateData; property OnColumnMoving: TdxMVColumnMovingEvent read FOnColumnMoving write FOnColumnMoving; property OnCustomizing: TdxMVCustomizingEvent read FOnCustomizing write FOnCustomizing; property OnFocusNode: TdxMVFocusNodeEvent read FOnFocusNode write FOnFocusNode; property OnLeftPosChanged: TNotifyEvent read FOnLeftPosChanged write FOnLeftPosChanged; property OnSelectNode: TdxMVSelectNodeEvent read FOnSelectNode write FOnSelectNode; property OnSelectionChanged: TNotifyEvent read FOnSelectionChanged write FOnSelectionChanged; property OnTopNodeChanged: TNotifyEvent read FOnTopNodeChanged write FOnTopNodeChanged; end; function DragDetect(Wnd: HWND): TdxMasterViewDragDetect; var dxMVDesigner: TdxMVDesigner; dxMVGroupByBoxText: string; function dxMasterViewColumnClass(Index: Integer): TdxMasterViewColumnClass; function dxMasterViewColumnCount: Integer; procedure dxMasterViewRegisterColumn(AColumnClass: TdxMasterViewColumnClass); procedure dxMasterViewUnregisterColumn(AColumnClass: TdxMasterViewColumnClass); procedure TransparentDraw(const DrawDC: HDC; var FullRect: TRect; const Alignment: TAlignment; const Brush: HBRUSH; const ABitmap: TBitmap); implementation {$R dxMasterView.res} uses Consts,{$IFDEF DELPHI6} RTLConsts,{$ENDIF} Buttons, SysUtils, StdCtrls, ExtCtrls, CheckLst, IniFiles, Registry, dxMasterViewStrs; const MaxExtended = 1.1E4932; MinExtended = -MaxExtended; crdxMasterViewMirror = 1501; crdxMasterViewFullScroll = 1502; crdxMasterViewHorScroll = 1503; crdxMasterViewVerScroll = 1504; crdxMasterViewUpScroll = 1505; crdxMasterViewRightScroll = 1506; crdxMasterViewDownScroll = 1507; crdxMasterViewLeftScroll = 1508; crdxMasterViewHorSize = 1509; crdxMasterViewVerSize = 1512; crdxMasterViewRemove = 1510; crdxMasterViewNoDrop = 1511; DefaultLineWidth = 1; ExpandButtonIndent = 3; ExpandButtonSize = 12; LevelIndent = ExpandButtonIndent + ExpandButtonSize + ExpandButtonIndent; ColumnSizingZoneWidth = 4; InsertZoneWidth = 10; HScrollDelta = 10; ScrollTimeStep = 70; ScrollLeftTimerId = 1; ScrollUpTimerId = 2; ScrollRightTimerId = 3; ScrollDownTimerId = 4; ColumnMovingHScrollZone = 15; AnimationOriginalStep = 1; AnimationIncreaseStepDivisor1 = 100; // collapse (TopIndex = PrevTopIndex) AnimationIncreaseStepDivisor2 = 40; // collapse (TopIndex <> PrevTopIndex) AnimationIncreaseStepDivisor3 = 100; // expand SortOrderMarkZoneWidth = 16; SortOrderMarkWidth = 8; SortOrderMarkHeight = 7; GroupByBoxLeftOffset = 8; GroupByBoxTopOffset = 8; GroupByBoxHorOffset = 4; GroupByBoxVerOffset = 0; GroupByBoxColumnWidth = 100; GroupByBoxLineColor = clBtnText; LeftHitTests = [htLeftSpace, htIndent, htExpandButton]; HeaderHitTests = [htHeader..htHeaderBottomEdge]; ContentHitTests = [htContent, htContentLeftEdge, htContentRightEdge]; type TDummyDataSet = class(TDataSet); PRegColumnRecord = ^TRegColumnRecord; TRegColumnRecord = record ColumnClass: TdxMasterViewColumnClass; end; var RegColumnList: TList; function dxMasterViewColumnClass(Index: Integer): TdxMasterViewColumnClass; begin Result := PRegColumnRecord(RegColumnList[Index])^.ColumnClass; end; function dxMasterViewColumnCount: Integer; begin Result := RegColumnList.Count; end; procedure dxMasterViewRegisterColumn(AColumnClass: TdxMasterViewColumnClass); var RegColumnRecord: PRegColumnRecord; begin New(RegColumnRecord); with RegColumnRecord^ do begin ColumnClass := AColumnClass; end; RegColumnList.Add(RegColumnRecord); RegisterClass(AColumnClass); end; procedure dxMasterViewUnregisterColumn(AColumnClass: TdxMasterViewColumnClass); var I: Integer; begin for I := 0 to dxMasterViewColumnCount - 1 do if dxMasterViewColumnClass(I) = AColumnClass then begin Dispose(PRegColumnRecord(RegColumnList[I])); RegColumnList.Delete(I); UnregisterClass(AColumnClass); Break; end; end; function DragDetect(Wnd: HWND): TdxMasterViewDragDetect; var NoDragZone: TRect; P: TPoint; Msg: TMsg; begin Result := ddCancel; GetCursorPos(P); with NoDragZone, P do begin Right := 2 * GetSystemMetrics(SM_CXDRAG); Bottom := 2 * GetSystemMetrics(SM_CYDRAG); Left := X - Right div 2; Top := Y - Bottom div 2; Inc(Right, Left); Inc(Bottom, Top); end; SetCapture(Wnd); try while GetCapture = Wnd do begin case Integer(GetMessage(Msg, 0, 0, 0)) of -1: Break; 0: begin PostQuitMessage(Msg.wParam); Break; end; end; try case Msg.message of WM_KEYDOWN, WM_KEYUP: if Msg.wParam = VK_ESCAPE then Break; WM_MOUSEMOVE: begin P := SmallPointToPoint(TSmallPoint(Msg.lParam)); ClientToScreen(Msg.hwnd, P); if not PtInRect(NoDragZone, P) then begin Result := ddDrag; Break; end; end; WM_LBUTTONUP: begin Result := ddNone; Break; end; end; finally TranslateMessage(Msg); DispatchMessage(Msg); end; end; finally if GetCapture = Wnd then ReleaseCapture; end; end; procedure CalcFontSize(AFont: TFont; var Size: TSize); var DC: HDC; PrevFont: HFONT; begin DC := GetDC(0); PrevFont := SelectObject(DC, AFont.Handle); GetTextExtentPoint{32}(DC, 'Qq', 2, Size); SelectObject(DC, PrevFont); ReleaseDC(0, DC); Size.cx := Size.cx div 2; end; procedure DrawExpandButton(DC: HDC; const ARect: TRect; BkBrush: HBRUSH; Expanded, Enabled: Boolean); var X, Y: Integer; R: TRect; CrossBrush: HBRUSH; procedure DrawCross; begin FillRect(DC, Rect(X - 2, Y, X + 3, Y + 1), CrossBrush); if not Expanded then FillRect(DC, Rect(X, Y - 2, X + 1, Y + 3), CrossBrush); end; begin with ARect do begin X := (Left + Right - ExpandButtonSize) div 2; Y := (Top + Bottom - ExpandButtonSize) div 2; FillRect(DC, Rect(Left, Top, Right, Y), BkBrush); FillRect(DC, Rect(Left, Y + ExpandButtonSize, Right, Bottom), BkBrush); FillRect(DC, Rect(Left, Y, X, Y + ExpandButtonSize), BkBrush); FillRect(DC, Rect(X + ExpandButtonSize, Y, Right, Y + ExpandButtonSize), BkBrush); end; with R do begin Left := X; Top := Y; Right := Left + ExpandButtonSize; Bottom := Top + ExpandButtonSize; DrawEdge(DC, R, BDR_RAISEDINNER, BF_LEFT or BF_TOP); DrawEdge(DC, R, BDR_RAISEDOUTER, BF_RIGHT or BF_BOTTOM); InflateRect(R, -1, -1); DrawEdge(DC, R, BDR_RAISEDINNER, BF_RIGHT or BF_BOTTOM); Dec(Right); Dec(Bottom); FillRect(DC, R, COLOR_BTNFACE + 1); X := (Left + Right - 1) div 2; Y := (Top + Bottom - 1) div 2; if Enabled then begin CrossBrush := COLOR_BTNTEXT + 1; DrawCross; end else begin Inc(X); Inc(Y); CrossBrush := COLOR_BTNHIGHLIGHT + 1; DrawCross; Dec(X); Dec(Y); CrossBrush := COLOR_BTNSHADOW + 1; DrawCross; end; end; end; procedure TransparentDraw(const DrawDC: HDC; var FullRect: TRect; const Alignment: TAlignment; const Brush: HBRUSH; const ABitmap: TBitmap); const ROP_DSPDxax = $00E20746; var BW, BH, X, Y: Integer; DC, MaskDC: HDC; B, MaskB: HBITMAP; ATextColor, ABackColor: COLORREF; ABrush: HBRUSH; begin with FullRect do begin BW := ABitmap.Width; BH := ABitmap.Height; case Alignment of taLeftJustify: X := Left; taRightJustify: X := Right - BW; else X := (Left + Right - BW) div 2; end; if X < Left then X := Left; if X + BW > Right then BW := Right - X; case Alignment of taLeftJustify: Left := X + BW; taRightJustify: Right := X; end; Y := (Top + Bottom - BH) div 2; if Y < Top then Y := Top; if Y + BH > Bottom then BH := Bottom - Y; DC := CreateCompatibleDC(DrawDC); B := CreateCompatibleBitmap(DrawDC, BW, BH); B := SelectObject(DC, B); try BitBlt(DC, 0, 0, BW, BH, ABitmap.Canvas.Handle, 0, 0, SRCCOPY); MaskDC := CreateCompatibleDC(DrawDC); MaskB := CreateBitmap(BW, BH, 1, 1, nil); MaskB := SelectObject(MaskDC, MaskB); try ABackColor := SetBkColor(DC, ColorToRGB(ABitmap.TransparentColor)); BitBlt(MaskDC, 0, 0, BW, BH, DC, 0, 0, SRCCOPY); ATextColor := SetTextColor(DC, 0); SetBkColor(DC, $FFFFFF); ABrush := SelectObject(DC, Brush); BitBlt(DC, 0, 0, BW, BH, MaskDC, 0, 0, ROP_DSPDxax); SelectObject(DC, ABrush); SetTextColor(DC, ATextColor); SetBkColor(DC, ABackColor); finally DeleteObject(SelectObject(MaskDC, MaskB)); DeleteDC(MaskDC); end; BitBlt(DrawDC, X, Y, BW, BH, DC, 0, 0, SRCCOPY); finally DeleteObject(SelectObject(DC, B)); DeleteDC(DC); end; end; end; procedure DrawDowned(DC: HDC; const R: TRect); begin with R do BitBlt(DC, Left, Top, Right - Left, Bottom - Top, 0, 0, 0, DSTINVERT); end; procedure ProcessPaintMessages(Wnd: HWND); const MessageCount = 4; Messages: array[0..MessageCount - 1] of UINT = (WM_NCCALCSIZE, WM_NCPAINT, WM_ERASEBKGND, WM_PAINT); var Exists: Boolean; I: Integer; Msg: TMsg; begin repeat for I := 0 to MessageCount - 1 do begin Exists := PeekMessage(Msg, Wnd, Messages[I], Messages[I], PM_REMOVE); if Exists then begin DispatchMessage(Msg); Break; end; end; until not Exists; UpdateWindow(Wnd); end; function IsBetween(N, N1, N2: TdxMasterViewNode): Boolean; var I, M1, M2: Integer; begin if (N1 = nil) or (N2 = nil) then Result := False else begin I := N.AbsoluteIndex; M1 := N1.AbsoluteIndex; M2 := N2.AbsoluteIndex; if M1 < M2 then Result := (M1 <= I) and (I <= M2) else Result := (M2 <= I) and (I <= M1); end; end; function VarAnsiUpperCase(const V: Variant): Variant; begin if VarIsNull(V) or VarIsEmpty(V) then Result := V else Result := AnsiUpperCase(V); end; function VarAreEqual(const V1, V2: Variant): Boolean; var I: Integer; begin if not VarIsArray(V1) then if VarIsEmpty(V1) or VarIsEmpty(V2) then Result := False else try Result := V1 = V2 except Result := False end else begin Result := True; for I := 0 to VarArrayHighBound(V1, 1) do try if V1[I] <> V2[I] then begin Result := False; Break; end; except Result := False; Break; end; end; end; function VarCompare(const V1, V2: Variant): Integer; var I: Integer; begin if VarIsArray(V1) and VarIsArray(V2) then begin Result := 0; for I := 0 to VarArrayHighBound(V1, 1) do begin if V1[I] < V2[I] then Result := -1 else if V1[I] = V2[I] then Continue else Result := 1; Break; end; end else if VarIsArray(V1) then Result := 1 else if VarIsArray(V2) then Result := -1 else if VarIsEmpty(V1) then if VarIsEmpty(V2) then Result := 0 else Result := -1 else if VarIsEmpty(V2) then Result := 1 else if V1 < V2 then Result := -1 else if V1 = V2 then Result := 0 else Result := 1 end; { TdxMVRegIniFile } type TCurIniFile = {$IFDEF DELPHI4}TMemIniFile{$ELSE}TIniFile{$ENDIF}; TdxMVRegIniFile = class private FIniFile: TCurIniFile; FMode: TdxMasterViewRegIniFileMode; FRegIniFile: TRegIniFile; public constructor Create(AFileName: string; AMode: TdxMasterViewRegIniFileMode); destructor Destroy; override; function ReadBool(const Section, Ident: string; Default: Boolean): Boolean; function ReadInteger(const Section, Ident: string; Default: Longint): Longint; function SectionExists(const Section: string): Boolean; function ValueExists(const Section, Ident: string): Boolean; procedure WriteBool(const Section, Ident: string; Value: Boolean); procedure WriteInteger(const Section, Ident: string; Value: Longint); end; constructor TdxMVRegIniFile.Create(AFileName: string; AMode: TdxMasterViewRegIniFileMode); begin inherited Create; if FMode = riRegistry then begin if AFileName[1] <> '\' then AFileName := '\' + AFileName; if AFileName[Length(AFileName)] = '\' then Delete(AFileName, Length(AFileName), 1); end; FMode := AMode; case FMode of riIniFile: FIniFile := TCurIniFile.Create(AFileName); riRegistry: FRegIniFile := TRegIniFile.Create(AFileName); end; end; destructor TdxMVRegIniFile.Destroy; begin case FMode of riIniFile: begin {$IFDEF DELPHI4} FIniFile.UpdateFile; {$ENDIF} FIniFile.Free; end; riRegistry: FRegIniFile.Free; end; inherited; end; function TdxMVRegIniFile.ReadBool(const Section, Ident: string; Default: Boolean): Boolean; begin Result := Default; case FMode of riIniFile: Result := FIniFile.ReadBool(Section, Ident, Default); riRegistry: Result := FRegIniFile.ReadBool(Section, Ident, Default); end; end; function TdxMVRegIniFile.ReadInteger(const Section, Ident: string; Default: Longint): Longint; begin Result := Default; case FMode of riIniFile: Result := FIniFile.ReadInteger(Section, Ident, Default); riRegistry: Result := FRegIniFile.ReadInteger(Section, Ident, Default); end; end; function TdxMVRegIniFile.SectionExists(const Section: string): Boolean; var S: TStrings; begin S := TStringList.Create; try case FMode of riIniFile: FIniFile.ReadSection(Section, S); riRegistry: FRegIniFile.ReadSection(Section, S); end; finally Result := S.Count <> 0; S.Free; end; end; function TdxMVRegIniFile.ValueExists(const Section, Ident: string): Boolean; var S: TStrings; begin S := TStringList.Create; try case FMode of riIniFile: FIniFile.ReadSection(Section, S); riRegistry: FRegIniFile.ReadSection(Section, S); end; finally Result := S.IndexOf(Ident) <> -1; S.Free; end; end; procedure TdxMVRegIniFile.WriteBool(const Section, Ident: string; Value: Boolean); begin case FMode of riIniFile: FIniFile.WriteBool(Section, Ident, Value); riRegistry: FRegIniFile.WriteBool(Section, Ident, Value); end; end; procedure TdxMVRegIniFile.WriteInteger(const Section, Ident: string; Value: Longint); begin case FMode of riIniFile: FIniFile.WriteInteger(Section, Ident, Value); riRegistry: FRegIniFile.WriteInteger(Section, Ident, Value); end; end; { TFlatComboBox } type TFlatComboBox = class(TComboBox) private procedure WMPaint(var Message: TWMPaint); message WM_PAINT; protected procedure CreateParams(var Params: TCreateParams); override; public constructor Create(AOwner: TComponent); override; end; constructor TFlatComboBox.Create(AOwner: TComponent); begin inherited; Style := csDropDownList; end; procedure TFlatComboBox.WMPaint(var Message: TWMPaint); const Borders: array[Boolean] of Integer = (BDR_RAISEDINNER, BDR_SUNKENOUTER); Pushes: array[Boolean] of Integer = (0, DFCS_PUSHED); var DC: HDC; R: TRect; begin inherited; DC := GetDC(Handle); R := ClientRect; InflateRect(R, -1, -1); FrameRect(DC, R, GetSysColorBrush(COLOR_BTNFACE)); InflateRect(R, -1, -1); with R do begin Left := Right - GetSystemMetrics(SM_CXVSCROLL); FillRect(DC, Rect(Left, Top, Left + 1, Bottom), COLOR_BTNFACE + 1); Inc(Left); end; DrawEdge(DC, R, Borders[DroppedDown], BF_RECT); InflateRect(R, -1, -1); with R do IntersectClipRect(DC, Left, Top, Right, Bottom); InflateRect(R, 2, 2); DrawFrameControl(DC, R, DFC_SCROLL, DFCS_SCROLLCOMBOBOX or DFCS_FLAT or Pushes[DroppedDown]); ReleaseDC(Handle, DC); end; procedure TFlatComboBox.CreateParams(var Params: TCreateParams); begin inherited; with Params do begin Style := WS_CHILD or CBS_DROPDOWNLIST; ExStyle := 0; end; end; { TChangePageButton } type TChangePageButton = class(TSpeedButton) private procedure WMCancelMode(var Message: TWMCancelMode); message WM_CANCELMODE; protected procedure Paint; override; end; procedure TChangePageButton.WMCancelMode(var Message: TWMCancelMode); begin inherited; if not Down then begin FState := bsUp; Invalidate; end; end; procedure TChangePageButton.Paint; var DC: HDC; R: TRect; PrevFont: HFONT; begin if FState = bsUp then begin DC := Canvas.Handle; R := ClientRect; DrawEdge(DC, R, BDR_RAISEDINNER, BF_RECT); InflateRect(R, -1, -1); FillRect(DC, R, COLOR_BTNFACE + 1); SetBkMode(DC, Windows.TRANSPARENT); PrevFont := SelectObject(DC, Font.Handle); DrawText(DC, PChar(Caption), Length(Caption), R, DT_SINGLELINE or DT_CENTER or DT_VCENTER); SelectObject(DC, PrevFont); SetBkMode(DC, OPAQUE); end else inherited; end; { TColumnsListBox } type TColumnsListBox = class(TListBox) private procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE; procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT; procedure WMPaint(var Message: TWMPaint); message WM_PAINT; procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM; protected procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; public Control: TdxMasterView; constructor Create(AOwner: TComponent); override; end; constructor TColumnsListBox.Create(AOwner: TComponent); begin inherited; BorderStyle := bsNone; Color := clBtnFace; Sorted := True; Style := lbOwnerDrawFixed; end; procedure TColumnsListBox.WMNCCalcSize(var Message: TWMNCCalcSize); begin inherited; InflateRect(Message.CalcSize_Params^.rgrc[0], -1, -1); end; procedure TColumnsListBox.WMNCPaint(var Message: TWMNCPaint); var R: TRect; DC: HDC; begin inherited; GetWindowRect(Handle, R); OffsetRect(R, -R.Left, -R.Top); DC := GetWindowDC(Handle); DrawEdge(DC, R, BDR_SUNKENOUTER, BF_RECT); ReleaseDC(Handle, DC); end; procedure TColumnsListBox.WMPaint(var Message: TWMPaint); var DC: HDC; PS: TPaintStruct; S: string; R: TRect; PrevTextColor: COLORREF; PrevBkMode: Integer; PrevFont: HFONT; begin if Items.Count = 0 then begin if Message.DC = 0 then DC := BeginPaint(Handle, PS) else DC := Message.DC; R := ClientRect; S := LoadStr(dxsMVCBNoFields); PrevTextColor := SetTextColor(DC, GetSysColor(COLOR_BTNSHADOW)); PrevBkMode := SetBkMode(DC, TRANSPARENT); PrevFont := SelectObject(DC, Font.Handle); Windows.DrawText(DC, PChar(S), Length(S), R, DT_SINGLELINE or DT_CENTER or DT_VCENTER); SelectObject(DC, PrevFont); SetBkMode(DC, PrevBkMode); SetTextColor(DC, PrevTextColor); if Message.DC = 0 then EndPaint(Handle, PS); end else inherited; end; procedure TColumnsListBox.CNDrawItem(var Message: TWMDrawItem); var R: TRect; S: string; PrevTextColor: COLORREF; PrevBkMode: Integer; begin with Message.DrawItemStruct^ do if Integer(itemID) >= 0 then begin R := rcItem; DrawEdge(hDC, R, BDR_RAISEDINNER, BF_RECT); InflateRect(R, -1, -1); FillRect(hDC, R, COLOR_BTNFACE + 1); InflateRect(R, -2, 0); S := TdxMasterViewColumn(itemData).Caption; PrevTextColor := SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT)); PrevBkMode := SetBkMode(hDC, TRANSPARENT); Windows.DrawText(hDC, PChar(S), Length(S), R, DT_SINGLELINE or DT_VCENTER or DT_END_ELLIPSIS); SetBkMode(hDC, PrevBkMode); SetTextColor(hDC, PrevTextColor); if itemState and ODS_SELECTED <> 0 then with rcItem do BitBlt(hDC, Left, Top, Right - Left, Bottom - Top, 0, 0, 0, DSTINVERT); end; end; procedure TColumnsListBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited; if (Button = mbLeft) and (ItemAtPos(Point(X, Y), True) <> -1) and (DragDetect(Handle) = ddDrag) then Control.DoColumnMoving(nil, TdxMasterViewColumn(Items.Objects[ItemIndex]), Point(-1, -1), dsCustomizingForm); end; { TOptionsListBox } type TOptionsListBox = class(TCheckListBox) private procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE; procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT; protected procedure ClickCheck; override; public Level: TdxMasterViewLevel; constructor Create(AOwner: TComponent); override; end; constructor TOptionsListBox.Create(AOwner: TComponent); begin inherited; BorderStyle := bsNone; Color := clBtnFace; end; procedure TOptionsListBox.WMNCCalcSize(var Message: TWMNCCalcSize); begin inherited; InflateRect(Message.CalcSize_Params^.rgrc[0], -2, -2); end; procedure TOptionsListBox.WMNCPaint(var Message: TWMNCPaint); var R: TRect; DC: HDC; begin inherited; GetWindowRect(Handle, R); OffsetRect(R, -R.Left, -R.Top); DC := GetWindowDC(Handle); DrawEdge(DC, R, EDGE_ETCHED, BF_RECT); ReleaseDC(Handle, DC); end; procedure TOptionsListBox.ClickCheck; var S: string; I: Integer; procedure XorOptionsView(Option: TdxMasterViewLevelOptionView); begin with Level do if Option in OptionsView then OptionsView := OptionsView - [Option] else OptionsView := OptionsView + [Option]; end; procedure XorOptionsHeader(Option: TdxMasterViewLevelOptionHeader); begin with Level do if Option in OptionsHeader then OptionsHeader := OptionsHeader - [Option] else OptionsHeader := OptionsHeader + [Option]; end; begin inherited; S := Items[ItemIndex]; for I := dxsMVCBOptionCaption to dxsMVCBOptionViewMode do if S = LoadStr(I) then Break; with Level do case I - dxsMVBase of 08: XorOptionsView(lovCaption); 09: XorOptionsView(lovFooter); 10: XorOptionsView(lovGrid); 11: XorOptionsView(lovGridWithPreview); 12: XorOptionsView(lovGroupByBox); 13: XorOptionsView(lovHeader); 14: XorOptionsHeader(lohForFirstNode); 15: XorOptionsHeader(lohForFirstVisibleNode); 16: XorOptionsHeader(lohAfterExpandedNode); 17: XorOptionsView(lovOccupyRestSpace); 18: XorOptionsView(lovPreview); 19: if ViewMode = vmHorizontal then ViewMode := vmVertical else ViewMode := vmHorizontal; end; end; { TdxMVCustomizationForm } type TdxMVCustomizationForm = class(TWinControl) private FActiveLevel: TdxMasterViewLevel; ListBoxTop, ListBoxBottom: Integer; procedure SetActiveLevel(Value: TdxMasterViewLevel); procedure cmbLevelsClick(Sender: TObject); procedure btnChangePageClick(Sender: TObject); procedure WMActivate(var Message: TWMActivate); message WM_ACTIVATE; procedure WMClose(var Message: TWMClose); message WM_CLOSE; procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND; procedure WMMove(var Message: TWMMove); message WM_MOVE; procedure WMNCCreate(var Message: TWMNCCreate); message WM_NCCREATE; protected procedure CreateParams(var Params: TCreateParams); override; public cmbLevels: TComboBox; pnlChangePage: TPanel; btnColumns, btnOptions: TChangePageButton; lbColumns: TColumnsListBox; clbOptions: TOptionsListBox; Control: TdxMasterView; constructor Create(AOwner: TComponent); override; destructor Destroy; override; function MouseInBounds(P: TPoint): Boolean; procedure RefreshColumnsListBox(FocusedColumn: TdxMasterViewColumn); procedure RefreshOptions; property ActiveLevel: TdxMasterViewLevel read FActiveLevel write SetActiveLevel; end; var WndProcHandlers: TList; WndProcHookHandle: HHOOK; function dxMVCustFormWndProcHook(Code: Integer; wParam: WParam; lParam: LParam): LRESULT; stdcall; const Flags: array[Boolean] of Integer = (SWP_HIDEWINDOW, SWP_SHOWWINDOW); var I: Integer; Wnd, ParentWnd: HWND; function GetParentFormHandle(Wnd: HWND): HWND; begin Result := Wnd; while not (FindControl(Result) is TCustomForm) and (GetParent(Result) <> 0) do Result := GetParent(Result); end; begin with PCWPStruct(lParam)^ do if message = WM_ACTIVATE then for I := 0 to WndProcHandlers.Count - 1 do begin Wnd := TWinControl(WndProcHandlers[I]).Handle; ParentWnd := GetParentFormHandle(Wnd); if (hwnd = ParentWnd) and (Windows.HWND(lParam) <> Wnd) or (hwnd = Wnd) and (Windows.HWND(lParam) <> ParentWnd) then SetWindowPos(Wnd, 0, 0, 0, 0, 0, SWP_NOZORDER or SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or Flags[LOWORD(wParam) <> WA_INACTIVE]); end; Result := CallNextHookEx(WndProcHookHandle, Code, wParam, lParam); end; procedure RegisterWndProcHandler(CustForm: TWinControl); begin WndProcHandlers.Add(CustForm); if WndProcHookHandle = 0 then WndProcHookHandle := SetWindowsHookEx(WH_CALLWNDPROC, dxMVCustFormWndProcHook, 0, GetCurrentThreadId); end; procedure UnregisterWndProcHandler(CustForm: TWinControl); begin WndProcHandlers.Remove(CustForm); if (WndProcHandlers.Count = 0) and (WndProcHookHandle <> 0) then begin UnhookWindowsHookEx(WndProcHookHandle); WndProcHookHandle := 0; end; end; constructor TdxMVCustomizationForm.Create(AOwner: TComponent); var Size: TPoint; D, W, T, I: Integer; Level: TdxMasterViewLevel; R: TRect; function GetSymbolSize: TPoint; var DC: HDC; PrevFont: HFONT; begin DC := GetDC(0); PrevFont := SelectObject(DC, Font.Handle); GetTextExtentPoint{32}(DC, '0', 1, TSize(Result)); SelectObject(DC, PrevFont); ReleaseDC(0, DC); end; begin Control := TdxMasterView(AOwner); inherited; Visible := False; Caption := LoadStr(dxsMVCBCaption); Font := Control.Font; Size := GetSymbolSize; D := Size.Y div 6; W := 32 * Size.X; T := D; cmbLevels := TFlatComboBox.Create(Self); with cmbLevels do begin Parent := Self; for I := 0 to Control.AbsoluteLevelCount - 1 do begin Level := Control.AbsoluteLevels[I]; if Level.OptionsCustomizeBox <> [] then Items.AddObject(Level.Caption, Level); end; DropDownCount := Items.Count; if Items.Count > 1 then begin SetBounds(D, T, W, 10); OnClick := cmbLevelsClick; ShowWindow(Handle, SW_SHOW); Inc(T, Height + 2 * D); end; end; pnlChangePage := TPanel.Create(Self); with pnlChangePage do begin Parent := Self; BevelOuter := bvNone; SetBounds(D, T, W, 12 * D); btnColumns := TChangePageButton.Create(Self); with btnColumns do begin Caption := LoadStr(dxsMVCBColumns); Flat := True; GroupIndex := 1; Down := True; Parent := pnlChangePage; SetBounds(0, 0, (W - 2 * D) div 2, Parent.Height); OnClick := btnChangePageClick; end; btnOptions := TChangePageButton.Create(Self); with btnOptions do begin Caption := LoadStr(dxsMVCBOptions); Flat := True; GroupIndex := 1; Parent := pnlChangePage; SetBounds(btnColumns.Width + 2 * D, 0, W - btnColumns.Width - 2 * D, Parent.Height); OnClick := btnChangePageClick; end; end; Inc(T, pnlChangePage.Height + 2 * D); ListBoxTop := T; lbColumns := TColumnsListBox.Create(Self); with lbColumns do begin Control := Self.Control; Parent := Self; ItemHeight := Size.Y + 2 + 2 * 2; SetBounds(D, T, W, 2 + Control.CustomizationFormRowCount * ItemHeight); ListBoxBottom := BoundsRect.Bottom; end; clbOptions := TOptionsListBox.Create(Self); with clbOptions do begin Parent := Self; BoundsRect := lbColumns.BoundsRect; end; Width := 100; Height := 100; with lbColumns.BoundsRect do begin ClientWidth := Right + D; ClientHeight := Bottom + D; end; if cmbLevels.Items.Count <> 0 then ActiveLevel := TdxMasterViewLevel(cmbLevels.Items.Objects[0]); if Control.CustomizationFormPos.X = -1 then begin GetWindowRect(Control.Handle, R); Left := R.Right - Width; Top := R.Bottom - Height; end else with Control.CustomizationFormPos do begin Left := X; Top := Y; end; Visible := True; ShowWindow(Handle, SW_SHOW); RegisterWndProcHandler(Self); end; destructor TdxMVCustomizationForm.Destroy; begin UnregisterWndProcHandler(Self); Destroying; Control.Customizing := False; inherited; end; procedure TdxMVCustomizationForm.SetActiveLevel(Value: TdxMasterViewLevel); const Visibilities: array[Boolean] of Integer = (SW_HIDE, SW_SHOW); var ShowColumns, ShowOptions: Boolean; T: Integer; begin if FActiveLevel <> Value then begin FActiveLevel := Value; with cmbLevels do ItemIndex := Items.IndexOfObject(Value); ShowColumns := loxColumns in FActiveLevel.OptionsCustomizeBox; ShowOptions := FActiveLevel.OptionsCustomizeBox - [loxColumns] <> []; ShowWindow(pnlChangePage.Handle, Visibilities[ShowColumns and ShowOptions]); if ShowColumns and ShowOptions then T := ListBoxTop else T := pnlChangePage.BoundsRect.Top; if ShowColumns then begin with lbColumns do SetBounds(Left, T, Width, ListBoxBottom - T); RefreshColumnsListBox(nil); end; if ShowOptions then begin with clbOptions do SetBounds(Left, T, Width, ListBoxBottom - T); RefreshOptions; end; ShowColumns := ShowColumns and (btnColumns.Down or not ShowOptions); ShowOptions := ShowOptions and (btnOptions.Down or not ShowColumns); ShowWindow(lbColumns.Handle, Visibilities[ShowColumns]); ShowWindow(clbOptions.Handle, Visibilities[ShowOptions]); end; end; procedure TdxMVCustomizationForm.cmbLevelsClick(Sender: TObject); begin with cmbLevels do ActiveLevel := TdxMasterViewLevel(Items.Objects[ItemIndex]); end; procedure TdxMVCustomizationForm.btnChangePageClick(Sender: TObject); begin if btnColumns.Down then begin ShowWindow(lbColumns.Handle, SW_SHOW); ShowWindow(clbOptions.Handle, SW_HIDE); end else begin ShowWindow(clbOptions.Handle, SW_SHOW); ShowWindow(lbColumns.Handle, SW_HIDE); end; end; procedure TdxMVCustomizationForm.WMActivate(var Message: TWMActivate); begin inherited; if (Message.Active <> WA_INACTIVE) and (GetFocus = Handle) then Windows.SetFocus(lbColumns.Handle); end; procedure TdxMVCustomizationForm.WMClose(var Message: TWMClose); begin inherited; Free; end; procedure TdxMVCustomizationForm.WMEraseBkgnd(var Message: TWMEraseBkgnd); begin FillRect(Message.DC, ClientRect, COLOR_BTNFACE + 1); Message.Result := 1; end; procedure TdxMVCustomizationForm.WMMove(var Message: TWMMove); var R: TRect; begin inherited; if IsWindowVisible(Handle) then begin GetWindowRect(Handle, R); Control.CustomizationFormPos := R.TopLeft; end; end; procedure TdxMVCustomizationForm.WMNCCreate(var Message: TWMNCCreate); var SysMenu: HMENU; begin inherited; SysMenu := GetSystemMenu(Handle, False); DeleteMenu(SysMenu, SC_MAXIMIZE, MF_BYCOMMAND); DeleteMenu(SysMenu, SC_MINIMIZE, MF_BYCOMMAND); DeleteMenu(SysMenu, SC_SIZE, MF_BYCOMMAND); DeleteMenu(SysMenu, SC_RESTORE, MF_BYCOMMAND); end; procedure TdxMVCustomizationForm.CreateParams(var Params: TCreateParams); begin inherited; with Params do begin Style := WS_POPUP or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or WS_CAPTION or WS_SYSMENU; ExStyle := WS_EX_TOOLWINDOW or WS_EX_DLGMODALFRAME or WS_EX_CONTROLPARENT; WindowClass.Style := CS_SAVEBITS; WndParent := Control.Handle; end; end; function TdxMVCustomizationForm.MouseInBounds(P: TPoint): Boolean; begin Windows.ClientToScreen(Control.Handle, P); Result := PtInRect(BoundsRect, P); end; procedure TdxMVCustomizationForm.RefreshColumnsListBox(FocusedColumn: TdxMasterViewColumn); var PrevFocusedColumn: TObject; I: Integer; AColumn: TdxMasterViewColumn; begin with lbColumns, Items do begin if ItemIndex = -1 then PrevFocusedColumn := nil else PrevFocusedColumn := Items.Objects[ItemIndex]; BeginUpdate; try Clear; for I := 0 to ActiveLevel.ColumnCount - 1 do begin AColumn := ActiveLevel.Columns[I]; if not AColumn.Hidden and not AColumn.Visible then AddObject(AColumn.Caption, AColumn); end; if FocusedColumn <> nil then ItemIndex := IndexOfObject(FocusedColumn) else if PrevFocusedColumn <> nil then ItemIndex := IndexOfObject(PrevFocusedColumn); finally EndUpdate; end; end; end; procedure TdxMVCustomizationForm.RefreshOptions; begin with ActiveLevel, clbOptions, Items do begin Level := ActiveLevel; BeginUpdate; try Clear; if loxViewMode in OptionsCustomizeBox then Checked[Add(LoadStr(dxsMVCBOptionViewMode))] := Horizontal; if loxCaption in OptionsCustomizeBox then Checked[Add(LoadStr(dxsMVCBOptionCaption))] := ShowCaption; if loxGroupByBox in OptionsCustomizeBox then Checked[Add(LoadStr(dxsMVCBOptionGroupByBox))] := ShowGroupByBox; if loxHeader in OptionsCustomizeBox then Checked[Add(LoadStr(dxsMVCBOptionHeader))] := ShowHeader; if loxHeaderOptions in OptionsCustomizeBox then begin Checked[Add(LoadStr(dxsMVCBOptionHeaderForFirstNode))] := lohForFirstNode in OptionsHeader; Checked[Add(LoadStr(dxsMVCBOptionHeaderForFirstVisibleNode))] := lohForFirstVisibleNode in OptionsHeader; Checked[Add(LoadStr(dxsMVCBOptionHeaderAfterExpandedNode))] := lohAfterExpandedNode in OptionsHeader; end; if loxGrid in OptionsCustomizeBox then Checked[Add(LoadStr(dxsMVCBOptionGrid))] := ShowGrid; if loxGridWithPreview in OptionsCustomizeBox then Checked[Add(LoadStr(dxsMVCBOptionGridWithPreview))] := ShowGridWithPreview; if loxPreview in OptionsCustomizeBox then Checked[Add(LoadStr(dxsMVCBOptionPreview))] := ShowPreview; if loxFooter in OptionsCustomizeBox then Checked[Add(LoadStr(dxsMVCBOptionFooter))] := ShowFooter; if loxOccupyRestSpace in OptionsCustomizeBox then Checked[Add(LoadStr(dxsMVCBOptionOccupyRestSpace))] := OccupyRestSpace; finally EndUpdate; end; end; end; { TdxMasterViewDataLink } constructor TdxMasterViewDataLink.Create(ALevel: TdxMasterViewLevel); begin inherited Create; {$IFDEF DELPHI5} VisualControl := True; {$ENDIF} FLevel := ALevel; end; function TdxMasterViewDataLink.GetFrozen: Boolean; begin Result := FFreezeCount > 0; end; procedure TdxMasterViewDataLink.ActiveChanged; begin FLevel.ActiveChanged; end; procedure TdxMasterViewDataLink.DataSetChanged; begin if not Frozen then FLevel.DataChanged else Unfreeze; end; procedure TdxMasterViewDataLink.DataSetScrolled(Distance: Integer); begin if not Frozen then FLevel.SyncPos else Unfreeze; end; procedure TdxMasterViewDataLink.EditingChanged; begin FLevel.EditingChanged; end; procedure TdxMasterViewDataLink.LayoutChanged; begin FLevel.LayoutChanged; inherited; end; procedure TdxMasterViewDataLink.RecordChanged(Field: TField); begin FLevel.RecordChanged(Field); end; function TdxMasterViewDataLink.Freeze: Integer; begin Inc(FFreezeCount); Result := FFreezeCount; end; procedure TdxMasterViewDataLink.Unfreeze; begin if FFreezeCount > 0 then Dec(FFreezeCount); end; { TdxMasterViewStyle } constructor TdxMasterViewStyle.Create(AOwner: TComponent); begin inherited; FAnotherColor := clNone; FColor := clNone; FConsumers := TList.Create; FFont := TFont.Create; FFont.OnChange := FontChanged; end; destructor TdxMasterViewStyle.Destroy; var I: Integer; begin if FControl <> nil then FControl.RemoveStyle(Self); if FBrush <> 0 then DeleteObject(FBrush); if FAnotherBrush <> 0 then DeleteObject(FAnotherBrush); FFont.Free; for I := ConsumerCount - 1 downto 0 do begin Dispose(ConsumerRecs[I]); FConsumers.Delete(I); end; FConsumers.Free; FConsumers := nil; inherited; end; function TdxMasterViewStyle.GetConsumerRec(Index: Integer): PdxMasterViewStyleConsumerRec; begin Result := PdxMasterViewStyleConsumerRec(FConsumers[Index]); end; function TdxMasterViewStyle.GetConsumerCount: Integer; begin Result := FConsumers.Count; end; function TdxMasterViewStyle.GetIndex: Integer; begin Result := FControl.FStyles.IndexOf(Self); end; procedure TdxMasterViewStyle.SetAnotherColor(Value: TColor); begin if FAnotherColor <> Value then begin FAnotherColor := Value; if FAnotherBrush <> 0 then DeleteObject(FAnotherBrush); if FAnotherColor = clNone then begin FAnotherBrush := 0; AssignedValues := AssignedValues - [svAnotherColor]; end else begin FAnotherBrush := CreateSolidBrush(ColorToRGB(FAnotherColor)); if svAnotherColor in AssignedValues then Changed([svAnotherColor]) else AssignedValues := AssignedValues + [svAnotherColor]; end; end; end; procedure TdxMasterViewStyle.SetAssignedValues(Value: TdxMasterViewStyleValues); var PrevValue: TdxMasterViewStyleValues; begin if FAnotherColor = clNone then Exclude(Value, svAnotherColor); if FColor = clNone then Exclude(Value, svColor); if FAssignedValues <> Value then begin PrevValue := FAssignedValues; FAssignedValues := Value; Byte(PrevValue) := Byte(PrevValue) xor Byte(Value); Changed(PrevValue); end; end; procedure TdxMasterViewStyle.SetColor(Value: TColor); begin if FColor <> Value then begin FColor := Value; if FBrush <> 0 then DeleteObject(FBrush); if FColor = clNone then begin FBrush := 0; AssignedValues := AssignedValues - [svColor]; end else begin FBrush := CreateSolidBrush(ColorToRGB(FColor)); if svColor in AssignedValues then Changed([svColor]) else AssignedValues := AssignedValues + [svColor]; end; end; end; procedure TdxMasterViewStyle.SetFont(Value: TFont); begin FFont.Assign(Value); end; procedure TdxMasterViewStyle.SetIndex(Value: Integer); begin if Value < 0 then Value := 0; if Value >= FControl.StyleCount then Value := FControl.StyleCount - 1; if Index <> Value then FControl.FStyles.Move(Index, Value); end; function TdxMasterViewStyle.IsAnotherColorStored: Boolean; begin Result := svAnotherColor in FAssignedValues; end; function TdxMasterViewStyle.IsColorStored: Boolean; begin Result := svColor in FAssignedValues; end; function TdxMasterViewStyle.IsFontStored: Boolean; begin Result := svFont in FAssignedValues; end; procedure TdxMasterViewStyle.FontChanged(Sender: TObject); begin if svFont in AssignedValues then Changed([svFont]) else AssignedValues := AssignedValues + [svFont]; end; procedure TdxMasterViewStyle.SetName(const NewName: TComponentName); begin inherited; if dxMVDesigner <> nil then dxMVDesigner.Changed(FControl, [rcStyles]); end; procedure TdxMasterViewStyle.SetParentComponent(AParent: TComponent); begin if AParent is TdxMasterView then TdxMasterView(AParent).AddStyle(Self); end; procedure TdxMasterViewStyle.AddConsumeType(AConsumer: TComponent; AConsumeType: TdxMasterViewStyleConsumeType); var I: Integer; ConsumerRec: PdxMasterViewStyleConsumerRec; begin I := IndexOfConsumer(AConsumer); if I = -1 then begin New(ConsumerRec); with ConsumerRec^ do begin Consumer := AConsumer; ConsumeTypes := [AConsumeType]; end; FConsumers.Add(ConsumerRec); AConsumer.FreeNotification(Self); end else Include(ConsumerRecs[I]^.ConsumeTypes, AConsumeType); end; procedure TdxMasterViewStyle.Changed(Values: TdxMasterViewStyleValues); var I: Integer; ConsumerRec: PdxMasterViewStyleConsumerRec; begin for I := 0 to ConsumerCount - 1 do begin ConsumerRec := ConsumerRecs[I]; with ConsumerRec^ do if Consumer is TdxMasterViewColumn then with TdxMasterViewColumn(Consumer) do begin if sctHeader in ConsumeTypes then HeaderStyleChanged(Values); if sctContent in ConsumeTypes then ContentStyleChanged(Values); if sctFooter in ConsumeTypes then FooterStyleChanged(Values); end else if Consumer is TdxMasterViewLevel then with TdxMasterViewLevel(Consumer) do begin if sctHeader in ConsumeTypes then HeaderStyleChanged(Values); if sctContent in ConsumeTypes then ContentStyleChanged(Values); if sctFooter in ConsumeTypes then FooterStyleChanged(Values); if sctPreview in ConsumeTypes then PreviewStyleChanged(Values); if sctCaption in ConsumeTypes then CaptionStyleChanged(Values); if sctGroup in ConsumeTypes then GroupStyleChanged(Values); if sctGroupByBox in ConsumeTypes then GroupByBoxStyleChanged(Values); end else if Consumer is TdxMasterView then with TdxMasterView(Consumer) do begin if sctHighlight in ConsumeTypes then HighlightStyleChanged(Values); if sctInactive in ConsumeTypes then InactiveStyleChanged(Values); end; end; end; function TdxMasterViewStyle.IndexOfConsumer(Consumer: TComponent): Integer; begin if FConsumers <> nil then for Result := 0 to ConsumerCount - 1 do if ConsumerRecs[Result]^.Consumer = Consumer then Exit; Result := -1; end; procedure TdxMasterViewStyle.RemoveConsumeType(AConsumer: TComponent; AConsumeType: TdxMasterViewStyleConsumeType); var I: Integer; begin I := IndexOfConsumer(AConsumer); if I = -1 then Exit; with ConsumerRecs[I]^ do begin Exclude(ConsumeTypes, AConsumeType); if ConsumeTypes = [] then begin Dispose(ConsumerRecs[I]); FConsumers.Delete(I); end; end; end; procedure TdxMasterViewStyle.SysColorChanged; procedure RecreateBrush(var ABrush: HBRUSH; AColor: TColor); begin DeleteObject(ABrush); ABrush := CreateSolidBrush(ColorToRGB(AColor)); end; begin if svColor in AssignedValues then RecreateBrush(FBrush, FColor); if svAnotherColor in AssignedValues then RecreateBrush(FAnotherBrush, FAnotherColor); end; procedure TdxMasterViewStyle.Assign(Source: TPersistent); begin if Source is TdxMasterViewStyle then begin AnotherColor := TdxMasterViewStyle(Source).AnotherColor; Color := TdxMasterViewStyle(Source).Color; Font := TdxMasterViewStyle(Source).Font; AssignedValues := TdxMasterViewStyle(Source).AssignedValues; end else inherited; end; function TdxMasterViewStyle.GetParentComponent: TComponent; begin Result := FControl; end; function TdxMasterViewStyle.HasParent: Boolean; begin Result := FControl <> nil; end; { TdxMasterViewColumn } constructor TdxMasterViewColumn.Create(AOwner: TComponent); begin inherited; FColIndex := -1; FHeaderGlyph := TBitmap.Create; FHeaderGlyph.OnChange := HeaderGlyphChanged; FLeftColumns := TList.Create; FLoadedGroupIndex := -1; FLoadedSortIndex := -1; FLogicalOffset := -1; FMinRowCount := 1; FMinWidth := dxMVColumnDefaultMinWidth; FMaxRowCount := dxMVColumnDefaultMaxRowCount; FMaxWidth := dxMVColumnDefaultMaxWidth; FOptions := [coGrouping , coHorSizing, coMoving, coSorting, coVerSizing, coShowCaption]; FRightColumns := TList.Create; FRowIndex := -1; FRowCount := 1; FVisible := True; end; destructor TdxMasterViewColumn.Destroy; begin Destroying; HeaderStyle := nil; ContentStyle := nil; FooterStyle := nil; FLevel.RemoveColumn(Self); FRightColumns.Free; FLeftColumns.Free; FHeaderGlyph.Free; inherited; end; function TdxMasterViewColumn.GetAlignment: TAlignment; begin if cvAlignment in FAssignedValues then Result := FAlignment else Result := DefaultAlignment; end; function TdxMasterViewColumn.GetCaption: string; begin if cvCaption in FAssignedValues then Result := FCaption else Result := DefaultCaption; end; function TdxMasterViewColumn.GetColIndex: Integer; begin if FVisible and (FLevel.ViewMode = vmHorizontal) then Result := FLevel.Layout.IndexOf(Self, FRowIndex) else Result := FColIndex; end; function TdxMasterViewColumn.GetContentAnotherBrush: HBRUSH; begin if (ContentStyle <> nil) and (svAnotherColor in ContentStyle.AssignedValues) then Result := ContentStyle.AnotherBrush else Result := FLevel.ContentAnotherBrush; end; function TdxMasterViewColumn.GetContentAnotherColor: TColor; begin if (ContentStyle <> nil) and (svAnotherColor in ContentStyle.AssignedValues) then Result := ContentStyle.AnotherColor else Result := FLevel.ContentAnotherColor; end; function TdxMasterViewColumn.GetContentBrush: HBRUSH; begin if (ContentStyle <> nil) and (svColor in ContentStyle.AssignedValues) then Result := ContentStyle.Brush else Result := FLevel.ContentBrush; end; function TdxMasterViewColumn.GetContentColor: TColor; begin if (ContentStyle <> nil) and (svColor in ContentStyle.AssignedValues) then Result := ContentStyle.Color else Result := FLevel.ContentColor; end; function TdxMasterViewColumn.GetContentFont: TFont; begin if (ContentStyle <> nil) and (svFont in ContentStyle.AssignedValues) then Result := ContentStyle.Font else Result := FLevel.ContentFont; end; function TdxMasterViewColumn.GetControl: TdxMasterView; begin Result := FLevel.Control; end; function TdxMasterViewColumn.GetFooterAlignment: TAlignment; begin if cvFooterAlignment in FAssignedValues then Result := FFooterAlignment else Result := DefaultAlignment; end; function TdxMasterViewColumn.GetFooterBrush: HBRUSH; begin if (FooterStyle <> nil) and (svColor in FooterStyle.AssignedValues) then Result := FooterStyle.Brush else Result := FLevel.FooterBrush; end; function TdxMasterViewColumn.GetFooterColor: TColor; begin if (FooterStyle <> nil) and (svColor in FooterStyle.AssignedValues) then Result := FooterStyle.Color else Result := FLevel.FooterColor; end; function TdxMasterViewColumn.GetFooterFont: TFont; begin if (FooterStyle <> nil) and (svFont in FooterStyle.AssignedValues) then Result := FooterStyle.Font else Result := FLevel.FooterFont; end; function TdxMasterViewColumn.GetGroupIndex: Integer; begin Result := FLevel.FGroupColumns.IndexOf(Self); end; function TdxMasterViewColumn.GetHeaderAlignment: TAlignment; begin if cvHeaderAlignment in FAssignedValues then Result := FHeaderAlignment else Result := DefaultHeaderAlignment; end; function TdxMasterViewColumn.GetHeaderBrush: HBRUSH; begin if (HeaderStyle <> nil) and (svColor in HeaderStyle.AssignedValues) then Result := HeaderStyle.Brush else Result := FLevel.HeaderBrush; end; function TdxMasterViewColumn.GetHeaderColor: TColor; begin if (HeaderStyle <> nil) and (svColor in HeaderStyle.AssignedValues) then Result := HeaderStyle.Color else Result := FLevel.HeaderColor; end; function TdxMasterViewColumn.GetHeaderFont: TFont; begin if (HeaderStyle <> nil) and (svFont in HeaderStyle.AssignedValues) then Result := HeaderStyle.Font else Result := FLevel.HeaderFont; end; function TdxMasterViewColumn.GetHidden: Boolean; begin Result := coHidden in FOptions; end; function TdxMasterViewColumn.GetIndex: Integer; begin Result := FLevel.FColumns.IndexOf(Self); end; function TdxMasterViewColumn.GetIsDestroying: Boolean; begin Result := csDestroying in ComponentState; end; function TdxMasterViewColumn.GetIsLoading: Boolean; begin Result := (csLoading in ComponentState) or FLevel.IsLoading; end; function TdxMasterViewColumn.GetLastInRow: Boolean; begin Result := ColIndex = FLevel.Layout[RowIndex].Count - 1; end; function TdxMasterViewColumn.GetLogicalOffset: Integer; var List: TList; I, AOffset: Integer; begin if FLogicalOffset = -1 then begin Result := 0; if IsLoading {FLevel} then Exit; if FLevel.Horizontal then begin if FRowIndex = -1 then Exit; List := TList.Create; try if FLevel.Layout.HasLeftNeighbours(Self, List, False) then for I := 0 to List.Count - 1 do begin with TdxMasterViewColumn(List[I]) do AOffset := LogicalOffset + VisibleWidth; if AOffset > Result then Result := AOffset; end; finally List.Free; end; end else with FLevel, Layout do begin if FColIndex = -1 then Exit; if HasExpandButton then Result := LevelIndent; for I := 0 to ColIndex - 1 do Inc(Result, HeaderWidths[I] + ContentWidths[I]); end; end else Result := FLogicalOffset; end; { function TdxMasterViewColumn.GetMaxLogicalOffset: Integer; var List: TList; I, AOffset: Integer; begin Result := 0; if FRowIndex = -1 then Exit; List := TList.Create; try if FLevel.Layout.HasLeftNeighbours(Self, List, True) then for I := 0 to List.Count - 1 do begin with TdxMasterViewColumn(List[I]) do AOffset := MaxLogicalOffset + VisibleMaxWidth; if AOffset > Result then Result := AOffset; end; finally List.Free; end; end; } function TdxMasterViewColumn.GetMinLogicalOffset: Integer; var List: TList; I, AOffset: Integer; begin Result := 0; if FRowIndex = -1 then Exit; List := TList.Create; try if FLevel.Layout.HasLeftNeighbours(Self, List, False) then for I := 0 to List.Count - 1 do begin with TdxMasterViewColumn(List[I]) do AOffset := MinLogicalOffset + VisibleMinWidth; if AOffset > Result then Result := AOffset; end; finally List.Free; end; end; function TdxMasterViewColumn.GetNonScaledLogicalOffset: Integer; var List: TList; I, AOffset: Integer; begin Result := 0; if FLevel.Horizontal then begin if FRowIndex = -1 then Exit; List := TList.Create; try if FLevel.Layout.HasLeftNeighbours(Self, List, False) then for I := 0 to List.Count - 1 do begin with TdxMasterViewColumn(List[I]) do AOffset := NonScaledLogicalOffset + NonScaledWidth; if AOffset > Result then Result := AOffset; end; finally List.Free; end; end else with FLevel, Layout do begin if FColIndex = -1 then Exit; if HasExpandButton then Result := LevelIndent; for I := 0 to ColIndex - 1 do Inc(Result, HeaderWidths[I] + ContentNonScaledWidths[I]); end; end; { function TdxMasterViewColumn.GetNonScaledOffset: Integer; begin Result := FLevel.Indent - Control.LeftPos + NonScaledLogicalOffset; end; } function TdxMasterViewColumn.GetNonScaledWidth: Integer; begin Result := Width + AddInWidth; end; function TdxMasterViewColumn.GetOffset: Integer; begin Result := FLevel.Indent - Control.LeftPos + LogicalOffset; end; function TdxMasterViewColumn.GetRowIndex: Integer; begin if (FLevel.ViewMode = vmVertical) and FVisible then Result := FLevel.Layout.IndexOf(Self, FColIndex) //!!! else Result := FRowIndex; end; function TdxMasterViewColumn.GetSortIndex: Integer; begin Result := FLevel.FSortedColumns.IndexOf(Self); end; function TdxMasterViewColumn.GetSummaryField: TField; begin if cvSummaryFieldName in FAssignedValues then Result := FSummaryField else Result := Field; end; function TdxMasterViewColumn.GetSummaryFieldName: string; begin if cvSummaryFieldName in FAssignedValues then Result := FSummaryFieldName else Result := FieldName; end; function TdxMasterViewColumn.GetSummaryIndex: Integer; begin Result := FLevel.FSummaryColumns.IndexOf(Self); end; function TdxMasterViewColumn.GetVisibleIndex: Integer; begin if FLevel = nil then Result := -1 else Result := FLevel.FVisibleColumns.IndexOf(Self); end; function TdxMasterViewColumn.GetVisibleMaxWidth: Integer; begin if coHorSizing in FOptions then Result := MaxWidth + AddInWidth else Result := VisibleWidth; end; function TdxMasterViewColumn.GetVisibleMinWidth: Integer; begin if coHorSizing in FOptions then Result := MinWidth + AddInWidth else Result := VisibleWidth; end; function TdxMasterViewColumn.GetVisibleWidth: Integer; begin if FVisible and (FVisibleWidth <> 0) then Result := FVisibleWidth else Result := Width; Inc(Result, FAddInWidth); { if Control.AutoColumnWidth and FVisible then Result := FVisibleWidth else Result := Width; Inc(Result, AddInWidth);} end; procedure TdxMasterViewColumn.SetAlignment(Value: TAlignment); begin if (Alignment <> Value) or IsLoading then begin FAlignment := Value; AssignedValues := AssignedValues + [cvAlignment]; ColumnChanged(False, hpOne, vpContent); end; end; procedure TdxMasterViewColumn.SetAssignedValues(Value: TdxMasterViewColumnAssignedValues); var PrevValue: TdxMasterViewColumnAssignedValues; begin if FAssignedValues <> Value then begin PrevValue := FAssignedValues; FAssignedValues := Value; if not (cvCaption in Value) then FCaption := ''; if not (cvSummaryFieldName in Value) then begin FSummaryField := nil; FSummaryFieldName := ''; end; if (cvWidth in Value) and not (cvWidth in PrevValue) and FVisible then FLevel.WidthChanged else if not (cvWidth in Value) and ((cvWidth in PrevValue) or FLevel.ShowHeader and ([cvCaption] * Value <> [cvCaption] * PrevValue)) then FLevel.CalcDefaultWidths(Self); ColumnChanged( (cvSummaryFieldName in PrevValue) and not (cvSummaryFieldName in Value), hpAll, vpAll); // if dxMVDesigner <> nil then dxMVDesigner.Changed(Control, [rcEnables]); end; end; procedure TdxMasterViewColumn.SetCaption(Value: string); const HParts: array[Boolean] of TdxMasterViewHPart = (hpAll, hpOne); begin if Caption <> Value then begin FCaption := Value; if not (cvCaption in FAssignedValues) then AssignedValues := AssignedValues + [cvCaption] else if not (cvWidth in FAssignedValues) and FLevel.ShowHeader then FLevel.CalcDefaultWidths(Self); ColumnChanged(False, HParts[cvWidth in FAssignedValues], vpAll); end; end; procedure TdxMasterViewColumn.SetColIndex(Value: Integer); begin FLevel.Layout.ChangeCol(Self, Value); end; procedure TdxMasterViewColumn.SetContentStyle(Value: TdxMasterViewStyle); begin if FContentStyle <> Value then begin if FContentStyle <> nil then FContentStyle.RemoveConsumeType(Self, sctContent); FContentStyle := Value; if FContentStyle <> nil then FContentStyle.AddConsumeType(Self, sctContent); ContentStyleChanged([]); end; end; procedure TdxMasterViewColumn.SetField(Value: TField); begin if FField <> Value then begin SetFieldInternally(Value); if ([cvWidth, cvCaption] * FAssignedValues = []) {and FLevel.ShowHeader }then FLevel.CalcDefaultWidths(Self); FLevel.FCanUseSmartReload := False; ColumnChanged(True, hpAll, vpAll); end; end; procedure TdxMasterViewColumn.SetFieldInternally(Value: TField); begin if FField <> Value then begin FField := Value; if FField <> nil then begin FField.FreeNotification(FLevel); FFieldName := FField.FieldName; end; end; end; procedure TdxMasterViewColumn.SetFieldName(Value: string); begin if FFieldName <> Value then begin FFieldName := Value; SetField(FindField); end; end; procedure TdxMasterViewColumn.SetFooterAlignment(Value: TAlignment); begin if (FooterAlignment <> Value) or IsLoading then begin FFooterAlignment := Value; AssignedValues := AssignedValues + [cvFooterAlignment]; ColumnChanged(False, hpOne, vpFooter); end; end; procedure TdxMasterViewColumn.SetFooterStyle(Value: TdxMasterViewStyle); begin if FFooterStyle <> Value then begin if FFooterStyle <> nil then FFooterStyle.RemoveConsumeType(Self, sctFooter); FFooterStyle := Value; if FFooterStyle <> nil then FFooterStyle.AddConsumeType(Self, sctFooter); FooterStyleChanged([]); end; end; procedure TdxMasterViewColumn.SetGroupIndex(Value: Integer); begin if IsLoading then FLoadedGroupIndex := Value else begin if Value < -1 then Value := -1; if Value > FLevel.GroupColumnCount then Value := FLevel.GroupColumnCount; if (GroupIndex <> Value) and (coGrouping in FOptions) {and ((coSorting in FOptions) or (Value = -1) or (FSortOrder <> soNone))} then FLevel.ChangeGrouping(Self, Value); end; end; procedure TdxMasterViewColumn.SetHeaderAlignment(Value: TAlignment); begin if HeaderAlignment <> Value then begin FHeaderAlignment := Value; AssignedValues := AssignedValues + [cvHeaderAlignment]; ColumnChanged(False, hpOne, vpAll); end; end; procedure TdxMasterViewColumn.SetHeaderGlyph(Value: TBitmap); begin FHeaderGlyph.Assign(Value); end; procedure TdxMasterViewColumn.SetHeaderGlyphAlignment(Value: TAlignment); begin if FHeaderGlyphAlignment <> Value then begin FHeaderGlyphAlignment := Value; ColumnChanged(False, hpOne, vpAll); end; end; procedure TdxMasterViewColumn.SetHeaderStyle(Value: TdxMasterViewStyle); begin if FHeaderStyle <> Value then begin if FHeaderStyle <> nil then FHeaderStyle.RemoveConsumeType(Self, sctHeader); FHeaderStyle := Value; if FHeaderStyle <> nil then FHeaderStyle.AddConsumeType(Self, sctHeader); HeaderStyleChanged([]); end; end; procedure TdxMasterViewColumn.SetIndex(Value: Integer); begin if Value < 0 then Value := 0; if Value >= FLevel.ColumnCount then Value := FLevel.ColumnCount - 1; if Index <> Value then begin Control.Items.MoveData(FLevel, Index, Value); FLevel.FColumns.Move(Index, Value); FLevel.RefreshVisibleColumnsList; end; end; procedure TdxMasterViewColumn.SetInternalVisibleWidth(Value: Integer); begin CheckVisibleWidthValue(Value); Dec(Value, AddInWidth); FVisibleWidth := Value; { Dec(Value, AddInWidth); CheckWidthValue(Value); FVisibleWidth := Value;} end; procedure TdxMasterViewColumn.SetMaxRowCount(Value: Integer); begin if Value < FMinRowCount then Value := FMinRowCount; if FMaxRowCount <> Value then begin FMaxRowCount := Value; if FRowCount > FMaxRowCount then RowCount := FMaxRowCount; end; end; procedure TdxMasterViewColumn.SetMaxWidth(Value: Integer); begin if Value < FMinWidth then Value := FMinWidth; if FMaxWidth <> Value then begin FMaxWidth := Value; if IsLoading then Exit; if Width > FMaxWidth then Width := FMaxWidth else if VisibleWidth > VisibleMaxWidth then VisibleWidth := VisibleMaxWidth else if Control.AutoColumnWidth then FLevel.WidthChangedEx; end; end; procedure TdxMasterViewColumn.SetMinRowCount(Value: Integer); begin if Value < 1 then Value := 1; if Value > FMaxRowCount then Value := FMaxRowCount; if FMinRowCount <> Value then begin FMinRowCount := Value; if FRowCount < FMinRowCount then RowCount := FMinRowCount; end; end; procedure TdxMasterViewColumn.SetMinWidth(Value: Integer); begin if Value < 0 then Value := 0; if Value > FMaxWidth then Value := FMaxWidth; if FMinWidth <> Value then begin FMinWidth := Value; if IsLoading then Exit; if Width < FMinWidth then Width := FMinWidth else if VisibleWidth < VisibleMinWidth then VisibleWidth := VisibleMinWidth else if Control.AutoColumnWidth then FLevel.WidthChangedEx; end; end; procedure TdxMasterViewColumn.SetMultiLine(Value: Boolean); begin if FMultiLine <> Value then begin FMultiLine := Value; ColumnChanged(False, hpOne, vpContent); end; end; procedure TdxMasterViewColumn.SetOptions(Value: TdxMasterViewColumnOptions); const HParts: array[Boolean] of TdxMasterViewHPart = (hpAll, hpOne); var ChangedValues: TdxMasterViewColumnOptions; begin if FOptions <> Value then begin Byte(ChangedValues) := Byte(Value) xor Byte(FOptions); FOptions := Value; if (coHidden in ChangedValues) and Control.Customizing then TdxMVCustomizationForm(Control.CustomizationForm).RefreshColumnsListBox(nil); if coShowCaption in ChangedValues then begin if not (cvWidth in FAssignedValues) and FLevel.ShowHeader then FLevel.CalcDefaultWidths(Self); ColumnChanged(False, HParts[cvWidth in FAssignedValues], vpAll); end; end; end; procedure TdxMasterViewColumn.SetPressed(Value: Boolean); begin if FPressed <> Value then begin FPressed := Value; ColumnChanged(False, hpOne, vpHeader); if FLevel.ShowGroupByBox then ColumnChanged(False, hpOne, vpGroupByBox); if Control <> nil then Control.Update; end; end; procedure TdxMasterViewColumn.SetRowCount(Value: Integer); begin FLevel.Layout.ChangeRowCount(Self, Value); end; procedure TdxMasterViewColumn.SetRowIndex(Value: Integer); begin FLevel.Layout.ChangeRow(Self, Value, False); end; procedure TdxMasterViewColumn.SetSortIndex(Value: Integer); begin if IsLoading then FLoadedSortIndex := Value else begin if FSortOrder = soNone then Exit; if Value < 0 then Value := 0; if Value > FLevel.SortedColumnCount - 1 then Value := FLevel.SortedColumnCount - 1; if SortIndex <> Value then FLevel.ChangeSorting(Self, FSortOrder, Value); end; end; procedure TdxMasterViewColumn.SetSortOrder(Value: TdxMasterViewSortOrder); begin if IsLoading then FSortOrder := Value else if (FSortOrder <> Value) and (coSorting in FOptions) then FLevel.ChangeSorting(Self, Value, SortIndex); end; procedure TdxMasterViewColumn.SetSummaryField(Value: TField); begin if SummaryField <> Value then begin SetSummaryFieldInternally(Value); FLevel.FCanUseSmartReload := False; ColumnChanged(True, hpAll, vpAll); end; end; procedure TdxMasterViewColumn.SetSummaryFieldInternally(Value: TField); begin if SummaryField <> Value then begin FSummaryField := Value; if FSummaryField <> nil then begin FSummaryField.FreeNotification(FLevel); SummaryFieldName := FSummaryField.FieldName; end; end; end; procedure TdxMasterViewColumn.SetSummaryFieldName(Value: string); begin if SummaryFieldName <> Value then begin FSummaryFieldName := Value; AssignedValues := AssignedValues + [cvSummaryFieldName]; SetSummaryField(FindSummaryField); end; end; procedure TdxMasterViewColumn.SetSummaryFormat(Value: string); begin if FSummaryFormat <> Value then begin FSummaryFormat := Value; ColumnChanged(False, hpOne, vpFooter); end; end; procedure TdxMasterViewColumn.SetSummaryType(Value: TdxMasterViewSummaryType); begin if FSummaryType <> Value then begin FSummaryType := Value; FLevel.SummaryColumnsChanged(Self, TOperation(Value = stNone)); ColumnChanged(True, hpAll, vpAll); end; end; procedure TdxMasterViewColumn.SetVisible(Value: Boolean); begin if FVisible <> Value then begin if not Value and not IsLoading then begin ColumnChanged(False, hpAll, vpAll); if FLevel.Horizontal then FColIndex := ColIndex else FRowIndex := RowIndex; end; FVisible := Value; FLevel.RefreshVisibleColumnsList; if Value then FLevel.Layout.InsertColumn(FRowIndex, FColIndex, Self) else FLevel.Layout.RemoveColumn(Self); if IsLoading then Exit; if (HeaderStyle <> nil) and (svFont in HeaderStyle.AssignedValues) then HeaderStyleChanged([svFont]); if (ContentStyle <> nil) and (svFont in ContentStyle.AssignedValues) then ContentStyleChanged([svFont]); if (FooterStyle <> nil) and (svFont in FooterStyle.AssignedValues) then FooterStyleChanged([svFont]); FLevel.WidthChanged; if Value then ColumnChanged(False, hpAll, vpAll); if Control <> nil then if Control.Customizing then TdxMVCustomizationForm(Control.CustomizationForm).RefreshColumnsListBox(Self); if FVisible then FLevel.DoShowColumn(Self) else FLevel.DoHideColumn(Self); end; end; procedure TdxMasterViewColumn.SetVisibleWidth(Value: Integer); begin CheckVisibleWidthValue(Value); if VisibleWidth <> Value then ChangeWidth(Value - AddInWidth, True); end; procedure TdxMasterViewColumn.SetWidth(Value: Integer); begin CheckWidthValue(Value); if Width <> Value then ChangeWidth(Value, False); end; procedure TdxMasterViewColumn.CheckRowCount(var Value: Integer); begin if Value < FMinRowCount then Value := FMinRowCount; if Value > FMaxRowCount then Value := FMaxRowCount; end; procedure TdxMasterViewColumn.CheckVisibleWidthValue(var Value: Integer); begin if Value < VisibleMinWidth then Value := VisibleMinWidth; if Value > VisibleMaxWidth then Value := VisibleMaxWidth; end; procedure TdxMasterViewColumn.CheckWidthValue(var Value: Integer); begin if Value < FMinWidth then Value := FMinWidth; if Value > FMaxWidth then Value := FMaxWidth; end; function TdxMasterViewColumn.FindField: TField; begin if (FFieldName <> '') and (FLevel.DataSet <> nil) then Result := FLevel.DataSet.FindField(FFieldName) else Result := nil; end; function TdxMasterViewColumn.FindSummaryField: TField; begin if cvSummaryFieldName in FAssignedValues then if (FSummaryFieldName <> '') and (FLevel.DataSet <> nil) then Result := FLevel.DataSet.FindField(FSummaryFieldName) else Result := nil else Result := Field; end; procedure TdxMasterViewColumn.HeaderGlyphChanged(Sender: TObject); const HParts: array[Boolean] of TdxMasterViewHPart = (hpAll, hpOne); begin if not (cvWidth in FAssignedValues) and FLevel.ShowHeader then FLevel.CalcDefaultWidths(Self); ColumnChanged(False, HParts[cvWidth in FAssignedValues], vpAll); end; function TdxMasterViewColumn.IsAlignmentStored: Boolean; begin Result := (cvAlignment in FAssignedValues) and (FAlignment <> DefaultAlignment); end; function TdxMasterViewColumn.IsCaptionStored: Boolean; begin Result := (cvCaption in FAssignedValues) and (FCaption <> DefaultCaption); end; function TdxMasterViewColumn.IsFooterAlignmentStored: Boolean; begin Result := (cvFooterAlignment in FAssignedValues) and (FFooterAlignment <> DefaultAlignment); end; function TdxMasterViewColumn.IsHeaderAlignmentStored: Boolean; begin Result := (cvHeaderAlignment in FAssignedValues) and (FHeaderAlignment <> DefaultHeaderAlignment); end; function TdxMasterViewColumn.IsSummaryFieldNameStored: Boolean; begin Result := (cvSummaryFieldName in FAssignedValues) and (FSummaryFieldName <> FFieldName); end; function TdxMasterViewColumn.IsWidthLinked: Boolean; begin Result := VisibleWidth = FLevel.Layout.ContentWidths[ColIndex]; end; function TdxMasterViewColumn.IsWidthStored: Boolean; begin Result := (cvWidth in FAssignedValues) and (FWidth <> DefaultWidth); end; procedure TdxMasterViewColumn.ReadLeaveSortOrder(Reader: TReader); begin FLeaveSortOrder := Reader.ReadBoolean; end; procedure TdxMasterViewColumn.WriteLeaveSortOrder(Writer: TWriter); begin Writer.WriteBoolean(FLeaveSortOrder); end; procedure TdxMasterViewColumn.DefineProperties(Filer: TFiler); begin inherited; Filer.DefineProperty('LeaveSortOrder', ReadLeaveSortOrder, WriteLeaveSortOrder, FLeaveSortOrder); end; procedure TdxMasterViewColumn.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation = opRemove) and (AComponent is TdxMasterViewStyle) then begin if HeaderStyle = AComponent then HeaderStyle := nil; if ContentStyle = AComponent then ContentStyle := nil; if FooterStyle = AComponent then FooterStyle := nil; end; end; procedure TdxMasterViewColumn.SetName(const NewName: TComponentName); begin inherited; if dxMVDesigner <> nil then dxMVDesigner.Changed(Control, [rcColumns]); end; procedure TdxMasterViewColumn.SetParentComponent(AParent: TComponent); begin if AParent is TdxMasterViewLevel then TdxMasterViewLevel(AParent).AddColumn(Self); end; function TdxMasterViewColumn.CanGrouping(Index: Integer): Boolean; begin Result := (coGrouping in FOptions) and FLevel.CanGrouping(Self, Index); end; function TdxMasterViewColumn.CanHiding: Boolean; begin Result := FVisible and not Hidden and (FLevel.VisibleColumnCount > 1) and ((locColumnHiding in FLevel.OptionsCustomize) or Control.Customizing or Control.IsDesigning); end; function TdxMasterViewColumn.CanMoving: Boolean; begin Result := (locColumnMoving in FLevel.OptionsCustomize) and (coMoving in FOptions); end; function TdxMasterViewColumn.CanHorSizing: Boolean; begin Result := FLevel.CanHorSizing and (coHorSizing in FOptions) and (FMinWidth <> FMaxWidth); end; function TdxMasterViewColumn.CanSorting: Boolean; begin Result := (coSorting in FOptions) and FLevel.CanSorting(Self); end; function TdxMasterViewColumn.CanVerSizing: Boolean; begin Result := (locColumnVerSizing in FLevel.OptionsCustomize) and (coVerSizing in FOptions) and (FMinRowCount <> FMaxRowCount); end; procedure TdxMasterViewColumn.ColumnChanged(HardRefresh: Boolean; HPart: TdxMasterViewHPart; VPart: TdxMasterViewVPart); var I: Integer; begin if not FLevel.Active or IsDestroying or IsLoading then Exit; if HardRefresh then FLevel.LevelChanged(HardRefresh) else if (FVisible or (VPart = vpGroupByBox)) and (Control <> nil) and (FLevel.FLayout.FUpdateLockCount = 0) then with Control do if (FUpdateLockCount = 0) and HandleAllocated then for I := TopIndex to TopIndex + PartVisibleItemCount - 1 do with AbsoluteItems[I] do if Level = Self.Level then if HPart = hpOne then Invalidate(Self, VPart) else Invalidate(nil, VPart) else if Level.HasAsParent(Self.Level) then Invalidate(nil, vpFooter); end; procedure TdxMasterViewColumn.ContentStyleChanged(Values: TdxMasterViewStyleValues); begin if (Values = []) or (svFont in Values) then FLevel.ContentStyleChanged([svFont]); ColumnChanged(False, hpOne, vpContent); end; function TdxMasterViewColumn.DefaultAlignment: TAlignment; begin if Field = nil then Result := taLeftJustify else Result := Field.Alignment; end; function TdxMasterViewColumn.DefaultCaption: string; begin if Field = nil then Result := FieldName else Result := Field.DisplayName; end; function TdxMasterViewColumn.DefaultHeaderAlignment: TAlignment; begin Result := taLeftJustify; end; function TdxMasterViewColumn.DefaultWidth: Integer; var DC: HDC; PrevFont: HFONT; TM: TTextMetric; Size: TSize; W: Integer; begin if (FLevel = nil) or (Field = nil) then Result := 64 else begin DC := GetDC(0); PrevFont := SelectObject(DC, ContentFont.Handle); GetTextMetrics(DC, TM); GetTextExtentPoint{32}(DC, '0', 1, Size); Result := Field.DisplayWidth * (Size.cx - TM.tmOverhang) + TM.tmOverhang + 4; if FLevel.ShowHeader then begin W := GetHeaderBestFitWidth(DC, False); if W > Result then Result := W; end; SelectObject(DC, PrevFont); ReleaseDC(0, DC); end; CheckWidthValue(Result); end; procedure TdxMasterViewColumn.DoAfterCalcSummary(Node: TdxMasterViewNode; DataSet: TDataSet; var Value: Extended); begin if Assigned(FOnAfterCalcSummary) then FOnAfterCalcSummary(Self, Node, DataSet, Value); end; procedure TdxMasterViewColumn.DoBeforeCalcSummary(Node: TdxMasterViewNode; DataSet: TDataSet; var Value: Extended); begin if Assigned(FOnBeforeCalcSummary) then FOnBeforeCalcSummary(Self, Node, DataSet, Value); end; procedure TdxMasterViewColumn.DoCalcSummary(Node: TdxMasterViewNode; DataSet: TDataSet; var Value: Extended); begin if Assigned(FOnCalcSummary) then FOnCalcSummary(Self, Node, DataSet, Value); end; procedure TdxMasterViewColumn.DrawSortMark(DC: HDC; var ARect: TRect); var Sign, X, Y: Integer; PrevPen: HPEN; begin Dec(ARect.Right, SortOrderMarkZoneWidth); Sign := 2 * Byte(FSortOrder = soAscending) - 1; X := ARect.Right + SortOrderMarkZoneWidth div 2 - 1; Y := (ARect.Top + ARect.Bottom - Sign * SortOrderMarkHeight) div 2 - Byte(FSortOrder = soDescending); PrevPen := SelectObject(DC, Control.BtnHighlightPen); MoveToEx(DC, X + 4, Y + Sign * 5, nil); LineTo(DC, X + 1, Y); LineTo(DC, X + 4, Y + Sign * 6); if FSortOrder = soDescending then SelectObject(DC, Control.BtnShadowPen); LineTo(DC, X - 3, Y + Sign * 6); if FSortOrder = soAscending then SelectObject(DC, Control.BtnShadowPen); LineTo(DC, X, Y); LineTo(DC, X - 3, Y + Sign * 7); SelectObject(DC, PrevPen); end; procedure TdxMasterViewColumn.DrawHeader(DC: HDC; var ARect: TRect; Node: TdxMasterViewNode); var ABrush: HBRUSH; ATextColor, ABkColor: TColor; AFont: TFont; R: TRect; ADC: HDC; CanUseBitmap, FillBackground, MakeRightSpace: Boolean; begin FLevel.GetHeaderParams(Node, Self, @ABrush, nil, @ATextColor, @ABkColor, @AFont); R := ARect; CanUseBitmap := (movUseBitmap in Control.OptionsView) or not FHeaderGlyph.Empty; with Control do if CanUseBitmap then begin with R do begin OffsetRect(R, -Left, -Top); PrepareBitmap(Right, Bottom); end; ADC := Bitmap.Canvas.Handle; end else ADC := DC; if not FHeaderGlyph.Empty then begin FillRect(ADC, R, ABrush); TransparentDraw(ADC, R, FHeaderGlyphAlignment, ABrush, FHeaderGlyph); FillBackground := False; end else FillBackground := True; MakeRightSpace := (FSortOrder = soNone) or (ARect.Right - ARect.Left < SortOrderMarkZoneWidth); if coShowCaption in FOptions then Control.DrawText(ADC, R, AFont, ABrush, ATextColor, ABkColor, HeaderAlignment, Caption, FillBackground, MakeRightSpace, False, False) else if FHeaderGlyph.Empty then FillRect(ADC, R, ABrush); if not MakeRightSpace then DrawSortMark(ADC, R); if CanUseBitmap then with ARect do BitBlt(DC, Left, Top, Right - Left, Bottom - Top, ADC, 0, 0, SRCCOPY); end; procedure TdxMasterViewColumn.DrawContent(DC: HDC; var ARect: TRect; Node: TdxMasterViewNode); var ABrush: HBRUSH; ATextColor, ABkColor: TColor; AFont: TFont; S: string; begin FLevel.GetContentParams(Node, Self, @ABrush, @ATextColor, @ABkColor, @AFont, True); if FMultiLine and not VarIsNull(Node.Values[Index]) then S := Node.Values[Index] else S := Node.Strings[Index]; Control.DrawText(DC, ARect, AFont, ABrush, ATextColor, ABkColor, Alignment, S, True, True, True, FMultiLine); end; procedure TdxMasterViewColumn.DrawFooter(DC: HDC; var ARect: TRect; Node: TdxMasterViewNode); var ABrush: HBRUSH; ATextColor, ABkColor: TColor; AFont: TFont; begin FLevel.GetFooterParams(Node, Self, @ABrush, @ATextColor, @ABkColor, @AFont); Control.DrawText(DC, ARect, AFont, ABrush, ATextColor, ABkColor, FooterAlignment, GetFooterText(Node), True, True, True, False); end; procedure TdxMasterViewColumn.FooterStyleChanged(Values: TdxMasterViewStyleValues); begin if (Values = []) or (svFont in Values) then FLevel.FooterStyleChanged([svFont]); ColumnChanged(False, hpOne, vpFooter); end; function TdxMasterViewColumn.GetFooterText(Node: TdxMasterViewNode): string; const SummaryFormats: array[Boolean, stSum..stAverage] of string = (('0.00;-0.00', 'MIN=0.00;MIN=-0.00', 'MAX=0.00;MAX=-0.00', '0', 'AVG=0.00;AVG=-0.00'), ('', 'MIN=', 'MAX=', '0', 'AVG=')); var Value: Extended; IsDateTime: Boolean; begin if SummaryType = stNone then Result := '' else begin Value := GetFooterValue(Node); IsDateTime := (SummaryField <> nil) and (SummaryField.DataType in [ftDate, ftTime, ftDateTime]) and (FSummaryType <> stCount); if FSummaryFormat = '' then Result := SummaryFormats[IsDateTime, FSummaryType] else Result := FSummaryFormat; if IsDateTime then case SummaryField.DataType of ftDate: Result := Result + DateToStr(Value); ftTime: Result := Result + TimeToStr(Value); else Result := Result + DateTimeToStr(Value); end else Result := FormatFloat(Result, Value); end; FLevel.DoGetFooterCellText(Node, Self, Result); end; function TdxMasterViewColumn.GetGroupByBoxBounds(Node: TdxMasterViewNode): TRect; begin Result := Node.GroupByBoxColumnBounds[GroupIndex]; end; function TdxMasterViewColumn.GetGroupDisplayText(Node: TdxMasterViewNode): string; begin Result := Node.GroupValueText; end; function TdxMasterViewColumn.GetHeaderBestFitWidth(DC: HDC; CheckSortOrderMark: Boolean): Integer; var PrevFont: HFONT; Size: TSize; begin if coShowCaption in FOptions then begin PrevFont := SelectObject(DC, HeaderFont.Handle); GetTextExtentPoint{32}(DC, PChar(Caption), Length(Caption), Size); SelectObject(DC, PrevFont); end else Size.cx := 0; Result := 2 + Size.cx + 2; if CheckSortOrderMark and (FSortOrder <> soNone) then Inc(Result, SortOrderMarkZoneWidth - 2); if not FHeaderGlyph.Empty then if FHeaderAlignment <> taCenter then Inc(Result, FHeaderGlyph.Width) else if (Caption = '') and (FHeaderGlyph.Width > Result) then Result := FHeaderGlyph.Width; Inc(Result, 1 + 1); end; function TdxMasterViewColumn.GetHeaderBounds(Node: TdxMasterViewNode): TRect; begin if Node.NodeType = ntData then begin Result := Node.HeaderBounds; with Result do begin Inc(Left, LogicalOffset); if FLevel.Horizontal then Right := Left + VisibleWidth else Right := Left + FLevel.Layout.HeaderWidths[ColIndex]; Inc(Top, RowIndex * FLevel.HeaderRealHeight); Bottom := Top + RowCount * FLevel.HeaderRealHeight; end; end else SetRectEmpty(Result); end; function TdxMasterViewColumn.GetHeaderHeight: Integer; var Size: TSize; begin CalcFontSize(HeaderFont, Size); Result := Size.cy + 2 + 2 * 2; // 19 end; function TdxMasterViewColumn.GetContentBounds(Node: TdxMasterViewNode): TRect; begin if Node.NodeType = ntData then begin Result := Node.ContentBounds; with Result do begin Inc(Left, LogicalOffset); if not FLevel.Horizontal then Inc(Left, FLevel.Layout.HeaderWidths[ColIndex]); Right := Left + VisibleWidth; Inc(Top, RowIndex * FLevel.ContentRealHeight); Bottom := Top + RowCount * FLevel.ContentRealHeight; end; end else SetRectEmpty(Result); end; function TdxMasterViewColumn.GetFooterBounds(Node: TdxMasterViewNode): TRect; begin Result := Node.SubFooterBounds[Node.Level.IndexOfParent(FLevel)]; if not IsRectEmpty(Result) then with Result do begin Inc(Left, LogicalOffset); if not FLevel.Horizontal then Inc(Left, FLevel.Layout.HeaderWidths[ColIndex]); Right := Left + VisibleWidth; Inc(Top, RowIndex * FLevel.FooterHeight); Bottom := Top + RowCount * FLevel.FooterHeight; end; end; procedure TdxMasterViewColumn.HeaderStyleChanged(Values: TdxMasterViewStyleValues); begin if (Values = []) or (svFont in Values) then FLevel.HeaderStyleChanged([svFont]); ColumnChanged(False, hpOne, vpHeader); if GroupIndex <> -1 then FLevel.GroupByBoxStyleChanged(Values); end; procedure TdxMasterViewColumn.Assign(Source: TPersistent); var AColumn: TdxMasterViewColumn; begin if Source is TdxMasterViewColumn then begin AColumn := TdxMasterViewColumn(Source); FLevel.BeginDataChanging; try AssignedValues := []; if cvAlignment in AColumn.AssignedValues then Alignment := AColumn.Alignment; if cvCaption in AColumn.AssignedValues then Caption := AColumn.Caption; if cvFooterAlignment in AColumn.AssignedValues then FooterAlignment := AColumn.FooterAlignment; if cvHeaderAlignment in AColumn.AssignedValues then HeaderAlignment := AColumn.HeaderAlignment; if cvSummaryFieldName in AColumn.AssignedValues then SummaryFieldName := AColumn.SummaryFieldName; if cvWidth in AColumn.AssignedValues then Width := AColumn.Width; ContentStyle := AColumn.ContentStyle; FieldName := AColumn.FieldName; FooterStyle := AColumn.FooterStyle; GroupIndex := AColumn.GroupIndex; HeaderGlyph := AColumn.HeaderGlyph; HeaderStyle := AColumn.HeaderStyle; Index := AColumn.Index; MaxRowCount := AColumn.MaxRowCount; MaxWidth := AColumn.MaxWidth; MinRowCount := AColumn.MinRowCount; MinWidth := AColumn.MinWidth; MultiLine := AColumn.MultiLine; Options := AColumn.Options; RowIndex := AColumn.RowIndex; ColIndex := AColumn.ColIndex; RowCount := AColumn.RowCount; SortIndex := AColumn.SortIndex; SortOrder := AColumn.SortOrder; SummaryFormat := AColumn.SummaryFormat; SummaryType := AColumn.SummaryType; VisibleWidth := AColumn.VisibleWidth; FLeaveSortOrder := AColumn.FLeaveSortOrder; Visible := AColumn.Visible; finally FLevel.EndDataChanging; end; end else inherited; end; procedure TdxMasterViewColumn.ChangeWidth(Value: Integer; AdjustLinkedColumns: Boolean); var Delta: Integer; NeedAssignWidths: Boolean; function ChangeLinkedWidths: Boolean; type PBoolArray = ^TBoolArray; TBoolArray = array[-1..MaxInt div (2 * SizeOf(Boolean))] of Boolean; var NewWidths: PIntArray; ColumnLocked, ColumnProcessed, ColumnFixed: PBoolArray; IsLast: Boolean; I, W: Integer; Column: TdxMasterViewColumn; procedure SetFixedColumns(Column: TdxMasterViewColumn); var List: TList; I: Integer; begin List := TList.Create; try FLevel.Layout.HasLeftNeighbours(Column, List, True); for I := 0 to List.Count - 1 do begin ColumnFixed[TdxMasterViewColumn(List[I]).VisibleIndex] := True; SetFixedColumns(List[I]); end; finally List.Free; end; end; procedure ChangeWidth(Column: TdxMasterViewColumn; var Delta: Integer; Direction: TdxMasterViewDirection); var VI, W, CheckW, CurDelta, I, ADelta: Integer; NeedChangeSize: Boolean; ListL, ListR, CollapsingList, ExpandingList: TList; OppositeDirection: TdxMasterViewDirection; function CanChangeSize(Column: TdxMasterViewColumn): Boolean; var List: TList; I: Integer; begin Result := not ColumnLocked[Column.VisibleIndex]; if Result then begin List := TList.Create; try if FLevel.Layout.HasLeftNeighbours(Column, List, True) then for I := 0 to List.Count - 1 do begin Result := CanChangeSize(List[I]); if not Result then Break; end; finally List.Free; end; end; end; begin if Column = nil then VI := -1 else VI := Column.VisibleIndex; if ColumnLocked[VI] or ColumnProcessed[VI] then begin Delta := 0; Exit; end; if ColumnFixed[VI] then begin Result := False; Exit; end; NeedChangeSize := (Column <> nil) and (Control.AutoColumnWidth or (Direction = dirRight) and CanChangeSize(Column)); if NeedChangeSize then begin W := NewWidths[VI]; if Delta < 0 then CheckW := Column.VisibleMinWidth else CheckW := Column.VisibleMaxWidth; if (Delta < 0) and (W + Delta < CheckW) or (Delta > 0) and (W + Delta > CheckW) then CurDelta := CheckW - W else CurDelta := Delta; Dec(Delta, CurDelta); end else begin CurDelta := 0; ColumnProcessed[VI] := True; end; if VI <> -1 then Inc(NewWidths[VI], CurDelta); ColumnLocked[VI] := True; ListL := TList.Create; ListR := TList.Create; try if Column <> nil then with FLevel.Layout do begin HasLeftNeighbours(Column, ListL, True); HasRightNeighbours(Column, ListR, True); end; if Direction = dirLeft then begin CollapsingList := ListL; if Column = nil then FLevel.Layout.RetrieveLastColumns(CollapsingList); ExpandingList := ListR; OppositeDirection := dirRight; end else begin CollapsingList := ListR; if (CollapsingList.Count = 0) and (Control.AutoColumnWidth or FLevel.OccupyRestSpace) then CollapsingList.Add(nil); ExpandingList := ListL; OppositeDirection := dirLeft; end; for I := 0 to CollapsingList.Count - 1 do begin ADelta := -(Delta + CurDelta); ChangeWidth(CollapsingList[I], ADelta, OppositeDirection); if ADelta <> 0 then begin Result := False; Exit; end; end; if Column = nil then Delta := 0; if Delta <> 0 then if (ExpandingList.Count = 0) and NeedChangeSize then Result := False else begin for I := 0 to ExpandingList.Count - 1 do begin ADelta := Delta; ChangeWidth(ExpandingList[I], ADelta, Direction); if ADelta <> 0 then begin Result := False; Exit; end; end; Delta := 0; end; finally ListR.Free; ListL.Free; ColumnLocked[VI] := False; end; end; begin if FLevel.Horizontal then begin if Control.AutoColumnWidth and FLevel.Layout.OneOnLine(Self) then begin Result := False; Exit; end; Result := True; IsLast := FLevel.Layout.IsColumnLast(Self); GetMem(NewWidths, FLevel.VisibleColumnCount * SizeOf(Integer)); GetMem(ColumnLocked, (1 + FLevel.VisibleColumnCount) * SizeOf(Boolean)); GetMem(ColumnProcessed, (1 + FLevel.VisibleColumnCount) * SizeOf(Boolean)); GetMem(ColumnFixed, (1 + FLevel.VisibleColumnCount) * SizeOf(Boolean)); try for I := -1 to FLevel.VisibleColumnCount - 1 do begin if I <> -1 then NewWidths[I] := FLevel.VisibleColumns[I].VisibleWidth; ColumnLocked[I] := False; ColumnProcessed[I] := False; ColumnFixed[I] := False; end; if not IsLast then SetFixedColumns(Self); if IsLast and Control.AutoColumnWidth then ChangeWidth(Self, Delta, dirLeft) else ChangeWidth(Self, Delta, dirRight); if not Result then Exit; for I := 0 to FLevel.VisibleColumnCount - 1 do begin Column := FLevel.VisibleColumns[I]; if Column <> Self then with Column do begin W := NewWidths[I] - AddInWidth; if Control.AutoColumnWidth or (VisibleWidth <> NewWidths[I]) then Width := W else InternalVisibleWidth := NewWidths[I]; end; end; finally FreeMem(ColumnFixed, (1 + FLevel.VisibleColumnCount) * SizeOf(Boolean)); FreeMem(ColumnProcessed, (1 + FLevel.VisibleColumnCount) * SizeOf(Boolean)); FreeMem(ColumnLocked, (1 + FLevel.VisibleColumnCount) * SizeOf(Boolean)); FreeMem(NewWidths, FLevel.VisibleColumnCount * SizeOf(Integer)); end; end else with FLevel.Layout do begin { Result := False; for I := 0 to Items[ColIndex].Count - 1 do with Columns[I, ColIndex] do if (Value < MinWidth) or (Value > MaxWidth) then Exit;} Result := True; if Control.AutoColumnWidth then begin W := ContentDynamicMinWidths[ColIndex]; if Value < W then Value := W; if ColIndex <> Count - 1 then begin W := ContentDynamicMaxWidths[ColIndex]; if Value > W then Value := W; end; Result := Self.Width <> Value; if not Result then Exit; FLevel.AssignColumnWidths; end; for I := 0 to Items[ColIndex].Count - 1 do with Columns[I, ColIndex] do if (RowIndex = I) and (coHorSizing in Options) then Width := Value; if Control.AutoColumnWidth then begin CalcColWidths; CheckColumnsWidths(Self); FLevel.AssignColumnWidths; end; end; end; begin CheckWidthValue(Value); NeedAssignWidths := False; try if not IsLoading{FLevel} and AdjustLinkedColumns and FVisible then begin Delta := Value - (VisibleWidth - AddInWidth); NeedAssignWidths := True; FLevel.BeginAssignWidths; if not ChangeLinkedWidths then begin NeedAssignWidths := False; FLevel.CancelAssignWidths; Exit; end; end; FWidth := Value; FVisibleWidth := Value; //InternalVisibleWidth := Value + AddInWidth; if not (cvWidth in FAssignedValues) then AssignedValues := AssignedValues + [cvWidth] else if FVisible then FLevel.WidthChanged; finally if NeedAssignWidths then FLevel.EndAssignWidths; end; ColumnChanged(False, hpAll, vpAll); end; function TdxMasterViewColumn.GetParentComponent: TComponent; begin Result := FLevel; end; function TdxMasterViewColumn.HasParent: Boolean; begin Result := True; end; procedure TdxMasterViewColumn.ApplyBestFit; var Value: Integer; begin if Visible then begin if FLevel.Horizontal then Value := GetBestFitWidth else Value := FLevel.Layout.GetBestFitWidth(ColIndex); VisibleWidth := Value; end; end; function TdxMasterViewColumn.GetBestFitWidth: Integer; var DC: HDC; PrevFont: HFONT; AIndex: Integer; Size: TSize; function GetTextWidth(const S: string): Integer; begin GetTextExtentPoint{32}(DC, PChar(S), Length(S), Size); Result := 2 + Size.cx + 2; end; procedure CalcBestFit(Root: TdxMasterViewNode); var I: Integer; begin if (Root.Level = FLevel) and (Root.NodeType = ntData) then begin I := FAddInWidth + GetTextWidth(Root.Strings[AIndex]) + FLevel.LineWidth; if I > Result then Result := I; end else for I := 0 to Root.Count - 1 do CalcBestFit(Root[I]); end; begin DC := GetDC(0); try if FLevel.Horizontal then Result := GetHeaderBestFitWidth(DC, True) else Result := 0; AIndex := Index; PrevFont := SelectObject(DC, ContentFont.Handle); try CalcBestFit(Control.FItems); finally SelectObject(DC, PrevFont); end; finally ReleaseDC(0, DC); end; end; function TdxMasterViewColumn.GetFooterValue(Node: TdxMasterViewNode): Extended; begin with Node.NodesWithSummaryData[FLevel] do Result := FSummaries[Byte(NodeType = ntData) * Self.FLevel.Index][SummaryIndex]; end; { TdxMasterViewLayout } constructor TdxMasterViewLayout.Create(ALevel: TdxMasterViewLevel); begin inherited Create; FLevel := ALevel; FHorizontal := True; FLines := TList.Create; end; destructor TdxMasterViewLayout.Destroy; var I: Integer; begin DestroyColArrays; for I := Count - 1 downto 0 do Items[I].Free; FLines.Free; inherited; end; function TdxMasterViewLayout.GetColLogicalOffset(ACol: Integer): Integer; var I: Integer; begin Result := 0; if FLevel.HasExpandButton then Inc(Result, LevelIndent); for I := 0 to ACol - 1 do Inc(Result, ColWidths[I]); end; function TdxMasterViewLayout.GetColOffset(ACol: Integer): Integer; begin with FLevel do Result := Indent - Control.LeftPos + ColLogicalOffsets[ACol]; end; function TdxMasterViewLayout.GetColumn(ARow, ACol: Integer): TdxMasterViewColumn; begin if FHorizontal then Result := TdxMasterViewColumn(TList(FLines[ARow])[ACol]) else Result := TdxMasterViewColumn(TList(FLines[ACol])[ARow]); end; function TdxMasterViewLayout.GetColWidth(ACol: Integer): Integer; begin Result := HeaderWidths[ACol] + ContentWidths[ACol]; end; function TdxMasterViewLayout.GetContentDynamicMaxWidth(ACol: Integer): Integer; var W, I: Integer; begin Result := ContentMaxWidths[ACol]; if FLevel.Control.AutoColumnWidth then begin W := VisibleWidth - ColLogicalOffsets[ACol] - HeaderWidths[ACol]; for I := Count - 1 downto ACol + 1 do Dec(W, HeaderWidths[I] + ContentDynamicMinWidths[I]); if W < Result then Result := W; W := ContentDynamicMinWidths[ACol]; if Result < W then Result := W; end; end; function TdxMasterViewLayout.GetContentDynamicMinWidth(ACol: Integer): Integer; begin Result := ContentMinWidths[ACol]; end; function TdxMasterViewLayout.GetContentMaxWidth(ACol: Integer): Integer; var I, W: Integer; begin Result := MaxInt; for I := 0 to Items[ACol].Count - 1 do with Columns[I, ACol] do begin W := VisibleMaxWidth; if (Result = MaxInt) or (W > Result) then Result := W; end; end; function TdxMasterViewLayout.GetContentMinWidth(ACol: Integer): Integer; var I, W: Integer; begin Result := 0; for I := 0 to Items[ACol].Count - 1 do begin W := Columns[I, ACol].VisibleMinWidth; if W > Result then Result := W; end; end; function TdxMasterViewLayout.GetContentNonScaledWidth(ACol: Integer): Integer; begin Result := FContentNonScaledWidths[ACol]; end; function TdxMasterViewLayout.GetContentWidth(ACol: Integer): Integer; begin Result := FContentWidths[ACol]; end; function TdxMasterViewLayout.GetCount: Integer; begin Result := FLines.Count; end; function TdxMasterViewLayout.GetHeaderDynamicMaxWidth(ACol: Integer): Integer; var W, I: Integer; begin Result := HeaderMaxWidths[ACol]; if FLevel.Control.AutoColumnWidth and (VisibleWidth > MinWidth) then begin W := VisibleWidth - ColLogicalOffsets[ACol] - ContentMinWidths[ACol]; for I := Count - 1 downto ACol + 1 do Dec(W, HeaderWidths[I] + ContentMinWidths[I]); if W < Result then Result := W; W := HeaderMinWidths[ACol]; if Result < W then Result := W; end; end; function TdxMasterViewLayout.GetHeaderDynamicMinWidth(ACol: Integer): Integer; begin Result := HeaderMinWidths[ACol]; end; function TdxMasterViewLayout.GetHeaderMaxWidth(ACol: Integer): Integer; begin Result := dxMVColumnDefaultMaxWidth; end; function TdxMasterViewLayout.GetHeaderMinWidth(ACol: Integer): Integer; begin Result := dxMVColumnDefaultMinWidth; end; function TdxMasterViewLayout.GetHeaderWidth(ACol: Integer): Integer; begin if FLevel.ShowHeader then Result := FHeaderWidths[ACol] else Result := 0; end; function TdxMasterViewLayout.GetHeaderWidthAssigned(ACol: Integer): Boolean; begin Result := FHeaderWidthAssigned[ACol]; end; function TdxMasterViewLayout.GetItem(Index: Integer): TList; begin Result := TList(FLines[Index]); end; { function TdxMasterViewLayout.GetMaxWidth: Integer; var I, AWidth: Integer; begin Result := 0; for I := 0 to Count - 1 do if Items[I].Count <> 0 then begin with Columns[I, Items[I].Count - 1] do AWidth := MaxLogicalOffset + VisibleMaxWidth; if AWidth > Result then Result := AWidth; end; end; } function TdxMasterViewLayout.GetMinWidth: Integer; var I, AWidth: Integer; begin Result := 0; if FHorizontal then for I := 0 to Count - 1 do if Items[I].Count <> 0 then begin with Columns[I, Items[I].Count - 1] do AWidth := MinLogicalOffset + VisibleMinWidth; if AWidth > Result then Result := AWidth; end else else begin if FLevel.HasExpandButton then Result := LevelIndent; for I := 0 to Count - 1 do Inc(Result, {HeaderMinWidths[I]}HeaderWidths[I]{!!!} + ContentMinWidths[I]); end; end; function TdxMasterViewLayout.GetNonScaledWidth: Integer; var I, AWidth: Integer; begin if not FHorizontal and FLevel.HasExpandButton then Result := LevelIndent else Result := 0; for I := 0 to Count - 1 do if Items[I].Count <> 0 then if FHorizontal then begin with Columns[I, Items[I].Count - 1] do AWidth := NonScaledLogicalOffset + NonScaledWidth; if AWidth > Result then Result := AWidth; end else Inc(Result, HeaderWidths[I] + ContentNonScaledWidths[I]); end; function TdxMasterViewLayout.GetVisibleWidth: Integer; var AIndent: Integer; begin if FLevel.Control.AutoColumnWidth and (FLevel.VisibleColumnCount <> 0) then begin AIndent := FLevel.Indent; Result := MinWidth; if AIndent + Result < FLevel.Control.ClientWidth then Result := FLevel.Control.ClientWidth - AIndent; end else Result := GetNonScaledWidth; end; procedure TdxMasterViewLayout.SetContentInternalVisibleWidth(ACol, Value: Integer); var I: Integer; begin if FContentWidths[ACol] <> Value then for I := 0 to Items[ACol].Count - 1 do with Columns[I, ACol] do if RowIndex = I then InternalVisibleWidth := Value; end; procedure TdxMasterViewLayout.SetContentWidth(ACol, Value: Integer); var I, PrevWidth: Integer; Changed: Boolean; begin if FContentWidths[ACol] <> Value then begin Changed := False; FLevel.BeginAssignWidths; try for I := 0 to Items[ACol].Count - 1 do with Columns[I, ACol] do if RowIndex = I then begin PrevWidth := Width; Width := Value; Changed := Changed or (Width <> PrevWidth); end; finally if Changed then FLevel.EndAssignWidths else FLevel.CancelAssignWidths; end; end; end; procedure TdxMasterViewLayout.SetCount(Value: Integer); var PrevCount, I: Integer; PrevHeaderWidths: PIntArray; PrevHeaderWidthAssigned: PBoolArray; begin if Value < 1 then Value := 1; if Count <> Value then begin if Value > Count then begin PrevCount := Count; PrevHeaderWidths := FHeaderWidths; PrevHeaderWidthAssigned := FHeaderWidthAssigned; if not FHorizontal then begin FHeaderWidths := nil; FHeaderWidthAssigned := nil; DestroyColArrays; end; for I := Count + 1 to Value do FLines.Add(TList.Create); if FHorizontal then FRowCount := Count else begin CreateColArrays; Move(PrevHeaderWidths^, FHeaderWidths^, PrevCount * SizeOf(Integer)); Move(PrevHeaderWidthAssigned^, FHeaderWidthAssigned^, PrevCount * SizeOf(Boolean)); FreeMem(PrevHeaderWidths); FreeMem(PrevHeaderWidthAssigned); end; end else for I := Count - 1 downto Value do Delete(I); end; end; procedure TdxMasterViewLayout.SetHeaderWidth(ACol, Value: Integer); begin ChangeHeaderWidth(ACol, Value, False); end; procedure TdxMasterViewLayout.SetHeaderWidthAssigned(ACol: Integer; Value: Boolean); begin if FHeaderWidthAssigned[ACol] <> Value then begin FHeaderWidthAssigned[ACol] := Value; if not Value then HeaderWidthChanged; end; end; function TdxMasterViewLayout.AreNeighbours(Column1, Column2: TdxMasterViewColumn; ExactResult: Boolean): Boolean; var MinRow, MaxRow, I, I1, I2: Integer; begin if ExactResult then Result := Column1.LogicalOffset + Column1.VisibleWidth = Column2.LogicalOffset else begin if Column1.RowIndex > Column2.RowIndex then MinRow := Column1.RowIndex else MinRow := Column2.RowIndex; if Column1.RowIndex + Column1.RowCount < Column2.RowIndex + Column2.RowCount then MaxRow := Column1.RowIndex + Column1.RowCount - 1 else MaxRow := Column2.RowIndex + Column2.RowCount - 1; Result := False; for I := MinRow to MaxRow do with Items[I] do begin I1 := IndexOf(Column1); if I1 = -1 then Continue; I2 := IndexOf(Column2); if I2 = -1 then Continue; if I1 + 1 <> I2 then Exit; end; Result := True; end; end; procedure TdxMasterViewLayout.CalcAddInWidths; var I: Integer; Column: TdxMasterViewColumn; begin for I := 0 to FLevel.ColumnCount - 1 do FLevel.Columns[I].FAddInWidth := 0; if FLevel.HasExpandButton and FHorizontal then for I := 0 to Count - 1 do if Items[I].Count <> 0 then begin Column := Columns[I, 0]; if (Column.FAddInWidth = 0) and IsColumnFirst(Column) then Column.FAddInWidth := LevelIndent; end; end; procedure TdxMasterViewLayout.CalcColWidths; var DC: HDC; I, J, W: Integer; AHeaderWidthAssigned, AutoWidth: Boolean; begin FRowCount := 0; AutoWidth := FLevel.Control.AutoColumnWidth; DC := GetDC(0); try for I := 0 to Count - 1 do begin AHeaderWidthAssigned := HeaderWidthAssigned[I]; if not AHeaderWidthAssigned then FHeaderWidths[I] := 0; FContentNonScaledWidths[I] := 0; FContentWidths[I] := 0; J := Items[I].Count; if J > FRowCount then FRowCount := J; for J := 0 to J - 1 do begin if not AHeaderWidthAssigned then begin W := Columns[J, I].GetHeaderBestFitWidth(DC, True); if W > FHeaderWidths[I] then FHeaderWidths[I] := W; end; W := Columns[J, I].NonScaledWidth; if W > FContentNonScaledWidths[I] then FContentNonScaledWidths[I] := W; if AutoWidth then W := Columns[J, I].VisibleWidth; if W > FContentWidths[I] then FContentWidths[I] := W; end; end; finally ReleaseDC(0, DC); end; end; procedure TdxMasterViewLayout.CalcLogicalOffsets(Mode: Byte); var I, J: Integer; begin if Mode in [0, 1] then for I := 0 to Count - 1 do for J := 0 to Items[I].Count - 1 do with TdxMasterViewColumn(Items[I][J]) do FLogicalOffset := -1; if Mode in [0, 2] then for I := 0 to Count - 1 do for J := 0 to Items[I].Count - 1 do with TdxMasterViewColumn(Items[I][J]) do if FLogicalOffset = -1 then FLogicalOffset := LogicalOffset; end; procedure TdxMasterViewLayout.CalcNeighbours; var I: Integer; Column: TdxMasterViewColumn; begin for I := 0 to FLevel.VisibleColumnCount - 1 do begin Column := FLevel.VisibleColumns[I]; Column.FLeftColumns.Clear; Column.FRightColumns.Clear; if FHorizontal then begin HasLeftNeighbours(Column, Column.FLeftColumns, False); HasRightNeighbours(Column, Column.FRightColumns, False); end; { Column.FLeftColumns.Clear; HasLeftNeighbours(Column, Column.FLeftColumns, False); Column.FRightColumns.Clear; HasRightNeighbours(Column, Column.FRightColumns, False);} end; end; procedure TdxMasterViewLayout.CalcOptimizedValues; begin CalcNeighbours; CalcLogicalOffsets(0); end; procedure TdxMasterViewLayout.CalcVisibleWidth; begin if FLevel.Control.AutoColumnWidth and (FLevel.VisibleColumnCount <> 0) then FVisibleWidth := FLevel.Control.ScrollableWidth - FLevel.Indent else FVisibleWidth := GetVisibleWidth; end; procedure TdxMasterViewLayout.CheckColumnsWidths(FixedColumn: TdxMasterViewColumn); procedure CheckColumnsAutoWidthsWhenHorizontal; var AColumns: TList; NewWidths, FreeSpaces: PIntArray; ColumnLocked: PBoolArray; NonScaledSize, AvailableSpace, I, NonScaledOffset, NonScaledW, W, CheckW: Integer; Column: TdxMasterViewColumn; function GetAdvanceNewWidth: Integer; begin NonScaledOffset := Column.NonScaledLogicalOffset; NonScaledW := Column.NonScaledWidth; if OneOnLine(Column) then Result := AvailableSpace else Result := MulDiv(NonScaledOffset + NonScaledW, AvailableSpace, NonScaledSize) - MulDiv(NonScaledOffset, AvailableSpace, NonScaledSize); end; procedure ChangeWidth(Column: TdxMasterViewColumn; Delta: Integer; Direction: TdxMasterViewDirection); var VI, W, CheckW, CurDelta, MaxW_L, MaxW_R, DeltaL, DeltaR, I: Integer; ListL, ListR, CollapsingList, ExpandingList: TList; OppositeDirection: TdxMasterViewDirection; function FindMaxWidth(List: TList): Integer; var I, W: Integer; begin Result := 0; for I := 0 to List.Count - 1 do begin W := TdxMasterViewColumn(List[I]).NonScaledWidth; if W > Result then Result := W; end; end; procedure CheckAvailableSpace(List: TList; Direction: TdxMasterViewDirection; var Delta1, Delta2: Integer); var AvailSpace: Integer; function FindAvailableSpace(List: TList): Integer; var AList: TList; I, Space: Integer; begin Result := MaxInt; AList := TList.Create; try for I := 0 to List.Count - 1 do begin if Direction = dirLeft then HasLeftNeighbours(List[I], AList, True) else HasRightNeighbours(List[I], AList, True); with TdxMasterViewColumn(List[I]) do if CurDelta > 0 then Space := NewWidths[VisibleIndex] - VisibleMinWidth else Space := VisibleMaxWidth - NewWidths[VisibleIndex]; Inc(Space, FindAvailableSpace(AList)); if Space < Result then Result := Space; end; finally AList.Free; if Result = MaxInt then Result := 0; end; end; begin AvailSpace := FindAvailableSpace(List); if CurDelta < 0 then AvailSpace := -AvailSpace; if (CurDelta > 0) and (Delta1 > AvailSpace) or (CurDelta < 0) and (Delta1 < AvailSpace) then begin Inc(Delta2, Delta1 - AvailSpace); Delta1 := AvailSpace; end; end; begin VI := Column.VisibleIndex; if ColumnLocked[VI] then Exit; if FreeSpaces[VI] * Delta > 0 then CurDelta := Delta else begin W := NewWidths[VI]; if Delta < 0 then CheckW := Column.VisibleMinWidth else CheckW := Column.VisibleMaxWidth; if (Delta < 0) and (W + Delta < CheckW) or (Delta > 0) and (W + Delta > CheckW) then CurDelta := CheckW - W else CurDelta := Delta; end; Dec(Delta, CurDelta); Inc(NewWidths[VI], CurDelta); if FreeSpaces[VI] <> 0 then if (FreeSpaces[VI] + CurDelta) * FreeSpaces[VI] < 0 then FreeSpaces[VI] := 0 else Inc(FreeSpaces[VI], CurDelta); ColumnLocked[VI] := True; ListL := TList.Create; ListR := TList.Create; try HasLeftNeighbours(Column, ListL, True); HasRightNeighbours(Column, ListR, True); if Direction = dirNone then begin MaxW_L := FindMaxWidth(ListL); MaxW_R := FindMaxWidth(ListR); DeltaL := MulDiv(CurDelta, MaxW_L, MaxW_L + MaxW_R); DeltaR := CurDelta - DeltaL; CheckAvailableSpace(ListL, dirLeft, DeltaL, DeltaR); CheckAvailableSpace(ListR, dirRight, DeltaR, DeltaL); for I := 0 to ListL.Count - 1 do ChangeWidth(ListL[I], -DeltaL, dirLeft); for I := 0 to ListR.Count - 1 do ChangeWidth(ListR[I], -DeltaR, dirRight); end else begin if Direction = dirLeft then begin CollapsingList := ListR; ExpandingList := ListL; OppositeDirection := dirRight; end else begin CollapsingList := ListL; ExpandingList := ListR; OppositeDirection := dirLeft; end; for I := 0 to CollapsingList.Count - 1 do ChangeWidth(CollapsingList[I], -(Delta + CurDelta), OppositeDirection); if Delta <> 0 then for I := 0 to ExpandingList.Count - 1 do ChangeWidth(ExpandingList[I], Delta, Direction); end; finally ListR.Free; ListL.Free; ColumnLocked[VI] := False; end; end; begin if not FLevel.Visible then Exit; AColumns := TList.Create; AColumns.Count := FLevel.VisibleColumnCount; Move(FLevel.FVisibleColumns.List^, AColumns.List^, AColumns.Count * SizeOf(Pointer)); GetMem(NewWidths, AColumns.Count * SizeOf(Integer)); GetMem(FreeSpaces, AColumns.Count * SizeOf(Integer)); GetMem(ColumnLocked, AColumns.Count * SizeOf(Boolean)); try NonScaledSize := NonScaledWidth; AvailableSpace := VisibleWidth; for I := 0 to AColumns.Count - 1 do begin Column := AColumns[I]; W := GetAdvanceNewWidth; CheckW := Column.VisibleMinWidth; if W < CheckW then FreeSpaces[I] := W - CheckW else begin CheckW := Column.VisibleMaxWidth; if W > CheckW then FreeSpaces[I] := W - CheckW else FreeSpaces[I] := 0; end; NewWidths[I] := W; end; for I := 0 to AColumns.Count - 1 do begin Column := AColumns[I]; if FreeSpaces^[Column.VisibleIndex] <> 0 then begin FillChar(ColumnLocked^, AColumns.Count * SizeOf(Boolean), 0); ChangeWidth(Column, -FreeSpaces[Column.VisibleIndex], dirNone); end; end; for I := 0 to AColumns.Count - 1 do TdxMasterViewColumn(AColumns[I]).InternalVisibleWidth := NewWidths[I]; finally FreeMem(ColumnLocked, AColumns.Count * SizeOf(Boolean)); FreeMem(FreeSpaces, AColumns.Count * SizeOf(Integer)); FreeMem(NewWidths, AColumns.Count * SizeOf(Integer)); AColumns.Free; end; end; procedure CheckColumnsAutoWidthsWhenVertical; var NewWidths, ContentMinWidths: PIntArray; NonScaledSize, AvailableSpace, FixedCol, FixedI1, FixedI2, I, NonScaledOffset, NonScaledW, W, CheckW: Integer; function ContentNonScaledOffset(const ACol: Integer): Integer; var I: Integer; begin Result := 0; for I := 0 to ACol - 1 do if NewWidths[I] = -1 then Inc(Result, ContentNonScaledWidths[I]); end; function GetAdvanceNewWidth: Integer; begin NonScaledOffset := ContentNonScaledOffset(I); NonScaledW := ContentNonScaledWidths[I]; if Count = 1 then Result := AvailableSpace else Result := MulDiv(NonScaledOffset + NonScaledW, AvailableSpace, NonScaledSize) - MulDiv(NonScaledOffset, AvailableSpace, NonScaledSize); end; procedure CheckWidths(ForMin: Boolean); begin I := 0; NonScaledOffset := 0; while I < Count do begin if NewWidths[I] = -1 then begin NonScaledW := ContentNonScaledWidths[I]; W := MulDiv(NonScaledOffset + NonScaledW, AvailableSpace, NonScaledSize) - MulDiv(NonScaledOffset, AvailableSpace, NonScaledSize); if ForMin then CheckW := ContentMinWidths[I] else CheckW := ContentMaxWidths[I]; if ForMin and (W < CheckW) or not ForMin and (W > CheckW) then begin Dec(NonScaledSize, NonScaledW); Dec(AvailableSpace, CheckW); NewWidths[I] := CheckW; I := 0; NonScaledOffset := 0; Continue; end; Inc(NonScaledOffset, NonScaledW); end; Inc(I); end; end; begin if not FLevel.Visible then Exit; GetMem(NewWidths, Count * SizeOf(Integer)); GetMem(ContentMinWidths, Count * SizeOf(Integer)); try NonScaledSize := NonScaledWidth; AvailableSpace := VisibleWidth; if FLevel.HasExpandButton then begin Dec(NonScaledSize, LevelIndent); Dec(AvailableSpace, LevelIndent); end; for I := 0 to Count - 1 do begin W := HeaderWidths[I]; Dec(NonScaledSize, W); Dec(AvailableSpace, W); ContentMinWidths[I] := Self.ContentMinWidths[I]; end; if FixedColumn = nil then FixedCol := -1 else FixedCol := FixedColumn.ColIndex; if (FixedCol <> -1) and (FixedCol = Count - 1) then begin FixedI1 := FixedCol; FixedI2 := FixedCol; end else begin FixedI1 := 0; FixedI2 := FixedCol; end; for I := 0 to Count - 1 do if (FixedI1 <= I) and (I <= FixedI2) then begin NonScaledW := ContentNonScaledWidths[I]; W := ContentWidths[I]; Dec(NonScaledSize, NonScaledW); Dec(AvailableSpace, W); NewWidths[I] := W; end else begin W := GetAdvanceNewWidth; CheckW := ContentMinWidths[I]; if W < CheckW then begin Dec(NonScaledSize, NonScaledW); Dec(AvailableSpace, CheckW); NewWidths[I] := CheckW; end else NewWidths[I] := -1; end; CheckWidths(True); CheckWidths(False); NonScaledOffset := 0; for I := 0 to Count - 1 do begin W := NewWidths[I]; if W = -1 then begin NonScaledW := ContentNonScaledWidths[I]; W := MulDiv(NonScaledOffset + NonScaledW, AvailableSpace, NonScaledSize) - MulDiv(NonScaledOffset, AvailableSpace, NonScaledSize); Inc(NonScaledOffset, NonScaledW); end; ContentInternalVisibleWidths[I] := W; end; finally FreeMem(ContentMinWidths, Count * SizeOf(Integer)); FreeMem(NewWidths, Count * SizeOf(Integer)); end; end; procedure CheckColumnsFreeWidths(UseVisibleValues: Boolean); var List: TList; I, J, K, MustBeOffset, FreeSpace, ASpace: Integer; Column: TdxMasterViewColumn; begin if FHorizontal then begin List := TList.Create; try for I := 0 to Count - 1 do for J := Items[I].Count - 1 downto 0 do begin Column := Columns[I, J]; if Column.RowIndex = I then begin with Column do if UseVisibleValues then MustBeOffset := LogicalOffset + VisibleWidth else MustBeOffset := NonScaledLogicalOffset + NonScaledWidth; if HasRightNeighbours(Column, List, False) then begin FreeSpace := MaxInt; for K := 0 to List.Count - 1 do begin with TdxMasterViewColumn(List[K]) do if UseVisibleValues then ASpace := LogicalOffset else ASpace := NonScaledLogicalOffset; Dec(ASpace, MustBeOffset); if ASpace < FreeSpace then FreeSpace := ASpace; end; end else if FLevel.OccupyRestSpace or UseVisibleValues then FreeSpace := FVisibleWidth - MustBeOffset else FreeSpace := 0; if UseVisibleValues then ASpace := Column.VisibleWidth else ASpace := Column.NonScaledWidth; Column.InternalVisibleWidth := ASpace + FreeSpace; end; end; finally List.Free; end; end else for I := 0 to Count - 1 do for J := 0 to Items[I].Count - 1 do with Columns[J, I] do if RowIndex = J then InternalVisibleWidth := ContentWidths[I]; end; begin if (FLevel.FAssignWidthsLockCount <> 0) and (FixedColumn = nil) or (FUpdateLockCount <> 0) or FLevel.IsLoading then Exit; CalcLogicalOffsets(1); if FHorizontal then CalcAddInWidths; CalcVisibleWidth; if FLevel.Control.AutoColumnWidth then if FHorizontal then begin CheckColumnsAutoWidthsWhenHorizontal; CalcLogicalOffsets(2); CheckColumnsFreeWidths(True); end else begin CheckColumnsAutoWidthsWhenVertical; CalcColWidths; CalcLogicalOffsets(2); CheckColumnsFreeWidths(False); end else begin CheckColumnsFreeWidths(False); CalcLogicalOffsets(2); end; end; procedure TdxMasterViewLayout.CheckCount; var I: Integer; begin if FUpdateLockCount <> 0 then Exit; for I := Count - 1 downto 0 do if Items[I].Count = 0 then Delete(I) else Break; for I := 0 to Count - 1 do if Items[0].Count = 0 then Delete(0) else Break; end; procedure TdxMasterViewLayout.Clear; var I: Integer; begin for I := Count - 1 downto 0 do Items[I].Clear; end; procedure TdxMasterViewLayout.ClearOptimizedValues; var I: Integer; begin for I := 0 to FLevel.VisibleColumnCount - 1 do with FLevel.VisibleColumns[I] do begin FLogicalOffset := -1; FLeftColumns.Clear; FRightColumns.Clear; end; end; function TdxMasterViewLayout.ColFromX(X: Integer): Integer; begin if not FHorizontal then for Result := 0 to Count - 1 do if (ColOffsets[Result] <= X) and (X < ColOffsets[Result + 1]) then Exit; Result := -1; end; procedure TdxMasterViewLayout.CreateColArrays; begin GetMem(FContentNonScaledWidths, Count * SizeOf(Integer)); GetMem(FContentWidths, Count * SizeOf(Integer)); GetMem(FHeaderWidths, Count * SizeOf(Integer)); GetMem(FHeaderWidthAssigned, Count * SizeOf(Boolean)); FillChar(FHeaderWidthAssigned^, Count * SizeOf(Boolean), 0); end; procedure TdxMasterViewLayout.Delete(Index: Integer); var I, J: Integer; begin Items[Index].Free; FLines.Delete(Index); if FHorizontal then FRowCount := Count; for I := Index to Count - 1 do for J := 0 to Items[I].Count - 1 do if FHorizontal then with Columns[I, J] do if FRowIndex = I + 1 then Dec(FRowIndex) else else with Columns[J, I] do if FColIndex = I + 1 then Dec(FColIndex); end; procedure TdxMasterViewLayout.DestroyColArrays; begin if FContentNonScaledWidths <> nil then begin FreeMem(FContentNonScaledWidths); FContentNonScaledWidths := nil; end; if FContentWidths <> nil then begin FreeMem(FContentWidths); FContentWidths := nil; end; if FHeaderWidths <> nil then begin FreeMem(FHeaderWidths); FHeaderWidths := nil; end; if FHeaderWidthAssigned <> nil then begin FreeMem(FHeaderWidthAssigned); FHeaderWidthAssigned := nil; end; end; function TdxMasterViewLayout.HasLeftNeighbours(AColumn: TdxMasterViewColumn; AList: TList; ExactResult: Boolean): Boolean; var I, J: Integer; begin if (AColumn.FLeftColumns.Count = 0) or ExactResult then begin Result := False; if AList <> nil then AList.Clear; for I := AColumn.RowIndex to AColumn.RowIndex + AColumn.RowCount - 1 do begin J := Items[I].IndexOf(AColumn); if (J > 0) and AreNeighbours(Columns[I, J - 1], AColumn, ExactResult) then begin Result := True; if AList = nil then Exit else if AList.IndexOf(Columns[I, J - 1]) = -1 then //!!! AList.Add(Columns[I, J - 1]); end; end; end else begin Result := True; if AList <> nil then with AColumn do begin AList.Count := FLeftColumns.Count; Move(FLeftColumns.List^, AList.List^, FLeftColumns.Count * SizeOf(Pointer)); end; end; end; { function TdxMasterViewLayout.HasLinkedColumns(AColumn: TdxMasterViewColumn; AList: TList): Boolean; var List, LinkedColumns: TList; I, J: Integer; begin Result := False; AList.Clear; List := TList.Create; LinkedColumns := TList.Create; try if HasRightNeighbours(AColumn, List, True) then for I := 0 to List.Count - 1 do if HasLeftNeighbours(List[I], LinkedColumns, True) then begin with AList do Capacity := Capacity + LinkedColumns.Count; for J := 0 to LinkedColumns.Count - 1 do if LinkedColumns[J] <> AColumn then AList.Add(LinkedColumns[J]); end else else if FLevel.OccupyRestSpace then begin RetrieveLastColumns(AList); for I := AList.Count - 1 downto 0 do if AList[I] = AColumn then AList.Delete(I); end else Exit; finally Result := AList.Count <> 0; LinkedColumns.Free; List.Free; end; end; } function TdxMasterViewLayout.HasRightNeighbours(AColumn: TdxMasterViewColumn; AList: TList; ExactResult: Boolean): Boolean; var I, J: Integer; begin if (AColumn.FRightColumns.Count = 0) or ExactResult then begin Result := False; if AList <> nil then AList.Clear; for I := AColumn.RowIndex to AColumn.RowIndex + AColumn.RowCount - 1 do begin J := Items[I].IndexOf(AColumn); if (J < Items[I].Count - 1) and AreNeighbours(AColumn, Columns[I, J + 1], ExactResult) then begin Result := True; if AList = nil then Exit else if AList.IndexOf(Columns[I, J + 1]) = -1 then //!!! AList.Add(Columns[I, J + 1]); end; end; end else begin Result := True; if AList <> nil then with AColumn do begin AList.Count := FRightColumns.Count; Move(FRightColumns.List^, AList.List^, FRightColumns.Count * SizeOf(Pointer)); end; end; end; procedure TdxMasterViewLayout.HeaderWidthChanged; begin FLevel.WidthChangedEx; end; procedure TdxMasterViewLayout.Insert(Index, ACount: Integer); var I, J: Integer; PrevHeaderWidths: PIntArray; PrevHeaderWidthAssigned: PBoolArray; begin for I := 0 to ACount - 1 do FLines.Insert(Index, TList.Create); for I := Count - 1 downto Index + ACount do for J := 0 to Items[I].Count - 1 do if FHorizontal then with Columns[I, J] do if FRowIndex = I - ACount then Inc(FRowIndex, ACount) else else with Columns[J, I] do if FColIndex = I - ACount then Inc(FColIndex, ACount); if FHorizontal then FRowCount := Count else begin PrevHeaderWidths := FHeaderWidths; PrevHeaderWidthAssigned := FHeaderWidthAssigned; FHeaderWidths := nil; FHeaderWidthAssigned := nil; DestroyColArrays; CreateColArrays; Move(PrevHeaderWidths^, FHeaderWidths^, Index * SizeOf(Integer)); Move(PrevHeaderWidthAssigned^, FHeaderWidthAssigned^, Index * SizeOf(Boolean)); I := Count - (Index + ACount); Move(PrevHeaderWidths[Index], FHeaderWidths[Index + ACount], I * SizeOf(Integer)); Move(PrevHeaderWidthAssigned[Index], FHeaderWidthAssigned[Index + ACount], I * SizeOf(Boolean)); FreeMem(PrevHeaderWidths); FreeMem(PrevHeaderWidthAssigned); end; end; procedure TdxMasterViewLayout.InsertAtPos(ARow, APos: Integer; AColumn: TdxMasterViewColumn); var ACol{, AMinOffset, Delta, I, AvailSpace}: Integer; begin ACol := GetNearestCol(ARow, APos); { while ACol <> 0 do begin with Columns[ARow, ACol - 1] do AMinOffset := MinLogicalOffset + VisibleMinWidth; if AMinOffset <= APos then begin with Columns[ARow, ACol - 1] do Delta := LogicalOffset + VisibleWidth - APos; if Delta > 0 then for I := ACol - 1 downto 0 do begin with Columns[ARow, I] do AvailSpace := VisibleWidth - VisibleMinWidth; if AvailSpace > Delta then AvailSpace := Delta; with Columns[ARow, I] do InternalVisibleWidth := VisibleWidth - AvailSpace; Dec(Delta, AvailSpace); if Delta = 0 then Break; end; Break; end else Dec(ACol); end;} Items[ARow].Insert(ACol, AColumn); end; procedure TdxMasterViewLayout.InsertColumn(ARow, ACol: Integer; AColumn: TdxMasterViewColumn); var I, Offset: Integer; begin // if not AColumn.Visible then Exit; if FHorizontal then begin RequireCount(ARow + AColumn.RowCount); if ACol > Items[ARow].Count then ACol := Items[ARow].Count; Offset := -1; BeginUpdate; //!!! try for I := ARow to ARow + AColumn.RowCount - 1 do if I = ARow then Items[I].Insert(ACol, AColumn) else begin if Offset = -1 then Offset := AColumn.LogicalOffset; InsertAtPos(I, Offset, AColumn); end; finally EndUpdate; end; end else begin RequireCount(ACol + 1); ARow := GetNearestRow(ACol, ARow); //if ARow > Items[ACol].Count then ARow := Items[ACol].Count; BeginUpdate; //!!! try for I := ARow to ARow + AColumn.RowCount - 1 do Items[ACol].Insert(I, AColumn); finally EndUpdate; end; end; end; procedure TdxMasterViewLayout.InsertColumnAtPos(ARow, APos: Integer; AColumn: TdxMasterViewColumn); var I: Integer; begin // if not AColumn.Visible or not FHorizontal then Exit; RequireCount(ARow + AColumn.RowCount); for I := ARow to ARow + AColumn.RowCount - 1 do InsertAtPos(I, APos, AColumn); end; procedure TdxMasterViewLayout.InternalRemoveFreeSpace; var I: Integer; begin for I := Count - 1 downto 0 do if Items[I].Count = 0 then Delete(I); end; procedure TdxMasterViewLayout.RemoveColumn(AColumn: TdxMasterViewColumn); var I: Integer; begin with AColumn do if (RowIndex <> -1) and not IsLoading {FLevel} then begin Self.BeginUpdate; try if FHorizontal then for I := RowIndex to RowIndex + RowCount - 1 do Items[I].Remove(AColumn) else with Items[ColIndex] do for I := RowIndex + RowCount - 1 downto RowIndex do Delete(I); finally Self.EndUpdate; end; end; // CheckCount; end; procedure TdxMasterViewLayout.RetrieveLastColumns(AList: TList); var I: Integer; Column: TdxMasterViewColumn; begin AList.Clear; for I := 0 to Count - 1 do if Items[I].Count <> 0 then begin Column := Items[I].Last; if (AList.IndexOf(Column) = -1) and not HasRightNeighbours(Column, nil, True) then AList.Add(Column); end; end; function TdxMasterViewLayout.GetNearestCol(ARow, AOffset: Integer): Integer; var CurOffset, I: Integer; begin if ARow >= Count then Result := 0 else begin Result := Items[ARow].Count; for I := 0 to Result - 1 do begin with Columns[ARow, I] do CurOffset := LogicalOffset; if AOffset <= CurOffset then begin Result := I; Break; end; with Columns[ARow, I] do Inc(CurOffset, VisibleWidth); if AOffset < CurOffset then begin Result := I + 1; Break; end; end; end; end; function TdxMasterViewLayout.GetNearestRow(ACol, ARow: Integer): Integer; var ACount: Integer; begin if ACol >= Count then Result := 0 else begin ACount := Items[ACol].Count; if ARow >= ACount then Result := ACount else begin Result := ARow; while (Result < ACount) and (Columns[Result, ACol].RowIndex <> Result) do Inc(Result); end; end; end; procedure TdxMasterViewLayout.RequireCount(ACount: Integer); begin if ACount > Count then Count := ACount; end; procedure TdxMasterViewLayout.ViewModeChanged; var I: Integer; begin FHorizontal := FLevel.Horizontal; BeginUpdate; try for I := Count - 1 downto 1 do Delete(I); if Count = 1 then Items[0].Clear; if FHorizontal then begin FRowCount := Count; for I := 0 to FLevel.VisibleColumnCount - 1 do begin FLevel.VisibleColumns[I].FRowIndex := 0; InsertColumn(0, Items[0].Count, FLevel.VisibleColumns[I]); end; DestroyColArrays; end else begin for I := 0 to FLevel.VisibleColumnCount - 1 do begin FLevel.VisibleColumns[I].FColIndex := 0; InsertColumn(Items[0].Count, 0, FLevel.VisibleColumns[I]); end; CreateColArrays; end; CalcAddInWidths; finally EndUpdate; end; end; procedure TdxMasterViewLayout.BeginUpdate; begin if FUpdateLockCount = 0 then ClearOptimizedValues; Inc(FUpdateLockCount); end; procedure TdxMasterViewLayout.CancelUpdate; begin if FUpdateLockCount > 0 then begin Dec(FUpdateLockCount); if FUpdateLockCount = 0 then CalcOptimizedValues; end; end; procedure TdxMasterViewLayout.ChangeCol(AColumn: TdxMasterViewColumn; ACol: Integer); var ARow: Integer; begin if ACol < 0 then ACol := 0; if AColumn.Visible and not FLevel.IsLoading then begin BeginUpdate; try ARow := AColumn.RowIndex; RemoveColumn(AColumn); if FHorizontal then if ACol > Items[AColumn.RowIndex].Count then ACol := Items[AColumn.RowIndex].Count else else if ACol > Count then ACol := Count; AColumn.FColIndex := ACol; if FHorizontal then ARow := AColumn.RowIndex else if ACol = Count then ARow := 0 else //ARow := Items[ACol].Count; begin if ARow < 0 then ARow := 0; if ARow > Items[ACol].Count then ARow := Items[ACol].Count; end; InsertColumn(ARow, ACol, AColumn); finally EndUpdate; end; end else AColumn.FColIndex := ACol; end; procedure TdxMasterViewLayout.ChangeHeaderWidth(ACol: Integer; Value: Integer; FixCol: Boolean); var MarginValue: Integer; begin MarginValue := HeaderMinWidths[ACol]; if Value < MarginValue then Value := MarginValue; MarginValue := HeaderMaxWidths[ACol]; if Value > MarginValue then Value := MarginValue; if FixCol then begin MarginValue := HeaderDynamicMinWidths[ACol]; if Value < MarginValue then Value := MarginValue; MarginValue := HeaderDynamicMaxWidths[ACol]; if Value > MarginValue then Value := MarginValue; end; if FHeaderWidths[ACol] <> Value then begin FHeaderWidths[ACol] := Value; HeaderWidthAssigned[ACol] := True; if FixCol and (ACol > 0) and FLevel.Control.AutoColumnWidth then begin CheckColumnsWidths(Columns[0, ACol - 1]); with FLevel do begin BeginAssignWidths; try AssignColumnWidths; finally CancelAssignWidths; end; end; end; HeaderWidthChanged; end; end; procedure TdxMasterViewLayout.ChangeRow(AColumn: TdxMasterViewColumn; ARow: Integer; FixedPos: Boolean); var APos: Integer; begin if ARow < 0 then ARow := 0; if AColumn.Visible and not FLevel.IsLoading then begin if FHorizontal and (ARow > Count) then ARow := Count; if AColumn.RowIndex <> ARow then begin BeginUpdate; try if FHorizontal then if FixedPos then APos := AColumn.LogicalOffset else APos := GetNearestCol(ARow, AColumn.LogicalOffset) else APos := AColumn.ColIndex; RemoveColumn(AColumn); if not FHorizontal then ARow := GetNearestRow(APos, ARow); AColumn.FRowIndex := ARow; if FHorizontal and FixedPos then InsertColumnAtPos(ARow, APos, AColumn) else InsertColumn(ARow, APos, AColumn); finally EndUpdate; end; end; end else AColumn.FRowIndex := ARow; end; procedure TdxMasterViewLayout.ChangeRowAndRowCount(AColumn: TdxMasterViewColumn; ARow, ARowCount: Integer; FixedPos: Boolean); var APos: Integer; begin if ARow < 0 then ARow := 0; AColumn.CheckRowCount(ARowCount); if AColumn.Visible and not FLevel.IsLoading then begin if FHorizontal and (ARow > Count) then ARow := Count; if (AColumn.RowIndex <> ARow) or (AColumn.RowCount <> ARowCount) then begin BeginUpdate; try if FHorizontal then if FixedPos then APos := AColumn.LogicalOffset else APos := GetNearestCol(ARow, AColumn.LogicalOffset) else APos := AColumn.ColIndex; RemoveColumn(AColumn); if not FHorizontal then ARow := GetNearestRow(APos, ARow); with AColumn do begin FRowIndex := ARow; FRowCount := ARowCount; end; if FHorizontal and FixedPos then InsertColumnAtPos(ARow, APos, AColumn) else InsertColumn(ARow, APos, AColumn); finally EndUpdate; end; end; end else with AColumn do begin FRowIndex := ARow; FRowCount := ARowCount; end; end; procedure TdxMasterViewLayout.ChangeRowCount(AColumn: TdxMasterViewColumn; ARowCount: Integer); var I, AOffset: Integer; begin with AColumn do begin CheckRowCount(ARowCount); if RowCount <> ARowCount then begin if Visible and not FLevel.IsLoading then begin Self.BeginUpdate; try if ARowCount < RowCount then for I := RowIndex + RowCount - 1 downto RowIndex + ARowCount do if FHorizontal then Self.Items[I].Remove(AColumn) else Self.Items[ColIndex].Delete(I) else begin if FHorizontal then begin RequireCount(RowIndex + ARowCount); AOffset := LogicalOffset; end else AOffset := 0; for I := RowIndex + RowCount to RowIndex + ARowCount - 1 do if FHorizontal then Self.InsertAtPos(I, AOffset, AColumn) else Self.Items[ColIndex].Insert(I, AColumn); end; FRowCount := ARowCount; finally Self.EndUpdate; end; end else FRowCount := ARowCount; end; end; end; procedure TdxMasterViewLayout.EndUpdate; begin if FUpdateLockCount > 0 then begin Dec(FUpdateLockCount); if FUpdateLockCount = 0 then begin if FHorizontal then CheckCount else InternalRemoveFreeSpace; FLevel.WidthChangedEx; end; end; end; function TdxMasterViewLayout.GetBestFitWidth(ACol: Integer): Integer; var I, AValue: Integer; begin Result := 0; for I := 0 to Items[ACol].Count - 1 do with Columns[I, ACol] do if RowIndex = I then begin AValue := GetBestFitWidth; if AValue > Result then Result := AValue; end; end; function TdxMasterViewLayout.IndexOf(AColumn: TdxMasterViewColumn; ALine: Integer): Integer; begin if (ALine < 0) or (ALine >= Count) then Result := -1 else Result := TList(FLines[ALine]).IndexOf(AColumn); end; function TdxMasterViewLayout.IsColumnFirst(AColumn: TdxMasterViewColumn): Boolean; begin if FHorizontal then Result := not HasLeftNeighbours(AColumn, nil, True) else Result := AColumn.RowIndex = 0;//IndexOf(AColumn, AColumn.ColIndex) = 0; end; function TdxMasterViewLayout.IsColumnLast(AColumn: TdxMasterViewColumn): Boolean; begin if FHorizontal then Result := not HasRightNeighbours(AColumn, nil, True) else with AColumn do Result := RowIndex + RowCount = Self.Items[ColIndex].Count; end; function TdxMasterViewLayout.OneOnLine(AColumn: TdxMasterViewColumn): Boolean; var I: Integer; begin if FHorizontal then begin Result := False; with AColumn do for I := RowIndex to RowIndex + RowCount - 1 do if Self.Items[I].Count <> 1 then Exit; Result := True; end else with AColumn do Result := RowCount = Self.Items[ColIndex].Count; end; procedure TdxMasterViewLayout.RemoveFreeSpace; begin BeginUpdate; try InternalRemoveFreeSpace; finally EndUpdate; end; end; { TdxMasterViewLevel } constructor TdxMasterViewLevel.Create(AOwner: TComponent); begin inherited; FAbsoluteIndex := -1; FCanUseSmartReload := True; FColumns := TList.Create; FDataLink := TdxMasterViewDataLink.Create(Self); FDeleteConfirmCaptionText := LoadStr(dxsMVDeleteConfirmCaptionText); FDeleteConfirmText := LoadStr(dxsMVDeleteConfirmText); FDetailKeyFields := TList.Create; FExtLineWidth := DefaultLineWidth; FGridLinesColor := clBtnShadow; FGroupColumns := TList.Create; FHorizontal := True; FIDFields := TList.Create; FIndex := -1; FItems := TList.Create; FLayout := TdxMasterViewLayout.Create(Self); FLevelSeparatorColor := clWindowText; FLineWidth := DefaultLineWidth; FMasterKeyFields := TList.Create; FMultipleDeleteConfirmText := LoadStr(dxsMVMultipleDeleteConfirmText); FOptionsBehavior := [lobDblClkExpanding]; FOptionsCustomize := [locColumnMoving, locColumnHorSizing, locColumnVerSizing, locColumnSorting, locColumnGrouping, locShowColumnOnUngrouping]; FOptionsCustomizeBox := [loxCaption, loxColumns, loxGrid, loxGroupByBox, loxHeader, loxViewMode]; FOptionsDB := [lodConfirmDelete, lodSmartLoad, lodSmartReload]; FOptionsHeader := [lohForFirstNode, lohForFirstVisibleNode, lohAfterExpandedNode]; FOptionsView := [lovGrid, lovGridWithPreview, lovHeader, lovOccupyRestSpace]; FPreviewLeftIndent := dxMVPreviewLeftIndent; FPreviewMaxLineCount := dxMVPreviewMaxLineCount; FPreviewRightIndent := dxMVPreviewRightIndent; FRowSeparatorColor := clBtnShadow; FSortedColumns := TList.Create; FSummaryColumns := TList.Create; FVisibleColumns := TList.Create; CalcParents; end; destructor TdxMasterViewLevel.Destroy; begin Destroying; if FParent <> nil then FParent.RemoveItem(Self); DestroyColumns; Clear; HeaderStyle := nil; ContentStyle := nil; FooterStyle := nil; PreviewStyle := nil; CaptionStyle := nil; GroupStyle := nil; GroupByBoxStyle := nil; if FParents <> nil then begin FreeMem(FParents, FParentCount * SizeOf(TdxMasterViewLevel)); FParentCount := 0; FParents := nil; end; FMasterKeyFields.Free; FLayout.Free; FIDFields.Free; FGroupColumns.Free; FDetailKeyFields.Free; FDataLink.Free; FVisibleColumns.Free; FSummaryColumns.Free; FSortedColumns.Free; FItems.Free; FColumns.Free; inherited; end; function TdxMasterViewLevel.GetActive: Boolean; begin Result := (FParent = nil) or (DataSet <> nil) and DataSet.Active and (IDFieldCount <> 0) and (DontFilterRecords or (DetailKeyFieldCount <> 0) and (DetailKeyFieldCount = MasterKeyFieldCount)); end; function TdxMasterViewLevel.GetCaptionBrush: HBRUSH; begin if (CaptionStyle <> nil) and (svColor in CaptionStyle.AssignedValues) then Result := CaptionStyle.Brush else Result := GetSysColorBrush(COLOR_BTNFACE);//FControl.CaptionBrush; end; function TdxMasterViewLevel.GetCaptionColor: TColor; begin if (CaptionStyle <> nil) and (svColor in CaptionStyle.AssignedValues) then Result := CaptionStyle.Color else Result := clBtnFace;//FControl.CaptionColor; end; function TdxMasterViewLevel.GetCaptionFont: TFont; begin if (CaptionStyle <> nil) and (svFont in CaptionStyle.AssignedValues) then Result := CaptionStyle.Font else Result := FControl.Font;//FControl.CaptionFont; end; function TdxMasterViewLevel.GetColumn(Index: Integer): TdxMasterViewColumn; begin Result := TdxMasterViewColumn(FColumns[Index]); end; function TdxMasterViewLevel.GetColumnCount: Integer; begin Result := FColumns.Count; end; function TdxMasterViewLevel.GetContentAnotherBrush: HBRUSH; begin if (ContentStyle <> nil) and (svAnotherColor in ContentStyle.AssignedValues) then Result := ContentStyle.AnotherBrush else Result := 0; end; function TdxMasterViewLevel.GetContentAnotherColor: TColor; begin if (ContentStyle <> nil) and (svAnotherColor in ContentStyle.AssignedValues) then Result := ContentStyle.AnotherColor else Result := clNone; end; function TdxMasterViewLevel.GetContentBrush: HBRUSH; begin if (ContentStyle <> nil) and (svColor in ContentStyle.AssignedValues) then Result := ContentStyle.Brush else Result := FControl.Brush.Handle; end; function TdxMasterViewLevel.GetContentColor: TColor; begin if (ContentStyle <> nil) and (svColor in ContentStyle.AssignedValues) then Result := ContentStyle.Color else Result := FControl.Color; end; function TdxMasterViewLevel.GetContentFont: TFont; begin if (ContentStyle <> nil) and (svFont in ContentStyle.AssignedValues) then Result := ContentStyle.Font else Result := FControl.Font; end; function TdxMasterViewLevel.GetCount: Integer; begin Result := FItems.Count; end; function TdxMasterViewLevel.GetDataSet: TDataSet; begin Result := FDataLink.DataSet; end; function TdxMasterViewLevel.GetDataSource: TDataSource; begin Result := FDataLink.DataSource; end; function TdxMasterViewLevel.GetDetailKeyField(Index: Integer): TField; begin Result := TField(FDetailKeyFields[Index]); end; function TdxMasterViewLevel.GetDetailKeyFieldCount: Integer; begin Result := FDetailKeyFields.Count; end; function TdxMasterViewLevel.GetDontFilterRecords: Boolean; begin Result := IsTop or (lodDontFilterRecords in FOptionsDB); end; function TdxMasterViewLevel.GetFooterBrush: HBRUSH; begin if (FooterStyle <> nil) and (svColor in FooterStyle.AssignedValues) then Result := FooterStyle.Brush else Result := GetSysColorBrush(COLOR_BTNFACE); end; function TdxMasterViewLevel.GetFooterColor: TColor; begin if (FooterStyle <> nil) and (svColor in FooterStyle.AssignedValues) then Result := FooterStyle.Color else Result := clBtnFace; end; function TdxMasterViewLevel.GetFooterFont: TFont; begin if (FooterStyle <> nil) and (svFont in FooterStyle.AssignedValues) then Result := FooterStyle.Font else Result := FControl.Font; end; function TdxMasterViewLevel.GetGroupBrush: HBRUSH; begin if (GroupStyle <> nil) and (svColor in GroupStyle.AssignedValues) then Result := GroupStyle.Brush else Result := GetSysColorBrush(COLOR_BTNFACE); end; function TdxMasterViewLevel.GetGroupByBoxBrush: HBRUSH; begin if (GroupByBoxStyle <> nil) and (svColor in GroupByBoxStyle.AssignedValues) then Result := GroupByBoxStyle.Brush else Result := GetSysColorBrush(COLOR_BTNSHADOW); end; function TdxMasterViewLevel.GetGroupByBoxColor: TColor; begin if (GroupByBoxStyle <> nil) and (svColor in GroupByBoxStyle.AssignedValues) then Result := GroupByBoxStyle.Color else Result := clBtnShadow; end; function TdxMasterViewLevel.GetGroupByBoxFont: TFont; begin if (GroupByBoxStyle <> nil) and (svFont in GroupByBoxStyle.AssignedValues) then Result := GroupByBoxStyle.Font else Result := FControl.Font; end; function TdxMasterViewLevel.GetGroupByBoxFontColor: TColor; begin if (GroupByBoxStyle <> nil) and (svFont in GroupByBoxStyle.AssignedValues) then Result := GroupByBoxStyle.Font.Color else Result := clBtnFace; end; function TdxMasterViewLevel.GetGroupByBoxHeight: Integer; begin Result := GroupColumnCount; if Result = 0 then Result := 2 * GroupByBoxTopOffset + GroupByBoxTextHeight else Result := 2 * GroupByBoxTopOffset + HeaderRealHeight div 2 * (Result + 1) + Byte(Odd(HeaderRealHeight)) + (Result - 1) * GroupByBoxVerOffset; end; function TdxMasterViewLevel.GetGroupColor: TColor; begin if (GroupStyle <> nil) and (svColor in GroupStyle.AssignedValues) then Result := GroupStyle.Color else Result := clBtnFace; end; function TdxMasterViewLevel.GetGroupColumn(Index: Integer): TdxMasterViewColumn; begin Result := FGroupColumns[Index]; end; function TdxMasterViewLevel.GetGroupColumnCount: Integer; begin Result := FGroupColumns.Count; end; function TdxMasterViewLevel.GetGroupFont: TFont; begin if (GroupStyle <> nil) and (svFont in GroupStyle.AssignedValues) then Result := GroupStyle.Font else Result := FControl.Font; end; function TdxMasterViewLevel.GetHasChildren: Boolean; begin Result := FItems.Count <> 0; end; function TdxMasterViewLevel.GetHeaderBrush: HBRUSH; begin if (HeaderStyle <> nil) and (svColor in HeaderStyle.AssignedValues) then Result := HeaderStyle.Brush else Result := GetSysColorBrush(COLOR_BTNFACE); end; function TdxMasterViewLevel.GetHeaderColor: TColor; begin if (HeaderStyle <> nil) and (svColor in HeaderStyle.AssignedValues) then Result := HeaderStyle.Color else Result := clBtnFace; end; function TdxMasterViewLevel.GetHeaderFont: TFont; begin if (HeaderStyle <> nil) and (svFont in HeaderStyle.AssignedValues) then Result := HeaderStyle.Font else Result := FControl.Font; end; function TdxMasterViewLevel.GetHeaderRestSpaceBrush: HBRUSH; begin if (HeaderStyle <> nil) and (svAnotherColor in HeaderStyle.AssignedValues) then Result := HeaderStyle.AnotherBrush else Result := GetSysColorBrush(COLOR_BTNSHADOW); end; function TdxMasterViewLevel.GetHeaderRestSpaceColor: TColor; begin if (HeaderStyle <> nil) and (svAnotherColor in HeaderStyle.AssignedValues) then Result := HeaderStyle.AnotherColor else Result := clBtnShadow; end; function TdxMasterViewLevel.GetIDField(Index: Integer): TField; begin Result := TField(FIDFields[Index]); end; function TdxMasterViewLevel.GetIDFieldCount: Integer; begin Result := FIDFields.Count; end; function TdxMasterViewLevel.GetIndent: Integer; var I: Integer; begin Result := 0; for I := ParentCount - 2 downto 0 do with Parents[I] do begin if ShowCaption then Inc(Result, LevelIndent + ExtLineWidth); Inc(Result, GroupColumnCount * (LevelIndent + ExtLineWidth)); if I = 0 then Break; Inc(Result, LevelIndent + ExtLineWidth + LevelSeparatorWidth); end; end; function TdxMasterViewLevel.GetIsDestroying: Boolean; var I: Integer; begin Result := csDestroying in ComponentState; if not Result then for I := 1 to ParentCount - 1 do begin Result := csDestroying in Parents[I].ComponentState; if Result then Break; end; end; function TdxMasterViewLevel.GetIsFirst: Boolean; begin Result := Index = 0; end; function TdxMasterViewLevel.GetIsLast: Boolean; begin Result := Index = FParent.Count - 1; end; function TdxMasterViewLevel.GetIsLoading: Boolean; begin Result := (csLoading in ComponentState) or (FLoadingLockCount <> 0); end; function TdxMasterViewLevel.GetIsTop: Boolean; begin Result := FParent.Parent = nil; end; function TdxMasterViewLevel.GetItem(Index: Integer): TdxMasterViewLevel; begin Result := FItems[Index]; end; function TdxMasterViewLevel.GetMasterKeyField(Index: Integer): TField; begin Result := TField(FMasterKeyFields[Index]); end; function TdxMasterViewLevel.GetMasterKeyFieldCount: Integer; begin Result := FMasterKeyFields.Count; end; function TdxMasterViewLevel.GetNonScaledWidth: Integer; begin Result := FLayout.NonScaledWidth; end; function TdxMasterViewLevel.GetOccupyRestSpace: Boolean; begin Result := lovOccupyRestSpace in FOptionsView; end; {$IFDEF CBUILDER3} function TdxMasterViewLevel.GetParent(Index: Integer): TdxMasterViewLevel; begin Result := TdxMasterViewLevel(FParents^[Index]); end; {$ENDIF} function TdxMasterViewLevel.GetPreviewAnotherBrush: HBRUSH; begin if (PreviewStyle <> nil) and (svAnotherColor in PreviewStyle.AssignedValues) then Result := PreviewStyle.AnotherBrush else Result := ContentAnotherBrush; end; function TdxMasterViewLevel.GetPreviewAnotherColor: TColor; begin if (PreviewStyle <> nil) and (svAnotherColor in PreviewStyle.AssignedValues) then Result := PreviewStyle.AnotherColor else Result := ContentAnotherColor; end; function TdxMasterViewLevel.GetPreviewBrush: HBRUSH; begin if (PreviewStyle <> nil) and (svColor in PreviewStyle.AssignedValues) then Result := PreviewStyle.Brush else Result := ContentBrush; end; function TdxMasterViewLevel.GetPreviewColor: TColor; begin if PreviewColorAssigned then Result := PreviewStyle.Color else Result := ContentColor; end; function TdxMasterViewLevel.GetPreviewColorAssigned: Boolean; begin Result := (PreviewStyle <> nil) and (svColor in PreviewStyle.AssignedValues); end; function TdxMasterViewLevel.GetPreviewFont: TFont; begin if PreviewFontAssigned then Result := PreviewStyle.Font else Result := ContentFont; end; function TdxMasterViewLevel.GetPreviewFontAssigned: Boolean; begin Result := (PreviewStyle <> nil) and (svFont in PreviewStyle.AssignedValues); end; function TdxMasterViewLevel.GetPreviewFontColor: TColor; //!!! begin if PreviewFontAssigned then Result := PreviewStyle.Font.Color else Result := clBlue; end; function TdxMasterViewLevel.GetRowCount: Integer; begin Result := FLayout.RowCount; end; function TdxMasterViewLevel.GetShowCaption: Boolean; begin Result := lovCaption in FOptionsView; end; function TdxMasterViewLevel.GetShowFooter: Boolean; begin Result := lovFooter in FOptionsView; end; function TdxMasterViewLevel.GetShowGrid: Boolean; begin Result := lovGrid in FOptionsView; end; function TdxMasterViewLevel.GetShowGridWithPreview: Boolean; begin Result := lovGridWithPreview in FOptionsView; end; function TdxMasterViewLevel.GetShowGroupByBox: Boolean; begin Result := lovGroupByBox in FOptionsView; end; function TdxMasterViewLevel.GetShowHeader: Boolean; begin Result := lovHeader in FOptionsView; end; function TdxMasterViewLevel.GetShowPreview: Boolean; begin Result := lovPreview in FOptionsView; end; function TdxMasterViewLevel.GetSmartLoad: Boolean; begin Result := lodSmartLoad in FOptionsDB; end; function TdxMasterViewLevel.GetSmartReload: Boolean; begin Result := (lodSmartReload in FOptionsDB) and FCanUseSmartReload; end; function TdxMasterViewLevel.GetSortedColumn(Index: Integer): TdxMasterViewColumn; begin Result := TdxMasterViewColumn(FSortedColumns[Index]); end; function TdxMasterViewLevel.GetSortedColumnCount: Integer; begin Result := FSortedColumns.Count; end; function TdxMasterViewLevel.GetVisibleColumn(Index: Integer): TdxMasterViewColumn; begin Result := TdxMasterViewColumn(FVisibleColumns[Index]); end; function TdxMasterViewLevel.GetVisibleColumnCount: Integer; begin Result := FVisibleColumns.Count; end; function TdxMasterViewLevel.GetVisibleRowCount: Integer; begin Result := FLayout.RowCount; if Result = 0 then Result := 1; end; function TdxMasterViewLevel.GetVisibleWidth: Integer; begin Result := FLayout.VisibleWidth; end; procedure TdxMasterViewLevel.SetCaptionStyle(Value: TdxMasterViewStyle); begin if FCaptionStyle <> Value then begin if FCaptionStyle <> nil then FCaptionStyle.RemoveConsumeType(Self, sctCaption); FCaptionStyle := Value; if FCaptionStyle <> nil then FCaptionStyle.AddConsumeType(Self, sctCaption); CaptionStyleChanged([]); end; end; procedure TdxMasterViewLevel.SetColumn(Index: Integer; Value: TdxMasterViewColumn); begin Columns[Index].Assign(Value); end; procedure TdxMasterViewLevel.SetCaption(const Value: string); begin if FCaption <> Value then begin FCaption := Value; if ShowCaption then LevelChanged(False); if dxMVDesigner <> nil then dxMVDesigner.Changed(FControl, [rcLevels]); end; end; procedure TdxMasterViewLevel.SetContentStyle(Value: TdxMasterViewStyle); begin if FContentStyle <> Value then begin if FContentStyle <> nil then FContentStyle.RemoveConsumeType(Self, sctContent); FContentStyle := Value; if FContentStyle <> nil then FContentStyle.AddConsumeType(Self, sctContent); ContentStyleChanged([]); end; end; procedure TdxMasterViewLevel.SetDataSource(Value: TDataSource); var PrevActive: Boolean; begin if DataSource <> Value then begin PrevActive := Active; FDataLink.DataSource := Value; if Value <> nil then Value.FreeNotification(Self); if PrevActive or Active then begin FCanUseSmartReload := False; LevelChanged(True); end; end; end; procedure TdxMasterViewLevel.SetDetailKey(const Value: string); var PrevActive: Boolean; begin if FDetailKey <> Value then begin PrevActive := Active; FDetailKey := Value; RefreshDetailKeyFieldsList; if PrevActive or Active then begin FCanUseSmartReload := False; LevelChanged(True); end; end; end; procedure TdxMasterViewLevel.SetFooterStyle(Value: TdxMasterViewStyle); begin if FFooterStyle <> Value then begin if FFooterStyle <> nil then FFooterStyle.RemoveConsumeType(Self, sctFooter); FFooterStyle := Value; if FFooterStyle <> nil then FFooterStyle.AddConsumeType(Self, sctFooter); FooterStyleChanged([]); end; end; procedure TdxMasterViewLevel.SetGridLinesColor(Value: TColor); begin if FGridLinesColor <> Value then begin FGridLinesColor := Value; LevelChanged(False); end; end; procedure TdxMasterViewLevel.SetGroupByBoxStyle(Value: TdxMasterViewStyle); begin if FGroupByBoxStyle <> Value then begin if FGroupByBoxStyle <> nil then FGroupByBoxStyle.RemoveConsumeType(Self, sctGroupByBox); FGroupByBoxStyle := Value; if FGroupByBoxStyle <> nil then FGroupByBoxStyle.AddConsumeType(Self, sctGroupByBox); GroupByBoxStyleChanged([]); end; end; procedure TdxMasterViewLevel.SetGroupStyle(Value: TdxMasterViewStyle); begin if FGroupStyle <> Value then begin if FGroupStyle <> nil then FGroupStyle.RemoveConsumeType(Self, sctGroup); FGroupStyle := Value; if FGroupStyle <> nil then FGroupStyle.AddConsumeType(Self, sctGroup); GroupStyleChanged([]); end; end; procedure TdxMasterViewLevel.SetHeaderStyle(Value: TdxMasterViewStyle); begin if FHeaderStyle <> Value then begin if FHeaderStyle <> nil then FHeaderStyle.RemoveConsumeType(Self, sctHeader); FHeaderStyle := Value; if FHeaderStyle <> nil then FHeaderStyle.AddConsumeType(Self, sctHeader); HeaderStyleChanged([]); end; end; procedure TdxMasterViewLevel.SetID(Value: string); var PrevActive: Boolean; begin if FID <> Value then begin PrevActive := Active; FID := Value; RefreshIDFieldsList; if PrevActive or Active then begin FCanUseSmartReload := False; LevelChanged(True); end; end; end; procedure TdxMasterViewLevel.SetLevelSeparatorColor(Value: TColor); begin if FLevelSeparatorColor <> Value then begin FLevelSeparatorColor := Value; if FLevelSeparatorWidth <> 0 then LevelChanged(False); end; end; procedure TdxMasterViewLevel.SetLevelSeparatorWidth(Value: Integer); begin if Value < 0 then Value := 0; if FLevelSeparatorWidth <> Value then begin FLevelSeparatorWidth := Value; LevelChanged(False); end; end; procedure TdxMasterViewLevel.SetMasterKey(const Value: string); var PrevActive: Boolean; begin if FMasterKey <> Value then begin PrevActive := Active; FMasterKey := Value; RefreshMasterKeyFieldsList; if not IsTop then with FParent do begin FCanUseSmartReload := False; LevelChanged(True); end; if PrevActive or Active then begin FCanUseSmartReload := False; LevelChanged(True); end; end; end; procedure TdxMasterViewLevel.SetOptionsDB(Value: TdxMasterViewLevelOptionsDB); var ChangedValues: TdxMasterViewLevelOptionsDB; begin if FOptionsDB <> Value then begin Byte(ChangedValues) := Byte(Value) xor Byte(FOptionsDB); FOptionsDB := Value; if lodDontFilterRecords in ChangedValues then begin FCanUseSmartReload := False; LevelChanged(True); end; if lodSmartLoad in ChangedValues then if FControl <> nil then with FControl do begin BeginUpdate; try Items.DoSmartLoadChanged(Self); finally EndUpdate; end; end; end; end; procedure TdxMasterViewLevel.SetOptionsHeader(Value: TdxMasterViewLevelOptionsHeader); begin if FOptionsHeader <> Value then begin FOptionsHeader := Value; LevelChanged(False); end; end; procedure TdxMasterViewLevel.SetOptionsView(Value: TdxMasterViewLevelOptionsView); var ChangedValues: TdxMasterViewLevelOptionsView; begin if FOptionsView <> Value then begin Word(ChangedValues) := Word(Value) xor Word(FOptionsView); if lovPreview in ChangedValues then if not (lovPreview in Value) then PreviewChanged; FOptionsView := Value; if lovCaption in ChangedValues then begin WidthChanged; LevelChanged(True); end; if [lovFooter, lovGroupByBox, lovNoButtonsWhenNoChildren] * ChangedValues <> [] then LevelChanged(False); if lovGrid in ChangedValues then begin FExtLineWidth := DefaultLineWidth * Byte(ShowGrid); CalcLineWidth; if ShowPreview then PreviewChanged else LevelChanged(False); end; if lovGridWithPreview in ChangedValues then if ShowPreview then begin CalcLineWidth; PreviewChanged; end; if lovHeader in ChangedValues then begin CalcDefaultWidths(nil); if not FHorizontal then WidthChanged; LevelChanged(False); end; if lovPreview in ChangedValues then begin CalcLineWidth; if lovPreview in FOptionsView then LevelChanged(False); end; if lovOccupyRestSpace in ChangedValues then if not FControl.AutoColumnWidth then begin FLayout.CheckColumnsWidths(nil); LevelChanged(False); end; end; end; procedure TdxMasterViewLevel.SetPreviewField(Value: TField); begin if FPreviewField <> Value then begin SetPreviewFieldInternally(Value); PreviewChanged; if Active then begin FCanUseSmartReload := False; LevelChanged(True); end; end; end; procedure TdxMasterViewLevel.SetPreviewFieldInternally(Value: TField); begin if FPreviewField <> Value then begin FPreviewField := Value; if FPreviewField <> nil then begin FPreviewField.FreeNotification(Self); FPreviewFieldName := FPreviewField.FieldName; end; end; end; procedure TdxMasterViewLevel.SetPreviewFieldName(Value: string); begin if FPreviewFieldName <> Value then begin FPreviewFieldName := Value; SetPreviewField(FindPreviewField); end; end; procedure TdxMasterViewLevel.SetPreviewLeftIndent(Value: Integer); begin if Value < 0 then Value := 0; if FPreviewLeftIndent <> Value then begin FPreviewLeftIndent := Value; if ShowPreview then PreviewChanged; end; end; procedure TdxMasterViewLevel.SetPreviewLineCount(Value: Integer); begin if Value < 0 then Value := 0; if FPreviewLineCount <> Value then begin FPreviewLineCount := Value; if ShowPreview then PreviewChanged; end; end; procedure TdxMasterViewLevel.SetPreviewMaxLength(Value: Integer); begin if Value < 0 then Value := 0; if FPreviewMaxLength <> Value then begin FPreviewMaxLength := Value; PreviewChanged; if Active then begin FCanUseSmartReload := False; LevelChanged(True); end; end; end; procedure TdxMasterViewLevel.SetPreviewMaxLineCount(Value: Integer); begin if Value < 0 then Value := 0; if FPreviewMaxLineCount <> Value then begin FPreviewMaxLineCount := Value; if ShowPreview and (FPreviewLineCount = 0) then PreviewChanged; end; end; procedure TdxMasterViewLevel.SetPreviewRightIndent(Value: Integer); begin if Value < 0 then Value := 0; if FPreviewRightIndent <> Value then begin FPreviewRightIndent := Value; if ShowPreview then PreviewChanged; end; end; procedure TdxMasterViewLevel.SetPreviewStyle(Value: TdxMasterViewStyle); begin if FPreviewStyle <> Value then begin if FPreviewStyle <> nil then FPreviewStyle.RemoveConsumeType(Self, sctPreview); FPreviewStyle := Value; if FPreviewStyle <> nil then FPreviewStyle.AddConsumeType(Self, sctPreview); PreviewStyleChanged([]); end; end; procedure TdxMasterViewLevel.SetRowSeparatorColor(Value: TColor); begin if FRowSeparatorColor <> Value then begin FRowSeparatorColor := Value; if FRowSeparatorWidth <> 0 then LevelChanged(False); end; end; procedure TdxMasterViewLevel.SetRowSeparatorWidth(Value: Integer); begin if Value < 0 then Value := 0; if FRowSeparatorWidth <> Value then begin FRowSeparatorWidth := Value; LevelChanged(False); end; end; procedure TdxMasterViewLevel.SetViewMode(Value: TdxMasterViewViewMode); begin if FViewMode <> Value then begin FViewMode := Value; FHorizontal := Value = vmHorizontal; CalcRealHeights; FLayout.ViewModeChanged; end; end; procedure TdxMasterViewLevel.SetVisibleColumn(Index: Integer; Value: TdxMasterViewColumn); begin VisibleColumns[Index].Assign(Value); end; function TdxMasterViewLevel.IsDeleteConfirmCaptionTextStored: Boolean; begin Result := FDeleteConfirmCaptionText <> LoadStr(dxsMVDeleteConfirmCaptionText); end; function TdxMasterViewLevel.IsDeleteConfirmTextStored: Boolean; begin Result := FDeleteConfirmText <> LoadStr(dxsMVDeleteConfirmText); end; function TdxMasterViewLevel.IsMultipleDeleteConfirmTextStored: Boolean; begin Result := FMultipleDeleteConfirmText <> LoadStr(dxsMVMultipleDeleteConfirmText); end; procedure TdxMasterViewLevel.ReadHeaderWidths(Reader: TReader); var I, Value: Integer; begin I := 0; Reader.ReadListBegin; try while not Reader.EndOfList do begin Value := Reader.ReadInteger; FLayout.Count := I + 1; if Value <> -1 then FLayout.HeaderWidths[I] := Value; Inc(I); end; finally Reader.ReadListEnd; end; end; procedure TdxMasterViewLevel.WriteHeaderWidths(Writer: TWriter); var I: Integer; begin Writer.WriteListBegin; try for I := 0 to FLayout.Count - 1 do if FLayout.HeaderWidthAssigned[I] then Writer.WriteInteger(FLayout.HeaderWidths[I]) else Writer.WriteInteger(-1); finally Writer.WriteListEnd; end; end; procedure TdxMasterViewLevel.AddColumn(AColumn: TdxMasterViewColumn); begin AColumn.FLevel := Self; FColumns.Add(AColumn); if AColumn.Visible then begin FVisibleColumns.Add(AColumn); if not IsLoading then if FViewMode = vmHorizontal then begin AColumn.RowIndex := 0; AColumn.ColIndex := FLayout[0].Count - 1; end else begin AColumn.ColIndex := 0; AColumn.RowIndex := FLayout[0].Count - 1; end; end; if FControl <> nil then FControl.Items.MoveData(Self, -1, AColumn.Index); FontsChanged; LevelChanged(False); end; procedure TdxMasterViewLevel.RemoveColumn(AColumn: TdxMasterViewColumn); begin AColumn.GroupIndex := -1; if (FControl <> nil) and (FControl.Items <> nil) then FControl.Items.MoveData(Self, AColumn.Index, -1); if AColumn.Visible then begin FVisibleColumns.Remove(AColumn); FLayout.RemoveColumn(AColumn); end; SummaryColumnsChanged(AColumn, opRemove); FSortedColumns.Remove(AColumn); FColumns.Remove(AColumn); FontsChanged; LevelChanged(False); AColumn.FLevel := nil; // if dxMVDesigner <> nil then dxMVDesigner.Changed(FControl, [rcColumns]); end; procedure TdxMasterViewLevel.AddItem(Value: TdxMasterViewLevel); begin DestroySummaries([ntData]); Value.FIndex := FItems.Add(Value); Value.FParent := Self; Value.FControl := FControl; Value.CalcParents; FControl.RefreshAbsoluteLevels; Value.FontsChanged; if Count = 1 then WidthChanged; //FControl.FItems.ChangeExpanded(True); if Active then begin FCanUseSmartReload := False; LevelChanged(True); end; end; procedure TdxMasterViewLevel.RemoveItem(Value: TdxMasterViewLevel); var I: Integer; begin //if (LevelCount = 1) and (FItems <> nil) then FItems.ChangeExpanded(False); Value.DeleteNodes; DestroySummaries([ntData]); FItems.Remove(Value); with Value do begin I := Index; FAbsoluteIndex := -1; FIndex := -1; FParent := nil; end; for I := I to Count - 1 do Dec(Items[I].FIndex); if (FParent <> nil) or (FControl <> nil) and (FControl.Levels = Self) then begin FControl.RefreshAbsoluteLevels; if Count = 0 then WidthChanged; if Active then begin FCanUseSmartReload := False; LevelChanged(True); end; end; // if dxMVDesigner <> nil then dxMVDesigner.Changed(FControl, [rcLevels]); end; function TdxMasterViewLevel.FindPreviewField: TField; begin if (FPreviewFieldName <> '') and (DataSet <> nil) then Result := DataSet.FindField(FPreviewFieldName) else Result := nil; end; procedure TdxMasterViewLevel.FontsChanged; begin if (FControl <> nil) and not IsDestroying then begin ContentStyleChanged([svFont]); FooterStyleChanged([svFont]); HeaderStyleChanged([svFont]); PreviewStyleChanged([svFont]); CaptionStyleChanged([svFont]); GroupStyleChanged([svFont]); GroupByBoxStyleChanged([svFont]); end; end; procedure TdxMasterViewLevel.DeleteNodes; procedure DeleteOne(Root: TdxMasterViewNode); var I: Integer; begin with Root do begin if Level = FParent then begin FItems.BeginDeletion; try for I := Count - 1 downto 0 do if Items[I].Level = Self then Items[I].Free; finally FItems.EndDeletion; end; end; if Self.HasAsParent(Level) then for I := 0 to Count - 1 do DeleteOne(Items[I]); end; end; begin with FControl do if FItems <> nil then begin FAbsoluteItems.BeginDeletion; FSelectedItems.BeginDeletion; try DeleteOne(FItems); finally FSelectedItems.EndDeletion; FAbsoluteItems.EndDeletion; end; end; end; procedure TdxMasterViewLevel.RefreshDetailKeyFieldsList; var I: Integer; begin FDetailKeyFields.Clear; if DataSet <> nil then begin try DataSet.GetFieldList(FDetailKeyFields, FDetailKey); except FDetailKeyFields.Clear; end; for I := 0 to DetailKeyFieldCount - 1 do DetailKeyFields[I].FreeNotification(Self); end; end; procedure TdxMasterViewLevel.RefreshIDFieldsList; var I: Integer; begin FIDFields.Clear; if DataSet <> nil then begin try DataSet.GetFieldList(FIDFields, FID); except FIDFields.Clear; end; for I := 0 to IDFieldCount - 1 do IDFields[I].FreeNotification(Self); end; end; procedure TdxMasterViewLevel.RefreshMasterKeyFieldsList; var I: Integer; begin FMasterKeyFields.Clear; if FParent.DataSet <> nil then begin try FParent.DataSet.GetFieldList(FMasterKeyFields, FMasterKey); except FMasterKeyFields.Clear; end; for I := 0 to MasterKeyFieldCount - 1 do MasterKeyFields[I].FreeNotification(FParent); end; end; procedure TdxMasterViewLevel.RefreshVisibleColumnsList; var I: Integer; begin FVisibleColumns.Clear; for I := 0 to ColumnCount - 1 do if Columns[I].Visible then FVisibleColumns.Add(Columns[I]); end; procedure TdxMasterViewLevel.SummaryColumnsChanged(AColumn: TdxMasterViewColumn; Operation: TOperation); begin if Operation = opInsert then if AColumn.SummaryIndex = -1 then FSummaryColumns.Add(AColumn) else Exit else if AColumn.SummaryIndex = -1 then Exit else FSummaryColumns.Remove(AColumn); if ShowCaption or (GroupColumnCount <> 0) then DestroySummaries([ntCaption, ntGroup]); if FParent <> nil then FParent.DestroySummaries([ntData]); end; procedure TdxMasterViewLevel.DefineProperties(Filer: TFiler); begin inherited; Filer.DefineProperty('HeaderWidths', ReadHeaderWidths, WriteHeaderWidths, not FHorizontal); end; procedure TdxMasterViewLevel.GetChildren(Proc: TGetChildProc; Root: TComponent); var I: Integer; begin for I := 0 to ColumnCount - 1 do if Columns[I].Owner = Root then Proc(Columns[I]); for I := 0 to Count - 1 do if Items[I].Owner = Root then Proc(Items[I]); end; procedure TdxMasterViewLevel.Loaded; begin inherited; LoadingComplete; end; procedure TdxMasterViewLevel.Notification(AComponent: TComponent; Operation: TOperation); var NeedLayout: Boolean; I: Integer; begin inherited; if (Operation = opRemove) and (AComponent is TdxMasterViewStyle) then begin if HeaderStyle = AComponent then HeaderStyle := nil; if ContentStyle = AComponent then ContentStyle := nil; if FooterStyle = AComponent then FooterStyle := nil; if PreviewStyle = AComponent then PreviewStyle := nil; if CaptionStyle = AComponent then CaptionStyle := nil; if GroupStyle = AComponent then GroupStyle := nil; if GroupByBoxStyle = AComponent then GroupByBoxStyle := nil; end; if not IsDestroying and (Operation = opRemove) then if AComponent = DataSource then DataSource := nil else if AComponent is TField then begin NeedLayout := False; FControl.BeginLayout; try if FDetailKeyFields.Remove(AComponent) <> -1 then NeedLayout := True; if FIDFields.Remove(AComponent) <> -1 then NeedLayout := True; for I := 0 to Count - 1 do if Items[I].FMasterKeyFields.Remove(AComponent) <> -1 then NeedLayout := True; if FPreviewField = AComponent then begin PreviewField := nil; NeedLayout := True; end; for I := 0 to ColumnCount - 1 do with Columns[I] do begin if Field = AComponent then begin Field := nil; NeedLayout := True; end; if SummaryField = AComponent then begin SummaryField := nil; NeedLayout := True; end; end; finally if NeedLayout and Active then FControl.EndLayout else FControl.CancelLayout; end; end; end; procedure TdxMasterViewLevel.SetName(const NewName: TComponentName); var OldName: TComponentName; I: Integer; Column: TdxMasterViewColumn; ColumnName, NamePrefix: TComponentName; begin OldName := Name; inherited; { In design mode the name of the columns should track the level name } if FControl.IsDesigning and (Name <> OldName) then for I := 0 to ColumnCount - 1 do begin Column := Columns[I]; if Column.Owner = Owner then begin ColumnName := Column.Name; if Length(ColumnName) > Length(OldName) then begin NamePrefix := Copy(ColumnName, 1, Length(OldName)); if CompareText(OldName, NamePrefix) = 0 then begin System.Delete(ColumnName, 1, Length(OldName)); System.Insert(NewName, ColumnName, 1); try Column.Name := ColumnName; except on EComponentError do {Ignore rename errors }; end; end; end; end; end; if dxMVDesigner <> nil then dxMVDesigner.Changed(FControl, [rcLevels]); end; procedure TdxMasterViewLevel.SetParentComponent(AParent: TComponent); begin if AParent is TdxMasterView then TdxMasterView(AParent).Levels.AddItem(Self) else if AParent is TdxMasterViewLevel then TdxMasterViewLevel(AParent).AddItem(Self); end; procedure TdxMasterViewLevel.ActiveChanged; var I: Integer; begin FControl.BeginLayout; try LayoutChanged; finally FControl.EndLayout; end; if FControl.FocusedNode = nil then begin if FControl.SyncMove then if Active then SyncPos else for I := ParentCount - 2 downto 1 do with Parents[I] do if Active then SyncPos else Break; with FControl do if (FocusedNode = nil) and (AbsoluteItemCount <> 0) then FocusNode(AbsoluteItems[0], True); end; end; procedure TdxMasterViewLevel.AfterPaint; begin DeleteObject(FRowSeparatorBrush); DeleteObject(FLevelSeparatorBrush); DeleteObject(FGridLinesPen); DeleteObject(FGridLinesBrush); end; procedure TdxMasterViewLevel.AssignColumnWidths; var I: Integer; begin BeginAssignWidths; try for I := 0 to VisibleColumnCount - 1 do with VisibleColumns[I] do Width := FVisibleWidth; finally EndAssignWidths; end; end; procedure TdxMasterViewLevel.BeforePaint; begin FGridLinesBrush := CreateSolidBrush(ColorToRGB(FGridLinesColor)); FGridLinesPen := CreatePen(PS_SOLID, DefaultLineWidth, ColorToRGB(FGridLinesColor)); FLevelSeparatorBrush := CreateSolidBrush(ColorToRGB(FLevelSeparatorColor)); FRowSeparatorBrush := CreateSolidBrush(ColorToRGB(FRowSeparatorColor)); end; procedure TdxMasterViewLevel.BeginLoad; var NeedUpdateData: Boolean; begin NeedUpdateData := False; if (FLoadLockCount = 0) and (DataSet <> nil) then begin DataSet.DisableControls; if DataSet.State in dsEditModes then begin DataSet.Cancel; NeedUpdateData := True; end; FBeforeLoadBookmark := DataSet.GetBookmark; FBeforeLoadActiveRecord := TDummyDataSet(DataSet).ActiveRecord; FBeforeLoadBOF := DataSet.BOF; FBeforeLoadEOF := DataSet.EOF; end; Inc(FLoadLockCount); if NeedUpdateData then DataChanged; end; procedure TdxMasterViewLevel.BeginLoading; begin if FLoadingLockCount = 0 then begin FCursorBeforeLoading := Screen.Cursor; Screen.Cursor := crHourglass; end; Inc(FLoadingLockCount); end; procedure TdxMasterViewLevel.CalcDefaultWidths(Column: TdxMasterViewColumn); var AChanged: Boolean; I, PrevWidth: Integer; procedure ProcessColumn(Column: TdxMasterViewColumn); begin with Column do if not (cvWidth in AssignedValues) then begin PrevWidth := FWidth; FWidth := DefaultWidth; if FWidth <> PrevWidth then FVisibleWidth := FWidth; AChanged := AChanged or Visible and (FWidth <> PrevWidth); end; end; begin if IsLoading or (Column <> nil) and Column.IsLoading or Control.IsDestroying then Exit; AChanged := False; BeginAssignWidths; try if Column <> nil then ProcessColumn(Column) else for I := 0 to ColumnCount - 1 do ProcessColumn(Columns[I]); finally if AChanged then EndAssignWidths else CancelAssignWidths; end; end; procedure TdxMasterViewLevel.CalcLineWidth; begin FLineWidth := DefaultLineWidth * Byte(HasGrid); CalcRealHeights; end; procedure TdxMasterViewLevel.CalcParents; var ALevel: TdxMasterViewLevel; begin if FParents <> nil then FreeMem(FParents); FParentCount := 0; ALevel := Self; while ALevel <> nil do begin Inc(FParentCount); ALevel := ALevel.Parent; end; GetMem(FParents, FParentCount * SizeOf(TdxMasterViewLevel)); FParentCount := 0; ALevel := Self; while ALevel <> nil do begin FParents[FParentCount] := ALevel; Inc(FParentCount); ALevel := ALevel.Parent; end; end; procedure TdxMasterViewLevel.CalcRealHeights; begin FHeaderRealHeight := FHeaderHeight; FContentRealHeight := FContentHeight + FLineWidth; if not FHorizontal then if FHeaderRealHeight > FContentRealHeight then FContentRealHeight := FHeaderRealHeight else FHeaderRealHeight := FContentRealHeight; end; procedure TdxMasterViewLevel.DestroySummaries(NodeTypes: TdxMasterViewNodeTypes); procedure CheckNode(Node: TdxMasterViewNode); var I: Integer; begin if (Node.Level = Self) and (Node.NodeType in NodeTypes) then Node.DestroySummaries; for I := 0 to Node.Count - 1 do CheckNode(Node[I]); end; begin with FControl do if FItems <> nil then CheckNode(FItems); end; procedure TdxMasterViewLevel.EndLoad(RecalcInfo: Boolean); var AFreezeCount: Integer; begin if FLoadLockCount > 0 then begin Dec(FLoadLockCount); if FLoadLockCount = 0 then begin if DataSet <> nil then with TDummyDataSet(DataSet) do begin if FBeforeLoadBookmark <> nil then begin if BookmarkValid(FBeforeLoadBookmark) then GotoBookmark(FBeforeLoadBookmark); FreeBookmark(FBeforeLoadBookmark); end; if ActiveRecord > FBeforeLoadActiveRecord then begin MoveBy(BufferCount - ActiveRecord - 1 + ActiveRecord - FBeforeLoadActiveRecord); MoveBy(FBeforeLoadActiveRecord - BufferCount + 1); end else if ActiveRecord < FBeforeLoadActiveRecord then begin MoveBy(-ActiveRecord + ActiveRecord - FBeforeLoadActiveRecord); MoveBy(FBeforeLoadActiveRecord); end; if FBeforeLoadBOF and not BOF then Prior; if FBeforeLoadEOF and not EOF then Next; AFreezeCount := FControl.FreezeDataSet(Self); try EnableControls; finally if FDataLink.FreezeCount = AFreezeCount then FControl.UnfreezeDataSet(Self); end; end; if RecalcInfo then begin DoSorting; end; end; end; end; procedure TdxMasterViewLevel.EndLoading; begin if FLoadingLockCount > 0 then begin Dec(FLoadingLockCount); if FLoadingLockCount = 0 then begin LoadingComplete; LevelChanged(True); Screen.Cursor := FCursorBeforeLoading; end; end; end; procedure TdxMasterViewLevel.LoadingComplete; var List: TList; PrevProcessedCount, ProcessedCount, CurIndex, I: Integer; PrevLeaveSortOrder: Boolean; Column: TdxMasterViewColumn; begin BeginLayout; List := TList.Create; try // grouping CurIndex := -1; repeat ProcessedCount := 0; for I := 0 to ColumnCount - 1 do begin Column := Columns[I]; with Column do if FLoadedGroupIndex = CurIndex then begin PrevLeaveSortOrder := FLeaveSortOrder; if GroupIndex <> FLoadedGroupIndex then ChangeGrouping(Column, FLoadedGroupIndex); FLeaveSortOrder := PrevLeaveSortOrder; ProcessedCount := 1; if CurIndex <> -1 then Break; end; end; Inc(CurIndex); until (ProcessedCount = 0) and (CurIndex <> -1); // sorting CurIndex := -1; repeat ProcessedCount := 0; for I := 0 to ColumnCount - 1 do begin Column := Columns[I]; if Column.FLoadedSortIndex = CurIndex then begin with Column do if SortIndex <> FLoadedSortIndex then ChangeSorting(Column, SortOrder, FLoadedSortIndex); ProcessedCount := 1; if CurIndex <> -1 then Break; end; end; Inc(CurIndex); until (ProcessedCount = 0) and (CurIndex <> -1); // layout CalcDefaultWidths(nil); MakeVisibleColumnsSortedList(List); FLayout.Clear; PrevProcessedCount := 0; ProcessedCount := 0; CurIndex := 0; while ProcessedCount < List.Count do begin while ProcessedCount < List.Count do begin Column := TdxMasterViewColumn(List[ProcessedCount]); if FHorizontal and (Column.RowIndex <> CurIndex) or not FHorizontal and (Column.ColIndex <> CurIndex) then Break; Inc(ProcessedCount); end; for I := PrevProcessedCount to ProcessedCount - 1 do with TdxMasterViewColumn(List[I]) do FLayout.InsertColumn(FRowIndex, FColIndex, List[I]); PrevProcessedCount := ProcessedCount; Inc(CurIndex); end; finally List.Free; EndLayout; end; end; procedure TdxMasterViewLevel.CalcPreviewLineCount(DC: HDC; const S: string; Width: Integer; Node: TdxMasterViewNode; var Height: Integer); var R: TRect; Size: TSize; LineCount: Integer; begin GetTextExtentPoint{32}(DC, 'Qq', 2, Size); if FPreviewLineCount = 0 then begin R := Rect(0, 0, Width - (FPreviewLeftIndent + FPreviewRightIndent + FExtLineWidth), 0); Windows.DrawText(DC, PChar(S), Length(S), R, DT_CALCRECT or DT_EXPANDTABS or DT_NOCLIP or DT_NOPREFIX or DT_WORDBREAK); LineCount := R.Bottom div Size.cy; if (FPreviewMaxLineCount <> 0) and (LineCount > FPreviewMaxLineCount) then LineCount := FPreviewMaxLineCount; end else LineCount := FPreviewLineCount; if Assigned(FOnGetPreviewLineCount) then FOnGetPreviewLineCount(Self, Node, LineCount); Height := 1 + Size.cy * LineCount + 1 + FExtLineWidth; end; function TdxMasterViewLevel.CanDelete: Boolean; begin Result := (lodAllowDelete in FOptionsDB) and Active and DataSet.CanModify; end; function TdxMasterViewLevel.CanGrouping(Column: TdxMasterViewColumn; Index: Integer): Boolean; begin Result := locColumnGrouping in FOptionsCustomize; if Assigned(FOnChangeGrouping) then FOnChangeGrouping(Self, Column, Index, Result); end; function TdxMasterViewLevel.CanHorSizing: Boolean; begin Result := locColumnHorSizing in FOptionsCustomize; end; function TdxMasterViewLevel.CanInsert: Boolean; begin Result := (lodAllowInsert in FOptionsDB) and Active and DataSet.CanModify; end; function TdxMasterViewLevel.CanSmartRefresh: Boolean; begin Result := FControl.SyncMove and (lodSmartRefresh in FOptionsDB) and not (FDataLink.DataSet.BOF or FDataLink.DataSet.EOF); end; function TdxMasterViewLevel.CanSorting(Column: TdxMasterViewColumn): Boolean; begin Result := locColumnSorting in FOptionsCustomize; if Assigned(FOnColumnSorting) then FOnColumnSorting(Self, Column, Result); end; procedure TdxMasterViewLevel.CaptionStyleChanged(Values: TdxMasterViewStyleValues); var Size: TSize; begin if (Values = []) or (svFont in Values) then begin CalcFontSize(CaptionFont, Size); FCaptionHeight := Size.cy + 2 + 2; // 17 - without line end; if ShowCaption then LevelChanged(False); end; procedure TdxMasterViewLevel.ChangeGrouping(Column: TdxMasterViewColumn; AIndex: Integer); begin with FGroupColumns, Column do if GroupIndex = -1 then begin FLeaveSortOrder := FSortOrder <> soNone; if (locHideColumnOnGrouping in OptionsCustomize) and (VisibleColumnCount <> 1) then Visible := False; if SortOrder = soNone then SortOrder := soAscending; Insert(AIndex, Column); end else if AIndex = -1 then begin Remove(Column); if locShowColumnOnUngrouping in OptionsCustomize then Visible := True; end else if AIndex = Count then Exit else Move(GroupIndex, AIndex); WidthChanged; if Active then LevelChanged(True); with Column do if (AIndex = -1) and not FLeaveSortOrder then SortOrder := soNone; end; procedure TdxMasterViewLevel.ChangeSorting(Column: TdxMasterViewColumn; ASortOrder: TdxMasterViewSortOrder; ASortIndex: Integer); var Resort: Boolean; I: Integer; begin if ASortIndex = -2 then if SortedColumnCount <> 0 then begin BeginSorting; try for I := SortedColumnCount - 1 downto 0 do if SortedColumns[I] <> Column then // exceptional column SortedColumns[I].SortOrder := soNone; finally EndSorting; end; end else else if Column <> nil then begin if (ASortOrder = soNone) and (Column.GroupIndex <> -1) then Exit; Column.FSortOrder := ASortOrder; Column.ColumnChanged(False, hpOne, vpHeader); Resort := True; if ASortOrder = soNone then begin FSortedColumns.Remove(Column); if FSortedColumns.Count = 0 then Resort := False; end else if Column.SortIndex = -1 then if ASortIndex = -1 then FSortedColumns.Add(Column) else FSortedColumns.Insert(ASortIndex, Column) else FSortedColumns.Move(Column.SortIndex, ASortIndex); if Column.Visible and not FHorizontal and ShowHeader and not FLayout.HeaderWidthAssigned[Column.ColIndex] then WidthChanged; if Resort then DoSorting else if Column.Visible and ShowHeader then if FHorizontal or FLayout.HeaderWidthAssigned[Column.ColIndex] then Column.ColumnChanged(False, hpOne, vpHeader) else LevelChanged(False); if not IsDestroying and Assigned(FOnColumnSorted) then FOnColumnSorted(Self, Column); end; end; procedure TdxMasterViewLevel.ContentStyleChanged(Values: TdxMasterViewStyleValues); var Size: TSize; H, I: Integer; begin if (Values = []) or (svFont in Values) then begin if VisibleColumnCount = 0 then begin CalcFontSize(ContentFont, Size); H := Size.cy; end else begin H := 0; for I := 0 to VisibleColumnCount - 1 do begin CalcFontSize(VisibleColumns[I].ContentFont, Size); if Size.cy > H then H := Size.cy; end; end; FContentHeight := H + 2 + 2; // 17 - without line CalcRealHeights; CalcDefaultWidths(nil); end; LevelChanged(False); if (svColor in Values) and not PreviewColorAssigned or (svFont in Values) and not PreviewFontAssigned then PreviewStyleChanged(Values); end; procedure TdxMasterViewLevel.DataChanged; var AKeyValue: Variant; ParentNode, Node: TdxMasterViewNode; begin if FDataChanging then Exit; FDataChanging := True; try if FDataLink.DataSet.State = dsSetKey then Exit; if FDataLink.DataSet.State = dsBrowse then if CanSmartRefresh then begin if FIsInsertMode or (FEditingNode <> nil) then begin UncheckTriedToExpand; if FIsInsertMode then begin FIsInsertMode := False; AKeyValue := GetCurDetailKeyValue; if not VarIsEmpty(AKeyValue) or IsTop then begin if IsTop then ParentNode := FControl.FItems else ParentNode := FControl.NodeFromKeyValue(FParent, Index, AKeyValue); if ParentNode <> nil then begin ParentNode := GetParentNode(ParentNode, nil, nil, nil, nil, True); if ParentNode <> nil then begin Node := ParentNode.Add(Self, -1, ntData, GetCurIDValue, GetCurKeyValue); Node.UpdateData(True, False, False); ParentNode.CheckChildNodes(Self, True); Node.MakeVisible; end; end; end; end; if FEditingNode <> nil then with FEditingNode do begin UpdateData(True, False, True); CheckParentNodes; MakeVisible; FEditingNode := nil; end; end; SyncPos; end else begin FIsInsertMode := False; FEditingNode := nil; UncheckTriedToExpand; //FCanUseSmartReload := False; LevelChanged(True); end; if FDataLink.DataSet.State = dsInsert then FIsInsertMode := True; finally FDataChanging := False; end; end; procedure TdxMasterViewLevel.DoAfterCollapse(Node: TdxMasterViewNode); begin if Assigned(FOnCollapsed) then FOnCollapsed(Self, Node); end; procedure TdxMasterViewLevel.DoAfterExpand(Node: TdxMasterViewNode); begin if Assigned(FOnExpanded) then FOnExpanded(Self, Node); end; function TdxMasterViewLevel.DoBeforeCollapse(Node: TdxMasterViewNode): Boolean; begin Result := True; if Assigned(FOnCollapsing) then FOnCollapsing(Self, Node, Result); end; function TdxMasterViewLevel.DoBeforeExpand(Node: TdxMasterViewNode): Boolean; begin Result := True; if Assigned(FOnExpanding) then FOnExpanding(Self, Node, Result); end; procedure TdxMasterViewLevel.DoGetContentStyle(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; var NewStyle: TdxMasterViewStyle); begin if Assigned(FOnGetContentStyle) then FOnGetContentStyle(Self, Node, Column, NewStyle); end; procedure TdxMasterViewLevel.DoGetFooterCellText(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; var Text: string); begin if Assigned(FOnGetFooterCellText) then FOnGetFooterCellText(Self, Node, Column, Text); end; procedure TdxMasterViewLevel.DoHideColumn(Column: TdxMasterViewColumn); begin if Assigned(FOnHideColumn) then FOnHideColumn(Self, Column); end; procedure TdxMasterViewLevel.DoNodeDeleted(Node: TdxMasterViewNode); begin if Node = FEditingNode then FEditingNode := nil; if Assigned(FOnDeleteNode) then FOnDeleteNode(Self, Node); end; procedure TdxMasterViewLevel.DoShowColumn(Column: TdxMasterViewColumn); begin if Assigned(FOnShowColumn) then FOnShowColumn(Self, Column); end; procedure TdxMasterViewLevel.DoSorting; var PrevCursor: TCursor; begin if (SortedColumnCount = 0) or (FSortingLockCount <> 0) or (FLoadLockCount <> 0) or IsDestroying then Exit; with FControl do begin PrevCursor := Screen.Cursor; if ShowHourGlassCursor then Screen.Cursor := crHourGlass; BeginUpdate; try Items.DoSorting(Self); finally EndUpdate; ShowFocusedNode; if ShowHourGlassCursor then Screen.Cursor := PrevCursor; end; end; end; procedure TdxMasterViewLevel.DrawIndent(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); var R: TRect; NextLevelIndex, I, RelativeLevelIndex: Integer; IsLowerIndent: Boolean; ABrush: HBRUSH; function GetYoungerParentNodeIndex(AParentIndex: Integer): Integer; begin for Result := AParentIndex - 1 downto 0 do if Node.ParentNodes[Result].NodeType = ntData then Exit; Result := 0; end; begin R := ARect; Dec(R.Left, FControl.LeftPos); if Node.AbsoluteIndex = Control.AbsoluteItemCount - 1 then NextLevelIndex := -1 else with FControl.AbsoluteItems[Node.AbsoluteIndex + 1], ViewInfo do if AbsoluteIndex = FControl.TopIndex + FControl.FLastPartVisibleItemCount then NextLevelIndex := GetLevelIndex else NextLevelIndex := LevelIndex; for I := Node.ViewInfo.LevelIndex downto 1 do with Node.ViewInfo, Node.ParentNodes[I], R do begin Right := Left + LevelIndent; if (Level = Self) and (Node.ViewInfo.GroupByBoxSize <> 0) and (NodeType in [ntData, ntGroup]) and (ParentNode.NodeType in [ntData, ntCaption]) then Inc(Top, Node.ViewInfo.GroupByBoxSize); if AbsoluteIndex < FControl.TopIndex then IsLowerIndent := NextLevelIndex <= GetLevelIndex else IsLowerIndent := NextLevelIndex <= ViewInfo.LevelIndex; with Level do begin GetContentParams(ParentNodes[0], nil, @ABrush, nil, nil, nil, False); case NodeType of ntData: begin RelativeLevelIndex := Node.Level.IndexOfParent(Level); if Node.HasFooter(RelativeLevelIndex) then Dec(Bottom, RowCount * FooterHeight); if IsLowerIndent then begin if (RowSeparatorWidth <> 0) and Node.HasRowSeparator(GetYoungerParentNodeIndex(I)) then begin FillRect(DC, Rect(Left, Bottom - RowSeparatorWidth, Right + ExtLineWidth + LevelSeparatorWidth, Bottom), RowSeparatorBrush); Dec(Bottom, RowSeparatorWidth); end; FillRect(DC, Rect(Left, Bottom - ExtLineWidth, Right, Bottom), GridLinesBrush); Dec(Bottom, ExtLineWidth); FillRect(DC, R, ABrush); Inc(Bottom, ExtLineWidth); end else FillRect(DC, R, ABrush); if ExtLineWidth <> 0 then begin FillRect(DC, Rect(Right, Top, Right + ExtLineWidth, Bottom), GridLinesBrush); Inc(Right, ExtLineWidth); end; if LevelSeparatorWidth <> 0 then begin FillRect(DC, Rect(Right, Top, Right + LevelSeparatorWidth, Bottom), LevelSeparatorBrush); Inc(Right, LevelSeparatorWidth); if Node.HasLevelSeparator(GetYoungerParentNodeIndex(I)) then Dec(Bottom, LevelSeparatorWidth); end; Left := Right; end; ntCaption, ntGroup: begin if IsLowerIndent then begin FillRect(DC, Rect(Left, Bottom - ExtLineWidth, Right, Bottom), GridLinesBrush); Dec(Bottom, ExtLineWidth); FillRect(DC, R, ABrush); Inc(Bottom, ExtLineWidth); end else FillRect(DC, R, ABrush); FillRect(DC, Rect(Right, Top, Right + ExtLineWidth, Bottom), GridLinesBrush); Left := Right + ExtLineWidth; Right := Left + LevelIndent; end; end; end; end; end; procedure TdxMasterViewLevel.DrawGroupByBox(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); var ATextColor, ABkColor: TColor; AFont: TFont; R, R1: TRect; RestRgn, ClipRgn, Rgn: HRGN; ClipRgnExists: Boolean; PrevFont: HFONT; Size: TSize; I, X, Y: Integer; ABrush, LineBrush: HBRUSH; procedure ExcludeRect(const R: TRect); begin Rgn := CreateRectRgnIndirect(R); CombineRgn(RestRgn, RestRgn, Rgn, RGN_DIFF); DeleteObject(Rgn); end; begin GetGroupByBoxParams(Node, @ABrush, @ATextColor, @ABkColor, @AFont); R := ARect; RestRgn := CreateRectRgnIndirect(R); try ClipRgn := CreateRectRgn(0, 0, 0, 0); ClipRgnExists := GetClipRgn(DC, ClipRgn) = 1; with R do IntersectClipRect(DC, Left, Top, Right, Bottom); try if GroupColumnCount = 0 then begin SetTextColor(DC, ColorToRGB(ATextColor)); SetBkColor(DC, ColorToRGB(ABkColor)); PrevFont := SelectObject(DC, AFont.Handle); GetTextExtentPoint{32}(DC, PChar(dxMVGroupByBoxText), Length(dxMVGroupByBoxText), Size); with R do begin Inc(Left, GroupByBoxLeftOffset); if Left + Size.cx < Right then Right := Left + Size.cx; Top := (Top + Bottom - Size.cy) div 2; Bottom := Top + Size.cy; end; DrawText(DC, PChar(dxMVGroupByBoxText), Length(dxMVGroupByBoxText), R, DT_SINGLELINE); SelectObject(DC, PrevFont); ExcludeRect(R); end else begin LineBrush := CreateSolidBrush(ColorToRGB(GroupByBoxLineColor)); try for I := 0 to GroupColumnCount - 1 do with GroupColumns[I] do begin R := GetGroupByBoxBounds(Node); ExcludeRect(R); if I < GroupColumnCount - 1 then with R do begin Y := (HeaderRealHeight div 2 + GroupByBoxVerOffset) div 2; X := Right - 2 * Y;//(Left + Right) div 2; Y := Bottom + Y; R1 := Rect(X, Bottom, X + 1, Y); FillRect(DC, R1, LineBrush); ExcludeRect(R1); R1 := Rect(X + 1, Y - 1, Right + GroupByBoxHorOffset, Y); FillRect(DC, R1, LineBrush); ExcludeRect(R1); end; if RectVisible(DC, R) then begin DrawEdge(DC, R, BDR_RAISEDINNER, BF_RECT); R1 := R; InflateRect(R, -1, -1); DrawHeader(DC, R, Node); if Pressed then DrawDowned(DC, R1); end; end; finally DeleteObject(LineBrush); end; end; finally SelectClipRgn(DC, Byte(ClipRgnExists) * ClipRgn); DeleteObject(ClipRgn); end; finally FillRgn(DC, RestRgn, ABrush); DeleteObject(RestRgn); R := ARect; with R do begin Left := Right; Right := FControl.ClientWidth; end; FillRect(DC, R, FControl.Brush.Handle); end; end; procedure TdxMasterViewLevel.DrawHeader(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); var ABrush: HBRUSH; R, R1, R2: TRect; RestRgn, Rgn: HRGN; I: Integer; Column: TdxMasterViewColumn; procedure ExcludeRect(const R: TRect); begin Rgn := CreateRectRgnIndirect(R); CombineRgn(RestRgn, RestRgn, Rgn, RGN_DIFF); DeleteObject(Rgn); end; begin GetHeaderParams(Node, nil, nil, @ABrush, nil, nil, nil); R := ARect; RestRgn := 0; if FHorizontal then if RowCount > 1 then RestRgn := CreateRectRgnIndirect(ARect) else else if (Node.ViewInfo.PreviewSize = 0) and (LineWidth <> 0) then Dec(R.Bottom, LineWidth); try for I := 0 to VisibleColumnCount - 1 do begin Column := VisibleColumns[I]; with Column, R1 do begin R1 := GetHeaderBounds(Node); if RestRgn <> 0 then ExcludeRect(R1) else if not FHorizontal and FLayout.IsColumnLast(Column) then FillRect(DC, Rect(Left, Bottom, Right, R.Bottom), ABrush); if (Left = Right) or not RectVisible(DC, R1) then Continue; // draw column's border DrawEdge(DC, R1, BDR_RAISEDINNER, BF_RECT); // draw column's content R2 := R1; InflateRect(R2, -1, -1); DrawHeader(DC, R2, Node); if Pressed then DrawDowned(DC, R1); end; end; finally // fill in rest space if RestRgn <> 0 then begin FillRgn(DC, RestRgn, ABrush); DeleteObject(RestRgn); end; // draw rest space if FHorizontal then begin with R do begin Left := Right; Right := FControl.ClientWidth; end; FillRect(DC, R, FControl.Brush.Handle); end; end; end; procedure TdxMasterViewLevel.DrawContent(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); type PPointArray = ^TPointArray; TPointArray = array[0..MaxInt div SizeOf(TPoint) - 1] of TPoint; PDWORDArray = ^TDWORDArray; TDWORDArray = array[0..MaxInt div SizeOf(DWORD) - 1] of DWORD; var R, R1, R2: TRect; RestRgn, Rgn: HRGN; AShowGrid, AHasExpandButton, IsMultiRows, HasLeftBorder, HasTopBorder: Boolean; S: string; I, J, K, L: Integer; AFont: TFont; P, RectsP: PPointArray; C, RectsC: PDWORDArray; Column: TdxMasterViewColumn; ABrush: HBRUSH; ATextColor, ABkColor: TColor; ADC: HDC; PrevFont: HFONT; procedure DrawFocus(const R: TRect); begin R1 := R; if AShowGrid or ShowPreview then begin if Node.Horizontal or AShowGrid or (I = FLayout.Count - 1) then Dec(R1.Right, ExtLineWidth); if Node.Horizontal or AShowGrid then Dec(R1.Bottom, ExtLineWidth); end; if FControl.HasFocus then begin with R1 do if Odd(Left) = Odd(Top) then begin SetTextColor(DC, $FFFFFF); SetBkColor(DC, 0); end else begin SetTextColor(DC, 0); SetBkColor(DC, $FFFFFF); end; DrawFocusRect(DC, R1); end else if not (movHideFocusRect in FControl.OptionsView) then if FControl.InactiveFocusColor = clNone then with R1 do begin BitBlt(DC, Left, Top, Right - Left, 1, 0, 0, 0, DSTINVERT); BitBlt(DC, Left, Bottom - 1, Right - Left, 1, 0, 0, 0, DSTINVERT); BitBlt(DC, Left, Top + 1, 1, Bottom - Top - 2, 0, 0, 0, DSTINVERT); BitBlt(DC, Right - 1, Top + 1, 1, Bottom - Top - 2, 0, 0, 0, DSTINVERT); end else FrameRect(DC, R1, FControl.InactiveFocusBrush); end; begin R := ARect; AShowGrid := HasGrid; AHasExpandButton := Node.HasExpandButton; Inc(R.Left, Byte(AHasExpandButton) * LevelIndent); IsMultiRows := False; case Node.NodeType of ntData: begin IsMultiRows := FHorizontal and (RowCount > 1); R.Bottom := R.Top + VisibleRowCount * ContentRealHeight + Node.ViewInfo.PreviewSize; if (Node.ViewInfo.PreviewSize <> 0) or (LineWidth = 0) then Inc(R.Bottom, ExtLineWidth); end; ntCaption: R.Bottom := R.Top + CaptionHeight + ExtLineWidth; ntGroup: R.Bottom := R.Top + GroupHeight + ExtLineWidth; end; R1 := R; // draw expand button if AHasExpandButton then with R, Node do begin GetContentParams(Node, nil, @ABrush, nil, nil, nil, False); R2 := Rect(Left - LevelIndent, Top, Left, Bottom - ExtLineWidth); if Node.ShowExpandButton then DrawExpandButton(DC, R2, ABrush, Expanded, CanExpand) else FillRect(DC, R2, ABrush); if ShowGrid then begin if not Node.Expanded then ABrush := GridLinesBrush; FillRect(DC, Rect(Left - LevelIndent, Bottom - ExtLineWidth, Left, Bottom), ABrush); end; end; // draw last(bottom and right) grid lines with R1 do if Left < Right then begin FillRect(DC, Rect(Left, Bottom - ExtLineWidth, Right, Bottom), GridLinesBrush); Dec(Bottom, ExtLineWidth); FillRect(DC, Rect(Right - ExtLineWidth, Top, Right, Bottom), GridLinesBrush); Dec(Right, ExtLineWidth); end; Dec(R1.Bottom, Node.ViewInfo.PreviewSize); if Node.NodeType in [ntCaption, ntGroup] then begin GetContentParams(Node, nil, @ABrush, @ATextColor, @ABkColor, @AFont, True); if Node.NodeType = ntCaption then S := FCaption else with GroupColumns[Node.GroupIndex] do S := Caption + ' : ' + GetGroupDisplayText(Node); FControl.DrawText(DC, R1, AFont, ABrush, ATextColor, ABkColor, taLeftJustify, S, True, True, True, False); end else begin R2 := R1; GetMem(P, 2 * 4 * VisibleColumnCount * SizeOf(TPoint)); GetMem(C, 4 * VisibleColumnCount * SizeOf(DWORD)); if IsMultiRows then begin GetMem(RectsP, 4 * VisibleColumnCount * SizeOf(TPoint)); GetMem(RectsC, VisibleColumnCount * SizeOf(DWORD)); end else begin RectsP := nil; RectsC := nil; end; J := 0; GetContentParams(Node, nil, @ABrush, nil, nil, nil, True); try // draw data in cells for I := 0 to VisibleColumnCount - 1 do begin Column := VisibleColumns[I]; with Column, R1 do begin R1 := GetContentBounds(Node); Inc(Left, AddInWidth); if Left = Right then Continue; if not FHorizontal then begin K := FLayout.ContentWidths[ColIndex]; L := Left + K; if (ColIndex = FLayout.Count - 1) and (RowIndex = 0) and (L < R2.Right) then FillRect(DC, Rect(L, Top, R2.Right, R2.Bottom), ABrush); if L = R.Right then L := R2.Right; if Right < L then FillRect(DC, Rect(Right, Top, L, Bottom - Byte(Bottom = R.Bottom) * LineWidth), ABrush); if FLayout.IsColumnLast(Column) and (Bottom < R2.Bottom) then FillRect(DC, Rect(Left, Bottom, L, R2.Bottom), ABrush); end; HasLeftBorder := Left <> R.Left; HasTopBorder := Top <> R.Top; if AShowGrid then begin if HasLeftBorder then Dec(Left, LineWidth); if HasTopBorder then Dec(Top, LineWidth); end else if Right = R.Right then Dec(Right, ExtLineWidth); if IsMultiRows then begin K := 4 * I; RectsP[K] := TopLeft; with RectsP[K + 1] do begin X := Right; Y := Top; end; RectsP[K + 2] := BottomRight; with RectsP[K + 3] do begin X := Left; Y := Bottom; end; RectsC[I] := 4; end; if not RectVisible(DC, R1) then Continue; // calc cell's border if AShowGrid then begin if HasLeftBorder then begin with P[J] do begin X := Left; Y := Top; end; with P[J + 1] do begin X := Left; Y := Bottom; end; Inc(J, 2); Inc(Left, LineWidth); end; if HasTopBorder then begin with P[J] do begin X := Left; Y := Top; end; with P[J + 1] do begin X := Right; Y := Top; end; Inc(J, 2); Inc(Top, LineWidth); end; with P[J] do begin X := Left; Y := Bottom - LineWidth; end; with P[J + 1] do begin X := Right; Y := P[J].Y; end; Inc(J, 2); Dec(Bottom, LineWidth); with P[J] do begin X := Right - LineWidth; Y := Top; end; with P[J + 1] do begin X := P[J].X; Y := Bottom; end; Inc(J, 2); Dec(Right, LineWidth); end; // draw cell's content DrawContent(DC, R1, Node); end; end; // draw grid J := J div 2; if J <> 0 then begin for I := 0 to J - 1 do C[I] := 2; FGridLinesPen := SelectObject(DC, FGridLinesPen); PolyPolyLine(DC, P^, C^, J); FGridLinesPen := SelectObject(DC, FGridLinesPen); end; // fill in rest space if IsMultiRows then begin RestRgn := CreateRectRgnIndirect(R2); Rgn := CreatePolyPolygonRgn(RectsP^, RectsC^, VisibleColumnCount, WINDING); CombineRgn(RestRgn, RestRgn, Rgn, RGN_DIFF); DeleteObject(Rgn); FillRgn(DC, RestRgn, ABrush); DeleteObject(RestRgn); end; finally if IsMultiRows then begin FreeMem(RectsC, VisibleColumnCount * SizeOf(DWORD)); FreeMem(RectsP, 4 * VisibleColumnCount * SizeOf(TPoint)); end; FreeMem(C, 4 * VisibleColumnCount * SizeOf(DWORD)); FreeMem(P, 2 * 4 * VisibleColumnCount * SizeOf(TPoint)); end; end; // draw preview if Node.ViewInfo.PreviewSize <> 0 then begin R1 := R2; R1.Top := R1.Bottom; R1.Bottom := R1.Top + Node.ViewInfo.PreviewSize; if RectVisible(DC, R1) then with R1 do begin R2 := R1; GetPreviewParams(Node, @ABrush, @ATextColor, nil, @AFont); if movUseBitmapToDrawPreview in FControl.OptionsView then with R1 do begin OffsetRect(R1, -Left, -Top); with FControl do begin PrepareBitmap(Right, Bottom); ADC := FBitmap.Canvas.Handle; end; end else ADC := DC; begin with R1 do begin FillRect(ADC, R1, ABrush); Inc(Left, FPreviewLeftIndent); Dec(Right, FPreviewRightIndent); Inc(Top); Dec(Bottom); end; SetTextColor(ADC, ColorToRGB(ATextColor)); SetBkMode(ADC, TRANSPARENT); PrevFont := SelectObject(ADC, AFont.Handle); Windows.DrawText(ADC, PChar(Node.PreviewText), Length(Node.PreviewText), R1, DT_EXPANDTABS or DT_NOPREFIX or DT_WORDBREAK); SelectObject(ADC, PrevFont); SetBkMode(ADC, OPAQUE); end; if movUseBitmapToDrawPreview in FControl.OptionsView then with R2 do BitBlt(DC, Left, Top, Right - Left, Bottom - Top, ADC, 0, 0, SRCCOPY); end; end; // draw focus rect if Node.Focused then if {true}Node.Horizontal or not ShowHeader then DrawFocus(R) else begin R2 := R; for I := 0 to FLayout.Count - 1 do with FLayout, R2 do begin Inc(Left, HeaderWidths[I]); Right := Left + ContentWidths[I]; Bottom := Top + Items[I].Count * ContentRealHeight; DrawFocus(R2); Left := Right; end; end; // draw separator of level or row if Node.NodeType = ntData then if Node.Expanded then if LevelSeparatorWidth <> 0 then with R do begin GetContentParams(Node, nil, @ABrush, nil, nil, nil, False); FillRect(DC, Rect(Left - LevelIndent, Bottom, Left, Bottom + LevelSeparatorWidth), ABrush); FillRect(DC, Rect(Left, Bottom, Left + ExtLineWidth, Bottom + LevelSeparatorWidth), GridLinesBrush); FillRect(DC, Rect(Left + ExtLineWidth, Bottom, FControl.ClientWidth, Bottom + LevelSeparatorWidth), LevelSeparatorBrush); end else else if RowSeparatorWidth <> 0 then with R do begin FillRect(DC, Rect(ARect.Left, Bottom, Right, Bottom + RowSeparatorWidth), RowSeparatorBrush); FillRect(DC, Rect(Right, Bottom, FControl.ClientWidth, Bottom + RowSeparatorWidth), FControl.Brush.Handle); end; // draw rest space with R do begin Left := Right; Right := FControl.ClientWidth; end; FillRect(DC, R, FControl.Brush.Handle); end; procedure TdxMasterViewLevel.DrawFooter(DC: HDC; const ARect: TRect; Node: TdxMasterViewNode); var ABrush: HBRUSH; R, R1, R2: TRect; RestRgn, Rgn: HRGN; I, J, K: Integer; procedure ExcludeRect(const R: TRect); begin Rgn := CreateRectRgnIndirect(R); CombineRgn(RestRgn, RestRgn, Rgn, RGN_DIFF); DeleteObject(Rgn); end; begin R := ARect; for I := 0 to Node.ViewInfo.EndLevelCount - 1 do with Parents[I] do begin if Node.HasFooter(I) then begin GetFooterParams(Node, nil, @ABrush, nil, nil, nil); R2 := Node.SubFooterBounds[I]; Inc(R.Top, R2.Bottom - R2.Top); DrawEdge(DC, R2, BDR_RAISEDINNER, BF_RECT); InflateRect(R2, -1, -1); RestRgn := CreateRectRgnIndirect(R2); try for J := 0 to VisibleColumnCount - 1 do with VisibleColumns[J], R1 do begin R1 := GetFooterBounds(Node); InflateRect(R1, -1, -1); ExcludeRect(R1); if (Left >= Right) or not RectVisible(DC, R1) then Continue; if SummaryType = stNone then FillRect(DC, R1, ABrush) else begin FrameRect(DC, R1, ABrush); InflateRect(R1, -1, -1); DrawEdge(DC, R1, BDR_SUNKENOUTER, BF_RECT); InflateRect(R1, -1, -1); // draw summary's content DrawFooter(DC, R1, Node); end; end; finally // fill in rest space FillRgn(DC, RestRgn, ABrush); DeleteObject(RestRgn); // draw rest space InflateRect(R2, 1, 1); with R2 do begin Left := Right; Right := FControl.ClientWidth; FillRect(DC, R2, {Content}FControl.Brush.Handle); end; end; end; if Parent.Parent <> nil then begin J := R.Left; if (I <> 0) or (Node.NodeType = ntData) then Dec(J, (Byte(ShowCaption) + GroupColumnCount) * (LevelIndent + ExtLineWidth)) else if Node.NodeType = ntGroup then Dec(J, (Byte(ShowCaption) + Node.GroupIndex) * (LevelIndent + ExtLineWidth)); with Parent, R do begin if (LevelSeparatorWidth <> 0) and Node.HasLevelSeparator(I) then begin FillRect(DC, Rect(J, Top, FControl.ClientWidth, Top + LevelSeparatorWidth), LevelSeparatorBrush); Inc(Top, LevelSeparatorWidth); end; if (RowSeparatorWidth <> 0) and Node.HasRowSeparator(I) then begin K := Indent + VisibleWidth - FControl.LeftPos{?!}; FillRect(DC, Rect(J, Top, K, Top + RowSeparatorWidth), RowSeparatorBrush); FillRect(DC, Rect(K, Top, FControl.ClientWidth, Top + RowSeparatorWidth), FControl.Brush.Handle); Inc(Top, RowSeparatorWidth); end; Left := J - (LevelIndent + ExtLineWidth + LevelSeparatorWidth); end; end; end; end; procedure TdxMasterViewLevel.EditingChanged; begin if (FDataLink.DataSet.State = dsEdit) {and CanSmartRefresh }then FEditingNode := FControl.NodeFromID(Self, GetCurIDValue); end; procedure TdxMasterViewLevel.FooterStyleChanged(Values: TdxMasterViewStyleValues); var Size: TSize; H, I: Integer; begin if (Values = []) or (svFont in Values) then begin if VisibleColumnCount = 0 then begin CalcFontSize(FooterFont, Size); H := Size.cy; end else begin H := 0; for I := 0 to VisibleColumnCount - 1 do begin CalcFontSize(VisibleColumns[I].FooterFont, Size); if Size.cy > H then H := Size.cy; end; end; FFooterHeight := H + 2 + 2 * 3; // 21 end; LevelChanged(False); end; function TdxMasterViewLevel.GetCurDetailKeyValue: Variant; var I: Integer; begin I := FDetailKeyFields.Count; case I of 0: Result := Unassigned; 1: Result := TField(FDetailKeyFields[0]).Value; else Result := VarArrayCreate([0, I - 1], varVariant); for I := 0 to I - 1 do Result[I] := TField(FDetailKeyFields[I]).Value; end; end; function TdxMasterViewLevel.GetCurIDValue: Variant; var I: Integer; begin I := FIDFields.Count; case I of 0: Result := Unassigned; 1: Result := TField(FIDFields[0]).Value; else Result := VarArrayCreate([0, I - 1], varVariant); for I := 0 to I - 1 do Result[I] := TField(FIDFields[I]).Value; end; end; function TdxMasterViewLevel.GetCurKeyValue: Variant; var I: Integer; begin I := FItems.Count; if I = 0 then Result := Unassigned else begin Result := VarArrayCreate([0, I - 1], varVariant); for I := 0 to I - 1 do Result[I] := TdxMasterViewLevel(FItems[I]).GetCurMasterKeyValue; end; end; function TdxMasterViewLevel.GetCurMasterKeyValue: Variant; var I: Integer; begin I := FMasterKeyFields.Count; case I of 0: Result := Unassigned; 1: Result := TField(FMasterKeyFields[0]).Value; else Result := VarArrayCreate([0, I - 1], varVariant); for I := 0 to I - 1 do Result[I] := TField(FMasterKeyFields[I]).Value; end; end; function TdxMasterViewLevel.GetCurPreviewText(Node: TdxMasterViewNode): string; begin if PreviewField = nil then Result := '' else begin Result := PreviewField.AsString; if (FPreviewMaxLength <> 0) and (Length(Result) > FPreviewMaxLength) then Result := Copy(Result, 1, FPreviewMaxLength); end; if Assigned(FOnGetPreviewText) then FOnGetPreviewText(Self, Node, Result); end; function TdxMasterViewLevel.GetParentNode(Root, Node: TdxMasterViewNode; FirstIndex, AvailIndex: PInteger; RestItems: TList; CheckRoot: Boolean): TdxMasterViewNode; var I, L, R, C, CResult, APos: Integer; function CompareGroupValues(const Node: TdxMasterViewNode): Integer; begin GPNCurValue := Node.GroupValue; GPNCurValueIsNull := VarIsNull(GPNCurValue); if GPNFindingValueIsNull or GPNCurValueIsNull then if GPNFindingValueIsNull and GPNCurValueIsNull then Result := 0 else if GPNFindingValueIsNull then Result := 1 else Result := -1 else if VarType(GPNFindingValue) <> VarType(GPNCurValue) then begin Result := -1; Exit; end else begin if GPNCanUseCaseInsensitiveGrouping then GPNCurValue := VarAnsiUpperCase(GPNCurValue); if GPNCanUseAnsiCompareStrOnGrouping then begin GPNS2 := GPNCurValue; Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(GPNS1), Length(GPNS1), PChar(GPNS2), Length(GPNS2)) - 2; end else if GPNCurValue < GPNFindingValue then Result := -1 else if GPNCurValue = GPNFindingValue then Result := 0 else Result := 1; end; if GPNReverseOrder then Result := -Result; end; function NodeFromGroupValue(const Root: TdxMasterViewNode; var AParentNode: TdxMasterViewNode): Integer; begin Result := 0; if I = 0 then begin if FirstIndex = nil then begin AParentNode := Root.FirstInLevel[Self]; if AParentNode = nil then begin Result := Root.GetAvailIndex(Index); Exit; end; L := AParentNode.Index; end else L := FirstIndex^; AParentNode := nil; if AvailIndex = nil then R := L + Root.CountInLevel[Self] - 1 else R := AvailIndex^ - 1; if L > R then begin Result := R + 1; Exit; end; end else begin AParentNode := nil; R := Root.FItems.Count - 1; if R = -1 then Exit; L := 0; end; while L <= R do begin C := (L + R) div 2; CResult := CompareGroupValues(Root.FItems[C]); case CResult of 0: begin Result := C; AParentNode := Root.FItems[C]; Exit; end; -1: L := C + 1; 1: R := C - 1; end; end; Result := C; if CResult = -1 then Inc(Result); end; procedure RemoveNodeFromRestItems(const Node: TdxMasterViewNode); begin R := RestItems.Count - 1; if R = -1 then Exit; L := 0; while L <= R do begin C := (L + R) div 2; CResult := Integer(Node) - Integer(RestItems.List^[C]); if CResult = 0 then begin RestItems.Delete(C); Break; end else if CResult < 0 then R := C - 1 else L := C + 1; end; end; begin if CheckRoot then begin if (Root = nil) or not Root.Expanded and SmartLoad then begin Result := nil; Exit; end else Result := Root; if (Result.NodeType = ntData) and ShowCaption then begin Result := Result.FirstInLevel[Self]; if not Result.Expanded and SmartLoad then begin Result := nil; Exit; end end; end else Result := Root; for I := 0 to FGroupColumns.Count - 1 do begin GPNCanUseAnsiCompareStrOnGrouping := False; GPNCanUseCaseInsensitiveGrouping := False; with TdxMasterViewColumn(FGroupColumns[I]) do if {(Node = nil)!!! and} (Field = nil) then begin GPNGroupValue := Null; GPNDisplayText := ''; GPNFindingValue := Null; end else begin if Node = nil then begin GPNGroupValue := Field.Value; GPNDisplayText := Field.DisplayText; end else begin GPNGroupValue := Node.Values[Index]; GPNDisplayText := Node.Strings[Index]; end; GPNFindingValue := GPNGroupValue; if not VarIsNull(GPNFindingValue) and (VarType(GPNFindingValue) = varString) then begin GPNCanUseAnsiCompareStrOnGrouping := mobAnsiCompareStrOnGrouping in FControl.OptionsBehavior; GPNCanUseCaseInsensitiveGrouping := mobCaseInsensitiveGrouping in FControl.OptionsBehavior; if GPNCanUseCaseInsensitiveGrouping then GPNFindingValue := VarAnsiUpperCase(GPNFindingValue); if GPNCanUseAnsiCompareStrOnGrouping then GPNS1 := GPNFindingValue; end; end; GPNFindingValueIsNull := VarIsNull(GPNFindingValue); GPNReverseOrder := TdxMasterViewColumn(FGroupColumns[Result.GroupIndex + 1]).SortOrder = soDescending; APos := NodeFromGroupValue(Result, GPNParentNode); if GPNParentNode = nil then begin if (I = 0) and (AvailIndex <> nil) then Inc(AvailIndex^); with Result do GPNParentNode := Add(Self, APos, ntGroup, GPNDisplayText, GPNGroupValue); end else if RestItems <> nil then RemoveNodeFromRestItems(GPNParentNode); Result := GPNParentNode; end; Result.CheckSummaries; end; procedure TdxMasterViewLevel.GroupByBoxStyleChanged(Values: TdxMasterViewStyleValues); var Size: TSize; begin if (Values = []) or (svFont in Values) then begin CalcFontSize(GroupByBoxFont, Size); FGroupByBoxTextHeight := Size.cy + 2 + 2 * 2; // 19 end; if ShowGroupByBox then LevelChanged(False); end; procedure TdxMasterViewLevel.GroupStyleChanged(Values: TdxMasterViewStyleValues); var Size: TSize; begin if (Values = []) or (svFont in Values) then begin CalcFontSize(GroupFont, Size); FGroupHeight := Size.cy + 2 + 2; // 17 - without line end; if GroupColumnCount <> 0 then LevelChanged(False); end; function TdxMasterViewLevel.HasExpandButton: Boolean; begin Result := HasChildren and (VisibleColumnCount <> 0); end; function TdxMasterViewLevel.HasGrid: Boolean; begin Result := ShowGrid and (not ShowPreview or ShowGridWithPreview); end; { function TdxMasterViewLevel.HasAsChild(Level: TdxMasterViewLevel): Boolean; var I: Integer; ALevel: TdxMasterViewLevel; begin Result := False; for I := AbsoluteIndex to FControl.AbsoluteLevelCount - 1 do begin ALevel := FControl.AbsoluteLevels[I]; if ALevel.Parent = Parent then Break; if Level = ALevel then begin Result := True; Break; end; end; end; } function TdxMasterViewLevel.HasAsParent(Level: TdxMasterViewLevel): Boolean; var ALevel: TdxMasterViewLevel; begin ALevel := Self; while (ALevel <> nil) and (ALevel <> Level) do ALevel := ALevel.Parent; Result := ALevel <> nil; end; procedure TdxMasterViewLevel.HeaderStyleChanged(Values: TdxMasterViewStyleValues); var Size: TSize; H, I: Integer; begin if (Values = []) or (svFont in Values) then begin if VisibleColumnCount = 0 then begin CalcFontSize(HeaderFont, Size); H := Size.cy; end else begin H := 0; for I := 0 to VisibleColumnCount - 1 do begin CalcFontSize(VisibleColumns[I].HeaderFont, Size); if Size.cy > H then H := Size.cy; end; end; FHeaderHeight := H + 2 + 2 * 2; // 19 CalcRealHeights; if ShowHeader then CalcDefaultWidths(nil); end; if ShowHeader then LevelChanged(False); if GroupColumnCount <> 0 then GroupByBoxStyleChanged(Values); end; function TdxMasterViewLevel.IndexOfParent(Level: TdxMasterViewLevel): Integer; begin for Result := 0 to FParentCount - 1 do if FParents[Result] = Level then Exit; Result := -1; end; procedure TdxMasterViewLevel.LayoutChanged; var I: Integer; begin RefreshDetailKeyFieldsList; RefreshIDFieldsList; for I := 0 to Count - 1 do Items[I].RefreshMasterKeyFieldsList; SetPreviewFieldInternally(FindPreviewField); for I := 0 to ColumnCount - 1 do with Columns[I] do begin SetFieldInternally(FindField); if cvSummaryFieldName in AssignedValues then SetSummaryFieldInternally(FindSummaryField); end; CalcDefaultWidths(nil); WidthChangedEx; end; procedure TdxMasterViewLevel.LevelChanged(HardRefresh: Boolean); var PrevCursor: TCursor; begin if IsDestroying then Exit; if FControl <> nil then begin if FControl.IsLoading then Exit; if HardRefresh then begin if (FDataChangingLockCount <> 0) or (FControl.FLayoutLockCount <> 0) or IsLoading or FControl.IsDestroying then Exit; FControl.DoBeforeUpdateData(Self); try if FParent = nil then if FControl.FItems <> nil then with FControl do begin FItems.UpdateData(False, True, False); CanUseSmartReload := True; end else else begin PrevCursor := Screen.Cursor; if FControl.ShowHourGlassCursor then Screen.Cursor := crHourGlass; FControl.BeginTopNodeChange; try FControl.BeginUpdate; try BeginLoad; try FControl.FItems.UpdateDataForLevel(Self); finally EndLoad(True); end; finally try if not FControl.SyncMove then SyncPos; finally FControl.EndUpdate; if FControl.SyncMove then SyncPos; if FControl.ShowHourGlassCursor then Screen.Cursor := PrevCursor; FCanUseSmartReload := True; end; end; finally FControl.EndTopNodeChange; end; end; finally FControl.DoAfterUpdateData(Self); end; end else FControl.Invalidate; end; end; function TdxMasterViewLevel.LoadDataOnExpand: Boolean; var I: Integer; begin Result := True; for I := 0 to Count - 1 do if Items[I].SmartLoad then Exit; Result := False; end; function MakeVisibleColumnsSortedListCompareHor(Column1, Column2: TdxMasterViewColumn): Integer; begin if Column1.FRowIndex < Column2.FRowIndex then Result := -1 else if Column1.FRowIndex > Column2.FRowIndex then Result := 1 else if Column1.FColIndex < Column2.FColIndex then Result := -1 else if Column1.FColIndex > Column2.FColIndex then Result := 1 else Result := 0; end; function MakeVisibleColumnsSortedListCompareVer(Column1, Column2: TdxMasterViewColumn): Integer; begin if Column1.FColIndex < Column2.FColIndex then Result := -1 else if Column1.FColIndex > Column2.FColIndex then Result := 1 else if Column1.FRowIndex < Column2.FRowIndex then Result := -1 else if Column1.FRowIndex > Column2.FRowIndex then Result := 1 else Result := 0; end; procedure TdxMasterViewLevel.MakeVisibleColumnsSortedList(List: TList); begin List.Count := VisibleColumnCount; Move(FVisibleColumns.List^, List.List^, List.Count * SizeOf(Pointer)); if FHorizontal then List.Sort(@MakeVisibleColumnsSortedListCompareHor) else List.Sort(@MakeVisibleColumnsSortedListCompareVer); end; { function TdxMasterViewLevel.ParentIndex(Level: TdxMasterViewLevel): Integer; var ALevel: TdxMasterViewLevel; begin Result := 0; ALevel := Self; while (ALevel <> nil) and (ALevel <> Level) do begin Inc(Result); ALevel := ALevel.Parent; end; if ALevel = nil then Result := -1; end; } procedure TdxMasterViewLevel.PreviewChanged; var I: Integer; begin if (FControl = nil) or not ShowPreview then Exit; with FControl do for I := 0 to AbsoluteItemCount - 1 do AbsoluteItems[I].FViewInfo.PreviewSize := 0; if not FControl.FPainting then LevelChanged(False); end; procedure TdxMasterViewLevel.PreviewStyleChanged(Values: TdxMasterViewStyleValues); begin if (Values = []) or (svFont in Values) then PreviewChanged else if ShowPreview then LevelChanged(False); end; procedure TdxMasterViewLevel.RecordChanged(Field: TField); begin { if not FControl.SyncMove or (FDataLink.DataSet.State = dsSetKey) then Exit; if FInsertedNode <> nil then with FInsertedNode do begin UpdateData(True, False, False); Invalidate(nil, vpContent); end; if FEditingNode <> nil then with FEditingNode do begin UpdateData(True, False, False); Invalidate(nil, vpContent); end;} end; procedure TdxMasterViewLevel.RemoveFreeSpace; begin FLayout.RemoveFreeSpace; end; type PColumnPlace = ^TColumnPlace; TColumnPlace = record Column: TdxMasterViewColumn; RowIndex, ColIndex, RowCount: Integer; end; procedure TdxMasterViewLevel.RestoreVisibleColumnsPlaces(var List: TList); var I: Integer; begin FLayout.BeginUpdate; try for I := 0 to List.Count - 1 do with PColumnPlace(List[I])^ do begin Column.RowIndex := RowIndex; Column.ColIndex := ColIndex; Column.RowCount := RowCount; end; finally FLayout.EndUpdate; FreeVisibleColumnsPlaces(List); end; end; procedure TdxMasterViewLevel.SaveVisibleColumnsPlaces(var List: TList); var I: Integer; ColumnPlace: PColumnPlace; begin List := TList.Create; MakeVisibleColumnsSortedList(List); for I := 0 to List.Count - 1 do begin New(ColumnPlace); ColumnPlace^.Column := List[I]; List[I] := ColumnPlace; with ColumnPlace^ do begin RowIndex := Column.RowIndex; ColIndex := Column.ColIndex; RowCount := Column.RowCount; end; end; end; procedure TdxMasterViewLevel.FreeVisibleColumnsPlaces(var List: TList); var I: Integer; begin for I := 0 to List.Count - 1 do Dispose(PColumnPlace(List[I])); List.Free; List := nil; end; procedure TdxMasterViewLevel.SyncPos; var I, AFreezeCount: Integer; Value: Variant; Node: TdxMasterViewNode; Level: TdxMasterViewLevel; { ft,lt:integer;} begin if not FControl.SyncMove then begin if FControl.FocusedNode <> nil then with FControl do begin FocusedNode.MakeVisible; ShowFocusedNode; end; Exit; end; // ft:=gettickcount; for I := 0 to ParentCount - 3 do with Parents[I] do if Active then if DataSet = Parent.DataSet then Continue else //if Parent.DataSet.Active then begin Value := GetCurDetailKeyValue; if not VarAreEqual(GetCurMasterKeyValue, Value) then begin AFreezeCount := FControl.FreezeDataSet(Parent); try Parent.DataSet.Locate(MasterKey, Value, []); finally if Parent.DataLink.FreezeCount = AFreezeCount then FControl.UnfreezeDataSet(Parent); end; end; end {else Break} else Break; Node := FControl.FItems; for I := ParentCount - 2 downto 0 do begin Level := Parents[I]; Node := Node.NodeFromID(Level, Level.GetCurIDValue); if Node = nil then Break else begin Node.MakeVisible; if I = 0 then with FControl do begin Freeze; try FocusNode(Node, True); finally Unfreeze; end; end; end; end; // lt:=gettickcount; // application.mainform.caption:=inttostr(lt-ft); end; procedure TdxMasterViewLevel.UncheckTriedToExpand; procedure ProcessOneNode(Node: TdxMasterViewNode); var I: Integer; begin with Node do if (NodeType = ntData) and (Level = Self) then FTriedToExpand := False else begin if ContainsDataInChildren(Self) then FTriedToExpand := False; if Self.HasAsParent(Level) then for I := 0 to Count - 1 do ProcessOneNode(Items[I]); end; end; begin ProcessOneNode(FControl.Items); end; procedure TdxMasterViewLevel.WidthChanged; var AWidth: Integer; begin if (FAssignWidthsLockCount = 0) and not IsLoading and not IsDestroying then begin AWidth := VisibleWidth; FLayout.CalcNeighbours; if not FHorizontal then FLayout.CalcColWidths; FControl.CalcScrollableWidth(Self); FLayout.CheckColumnsWidths(nil); if VisibleWidth <> AWidth then begin PreviewChanged; if Assigned(FOnWidthChanged) then FOnWidthChanged(Self); end; end; end; procedure TdxMasterViewLevel.WidthChangedEx; begin WidthChanged; LevelChanged(False); end; procedure TdxMasterViewLevel.Assign(Source: TPersistent); var ALevel: TdxMasterViewLevel; I: Integer; begin if Source is TdxMasterViewLevel then begin ALevel := TdxMasterViewLevel(Source); BeginLoading; try DestroyColumns; Caption := ALevel.Caption; CaptionStyle := ALevel.CaptionStyle; ContentStyle := ALevel.ContentStyle; DataSource := ALevel.DataSource; DeleteConfirmCaptionText := ALevel.DeleteConfirmCaptionText; DeleteConfirmText := ALevel.DeleteConfirmText; DetailKey := ALevel.DetailKey; FooterStyle := ALevel.FooterStyle; GridLinesColor := ALevel.GridLinesColor; GroupByBoxStyle := ALevel.GroupByBoxStyle; GroupStyle := ALevel.GroupStyle; HeaderStyle := ALevel.HeaderStyle; ID := ALevel.ID; LevelSeparatorColor := ALevel.LevelSeparatorColor; LevelSeparatorWidth := ALevel.LevelSeparatorWidth; MasterKey := ALevel.MasterKey; MultipleDeleteConfirmText := ALevel.MultipleDeleteConfirmText; OptionsBehavior := ALevel.OptionsBehavior; OptionsCustomize := ALevel.OptionsCustomize; OptionsDB := ALevel.OptionsDB; OptionsHeader := ALevel.OptionsHeader; OptionsView := ALevel.OptionsView; PreviewFieldName := ALevel.PreviewFieldName; PreviewLeftIndent := ALevel.PreviewLeftIndent; PreviewLineCount := ALevel.PreviewLineCount; PreviewMaxLength := ALevel.PreviewMaxLength; PreviewMaxLineCount := ALevel.PreviewMaxLineCount; PreviewRightIndent := ALevel.PreviewRightIndent; PreviewStyle := ALevel.PreviewStyle; RowSeparatorColor := ALevel.RowSeparatorColor; RowSeparatorWidth := ALevel.RowSeparatorWidth; ViewMode := ALevel.ViewMode; if not FHorizontal then begin FLayout.Count := ALevel.Layout.Count; for I := 0 to ALevel.Layout.Count - 1 do if ALevel.Layout.HeaderWidthAssigned[I] then FLayout.HeaderWidths[I] := ALevel.Layout.HeaderWidths[I]; end; for I := 0 to ALevel.ColumnCount - 1 do CreateColumn(TdxMasterViewColumnClass(ALevel.Columns[I].ClassType)).Assign(ALevel.Columns[I]); finally EndLoading; end; end else inherited; end; function TdxMasterViewLevel.GetParentComponent: TComponent; begin if IsTop then Result := FControl else Result := FParent; end; function TdxMasterViewLevel.HasParent: Boolean; begin Result := True; end; function TdxMasterViewLevel.Add: TdxMasterViewLevel; begin if ParentCount = 1 + 16 then Result := nil // due to .ViewInfo.EndLevelData else begin Result := TdxMasterViewLevel.Create(FControl.Owner); AddItem(Result); end; end; procedure TdxMasterViewLevel.BeginAssignWidths; begin Inc(FAssignWidthsLockCount); end; procedure TdxMasterViewLevel.BeginDataChanging; begin Inc(FDataChangingLockCount); end; procedure TdxMasterViewLevel.BeginLayout; begin BeginAssignWidths; FLayout.BeginUpdate; end; procedure TdxMasterViewLevel.BeginSorting; begin Inc(FSortingLockCount); end; procedure TdxMasterViewLevel.CancelAssignWidths; begin if FAssignWidthsLockCount > 0 then begin Dec(FAssignWidthsLockCount); {if FAssignWidthsLockCount = 0 then FLayout.CheckColumnsWidths;} end; end; procedure TdxMasterViewLevel.CancelDataChanging; begin if FDataChangingLockCount > 0 then begin Dec(FDataChangingLockCount); end; end; procedure TdxMasterViewLevel.CancelLayout; begin FLayout.CancelUpdate; CancelAssignWidths; end; procedure TdxMasterViewLevel.CancelSorting; begin if FSortingLockCount > 0 then begin Dec(FSortingLockCount); end; end; procedure TdxMasterViewLevel.Clear; begin if Count = 0 then Exit; if (FControl <> nil) and (FParent <> nil) then FControl.BeginLayout; try while Count <> 0 do Items[0].Free; finally if (FControl <> nil) and (FParent <> nil) then FControl.EndLayout; end; end; procedure TdxMasterViewLevel.ClearSorting(ExceptionalColumn: TdxMasterViewColumn); begin ChangeSorting(ExceptionalColumn, soNone, -2); end; function TdxMasterViewLevel.ColumnByFieldName(const AFieldName: string): TdxMasterViewColumn; var I: Integer; begin for I := 0 to ColumnCount - 1 do begin Result := Columns[I]; if AnsiCompareText(Result.FieldName, AFieldName) = 0 then Exit; end; Result := nil; end; function TdxMasterViewLevel.ColumnByName(const AName: string): TdxMasterViewColumn; var I: Integer; begin for I := 0 to ColumnCount - 1 do begin Result := Columns[I]; if CompareText(Result.Name, AName) = 0 then Exit; end; Result := nil; end; procedure TdxMasterViewLevel.CreateAllColumns; var PrevCursor: TCursor; I: Integer; function GetValidName(const S: string): string; const ValidChars = ['A'..'Z', 'a'..'z', '_', '0'..'9']; var I: Integer; begin Result := ''; for I := 1 to Length(S) do if S[I] in ValidChars then Result := Result + S[I]; end; begin if DataSet = nil then Exit; PrevCursor := Screen.Cursor; if FControl.ShowHourGlassCursor then Screen.Cursor := crHourglass; try FControl.BeginLayout; try BeginLayout; try with DataSet do for I := 0 to FieldCount - 1 do with CreateColumn(TdxMasterViewColumn) do begin FieldName := Fields[I].FieldName; Visible := Fields[I].Visible; try Name := Self.Name + GetValidName(FieldName); except on EComponentError do if FControl.IsDesigning then Name := dxMVDesigner.UniqueName(FControl, Self.Name + 'Column'); end; end; finally EndLayout; end; finally FControl.EndLayout; end; finally Screen.Cursor := PrevCursor; end; end; function TdxMasterViewLevel.CreateColumn(AColumnClass: TdxMasterViewColumnClass): TdxMasterViewColumn; begin Result := AColumnClass.Create(Owner); AddColumn(Result); end; procedure TdxMasterViewLevel.DestroyColumns; var LockControlLayout, LockSorting: Boolean; begin if ColumnCount = 0 then Exit; LockControlLayout := GroupColumnCount <> 0; LockSorting := SortedColumnCount <> 0; if LockControlLayout then FControl.BeginLayout; try if LockSorting then BeginSorting; try BeginLayout; try while ColumnCount <> 0 do Columns[0].Free; finally EndLayout; end; finally if LockSorting then EndSorting; end; finally if LockControlLayout then FControl.EndLayout; end; end; procedure TdxMasterViewLevel.EndAssignWidths; begin if FAssignWidthsLockCount > 0 then begin Dec(FAssignWidthsLockCount); if FAssignWidthsLockCount = 0 then WidthChanged; end; end; procedure TdxMasterViewLevel.EndDataChanging; begin if FDataChangingLockCount > 0 then begin Dec(FDataChangingLockCount); if FDataChangingLockCount = 0 then LevelChanged(True); end; end; procedure TdxMasterViewLevel.EndLayout; begin FLayout.EndUpdate; EndAssignWidths; end; procedure TdxMasterViewLevel.EndSorting; begin if FSortingLockCount > 0 then begin Dec(FSortingLockCount); if FSortingLockCount = 0 then DoSorting; end; end; procedure TdxMasterViewLevel.GetContentParams(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; ABrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont; CheckSelected: Boolean); var NewStyle: TdxMasterViewStyle; IsOdd: Boolean; begin NewStyle := nil; case Node.NodeType of ntData: begin DoGetContentStyle(Node, Column, NewStyle); if Column <> nil then with Column do if Assigned(FOnGetContentStyle) then FOnGetContentStyle(Column, Node, NewStyle); end; ntCaption: if Assigned(FOnGetCaptionStyle) then FOnGetCaptionStyle(Self, Node, nil, NewStyle); ntGroup: if Assigned(FOnGetGroupStyle) then FOnGetGroupStyle(Self, Node, nil, NewStyle); end; if not CheckSelected or not FControl.GetSelectionParams(Node, ABrush, ATextColor, ABkColor) then begin IsOdd := Odd(Node.Index) and (Node.NodeType = ntData); if ABrush <> nil then if (NewStyle <> nil) and IsOdd and (svAnotherColor in NewStyle.AssignedValues) then ABrush^ := NewStyle.AnotherBrush else if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABrush^ := NewStyle.Brush else case Node.NodeType of ntData: if Column = nil then if IsOdd then begin ABrush^ := ContentAnotherBrush; if ABrush^ = 0 then ABrush^ := ContentBrush; end else ABrush^ := ContentBrush else if IsOdd then begin ABrush^ := Column.ContentAnotherBrush; if ABrush^ = 0 then ABrush^ := Column.ContentBrush; end else ABrush^ := Column.ContentBrush; ntCaption: ABrush^ := CaptionBrush; ntGroup: ABrush^ := GroupBrush; end; if ATextColor <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then ATextColor^ := NewStyle.Font.Color else case Node.NodeType of ntData: ATextColor^ := Column.ContentFont.Color; ntCaption: ATextColor^ := CaptionFont.Color; ntGroup: ATextColor^ := GroupFont.Color; end; if ABkColor <> nil then if (NewStyle <> nil) and IsOdd and (svAnotherColor in NewStyle.AssignedValues) then ABkColor^ := NewStyle.AnotherColor else if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABkColor^ := NewStyle.Color else case Node.NodeType of ntData: if IsOdd then begin ABkColor^ := Column.ContentAnotherColor; if ABkColor^ = clNone then ABkColor^ := Column.ContentColor; end else ABkColor^ := Column.ContentColor; ntCaption: ABkColor^ := CaptionColor; ntGroup: ABkColor^ := GroupColor; end; end; if AFont <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then AFont^ := NewStyle.Font else case Node.NodeType of ntData: AFont^ := Column.ContentFont; ntCaption: AFont^ := CaptionFont; ntGroup: AFont^ := GroupFont; end; end; procedure TdxMasterViewLevel.GetFooterParams(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; ABrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont); var NewStyle: TdxMasterViewStyle; begin NewStyle := nil; if Assigned(FOnGetFooterStyle) then FOnGetFooterStyle(Self, Node, Column, NewStyle); if Column <> nil then with Column do if Assigned(FOnGetFooterStyle) then FOnGetFooterStyle(Column, Node, NewStyle); if ABrush <> nil then if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABrush^ := NewStyle.Brush else if Column = nil then ABrush^ := FooterBrush else ABrush^ := Column.FooterBrush; if ATextColor <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then ATextColor^ := NewStyle.Font.Color else ATextColor^ := Column.FooterFont.Color; if ABkColor <> nil then if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABkColor^ := NewStyle.Color else ABkColor^ := Column.FooterColor; if AFont <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then AFont^ := NewStyle.Font else AFont^ := Column.FooterFont; end; procedure TdxMasterViewLevel.GetGroupByBoxParams(Node: TdxMasterViewNode; ABrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont); var NewStyle: TdxMasterViewStyle; begin NewStyle := nil; if Assigned(FOnGetGroupByBoxStyle) then FOnGetGroupByBoxStyle(Self, Node, nil, NewStyle); if ABrush <> nil then if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABrush^ := NewStyle.Brush else ABrush^ := GroupByBoxBrush; if ATextColor <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then ATextColor^ := NewStyle.Font.Color else ATextColor^ := GroupByBoxFontColor; if ABkColor <> nil then if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABkColor^ := NewStyle.Color else ABkColor^ := GroupByBoxColor; if AFont <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then AFont^ := NewStyle.Font else AFont^ := GroupByBoxFont; end; procedure TdxMasterViewLevel.GetHeaderParams(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; ABrush, ARestSpaceBrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont); var NewStyle: TdxMasterViewStyle; begin NewStyle := nil; if Assigned(FOnGetHeaderStyle) then FOnGetHeaderStyle(Self, Node, Column, NewStyle); if Column <> nil then with Column do if Assigned(FOnGetHeaderStyle) then FOnGetHeaderStyle(Column, Node, NewStyle); if ABrush <> nil then if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABrush^ := NewStyle.Brush else ABrush^ := Column.HeaderBrush; if ARestSpaceBrush <> nil then if (NewStyle <> nil) and (svAnotherColor in NewStyle.AssignedValues) then ARestSpaceBrush^ := NewStyle.AnotherBrush else ARestSpaceBrush^ := HeaderRestSpaceBrush; if ATextColor <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then ATextColor^ := NewStyle.Font.Color else ATextColor^ := Column.HeaderFont.Color; if ABkColor <> nil then if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABkColor^ := NewStyle.Color else ABkColor^ := Column.HeaderColor; if AFont <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then AFont^ := NewStyle.Font else AFont^ := Column.HeaderFont; end; procedure TdxMasterViewLevel.GetPreviewParams(Node: TdxMasterViewNode; ABrush: PBRUSH; ATextColor, ABkColor: PColor; AFont: PFont); var NewStyle: TdxMasterViewStyle; IsOdd: Boolean; begin NewStyle := nil; DoGetContentStyle(Node, nil, NewStyle); if Assigned(FOnGetPreviewStyle) then FOnGetPreviewStyle(Self, Node, nil, NewStyle); if not FControl.GetSelectionParams(Node, ABrush, ATextColor, ABkColor) then begin IsOdd := Odd(Node.Index) and (Node.NodeType = ntData); if ABrush <> nil then if (NewStyle <> nil) and IsOdd and (svAnotherColor in NewStyle.AssignedValues) then ABrush^ := NewStyle.AnotherBrush else if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABrush^ := NewStyle.Brush else if IsOdd then begin ABrush^ := PreviewAnotherBrush; if ABrush^ = 0 then ABrush^ := PreviewBrush; end else ABrush^ := PreviewBrush; if ATextColor <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then ATextColor^ := NewStyle.Font.Color else ATextColor^ := PreviewFontColor; if ABkColor <> nil then if (NewStyle <> nil) and IsOdd and (svAnotherColor in NewStyle.AssignedValues) then ABkColor^ := NewStyle.AnotherColor else if (NewStyle <> nil) and (svColor in NewStyle.AssignedValues) then ABkColor^ := NewStyle.Color else if IsOdd then begin ABkColor^ := PreviewAnotherColor; if ABkColor^ = clNone then ABkColor^ := PreviewColor; end else ABkColor^ := PreviewColor; end; if AFont <> nil then if (NewStyle <> nil) and (svFont in NewStyle.AssignedValues) then AFont^ := NewStyle.Font else AFont^ := PreviewFont; end; { TdxMasterViewList } constructor TdxMasterViewList.Create(AOwner: TdxMasterViewNode; AMode: TdxMasterViewListMode); begin FOwner := AOwner; FMode := AMode; end; destructor TdxMasterViewList.Destroy; begin Clear; inherited; end; function TdxMasterViewList.Get(Index: Integer): TdxMasterViewNode; begin if (Index < 0) or (Index >= FCount) then Error(SListIndexError, Index); Result := FList^[Index]; end; procedure TdxMasterViewList.Put(Index: Integer; Item: TdxMasterViewNode); begin if (Index < 0) or (Index >= FCount) then Error(SListIndexError, Index); FList^[Index] := Item; end; procedure TdxMasterViewList.SetCapacity(NewCapacity: Integer); begin if (NewCapacity < FCount) or (NewCapacity > MaxListSize) then Error(SListCapacityError, NewCapacity); if NewCapacity <> FCapacity then begin ReallocMem(FList, NewCapacity * SizeOf(Pointer)); FCapacity := NewCapacity; end; end; procedure TdxMasterViewList.SetCount(NewCount: Integer); var I: Integer; begin if (NewCount < 0) or (NewCount > MaxListSize) then Error(SListCountError, NewCount); if NewCount > FCapacity then SetCapacity(NewCount); if NewCount > FCount then FillChar(FList^[FCount], (NewCount - FCount) * SizeOf(Pointer), 0) else for I := FCount - 1 downto NewCount do Delete(I); FCount := NewCount; end; procedure TdxMasterViewList.Grow; var Delta: Integer; begin if FCapacity > 64 then Delta := FCapacity div 4 else if FCapacity > 8 then Delta := 16 else Delta := 4; SetCapacity(FCapacity + Delta); end; function TdxMasterViewList.Add(Item: TdxMasterViewNode): Integer; begin Result := FCount; if Result = FCapacity then Grow; FList^[Result] := Item; case FMode of lmItems: Item.FIndex := Result; lmAbsoluteItems: Item.FAbsoluteIndex := Result; lmSelectedItems: Item.FSelectedIndex := Result; end; Inc(FCount); end; procedure TdxMasterViewList.BeginDeletion; begin if FMode = lmSelectedItems then FOwner.Control.BeginSelection; if FDeletionLockCount = 0 then FNewCount := FCount; Inc(FDeletionLockCount); end; procedure TdxMasterViewList.BeginInsertion; begin if FInsertionLockCount = 0 then FPrevCount := FCount; Inc(FInsertionLockCount); end; procedure TdxMasterViewList.Clear; begin SetCount(0); SetCapacity(0); end; procedure TdxMasterViewList.CopyTo(List: TList); begin List.Count := FCount; System.Move(FList^, List.List^, FCount * SizeOf(Pointer)); end; procedure TdxMasterViewList.Delete(Index: Integer); var I: Integer; begin if (Index < 0) or (Index >= FCount) then Error(SListIndexError, Index); if FList^[Index] = nil then Exit; case FMode of lmItems: FList^[Index].FIndex := -1; lmAbsoluteItems: FList^[Index].FAbsoluteIndex := -1; lmSelectedItems: FList^[Index].FSelectedIndex := -1; end; FList^[Index] := nil; if FDeletionLockCount = 0 then begin Dec(FCount); if Index < FCount then begin System.Move(FList^[Index + 1], FList^[Index], (FCount - Index) * SizeOf(Pointer)); for I := Index to FCount - 1 do case FMode of lmItems: Dec(FList^[I].FIndex); lmAbsoluteItems: Dec(FList^[I].FAbsoluteIndex); lmSelectedItems: Dec(FList^[I].FSelectedIndex); end; end; end else Dec(FNewCount); end; procedure TdxMasterViewList.EndDeletion; var I, J: Integer; NewList: PdxMasterViewNodeList; begin if FDeletionLockCount <> 0 then begin Dec(FDeletionLockCount); if (FDeletionLockCount = 0) and (FCount <> FNewCount) then begin if FNewCount = 0 then NewList := nil else begin GetMem(NewList, FNewCount * SizeOf(Pointer)); J := 0; for I := 0 to FCount - 1 do if FList^[I] <> nil then begin NewList^[J] := FList^[I]; case FMode of lmItems: NewList^[J].FIndex := J; lmAbsoluteItems: NewList^[J].FAbsoluteIndex := J; lmSelectedItems: NewList^[J].FSelectedIndex := J; end; Inc(J); end; end; if FList <> nil then FreeMem(FList); FCapacity := FNewCount; FCount := FNewCount; FList := NewList; if (FCount = 0) and (FMode = lmItems) then FOwner.CheckExpanded; end; end; if FMode = lmSelectedItems then FOwner.Control.EndSelection; end; procedure TdxMasterViewList.EndInsertion; var I: Integer; begin if FInsertionLockCount <> 0 then begin Dec(FInsertionLockCount); if (FInsertionLockCount = 0) and (FCount <> FPrevCount) then for I := 0 to FCount - 1 do case FMode of lmItems: FList^[I].FIndex := I; lmAbsoluteItems: FList^[I].FAbsoluteIndex := I; lmSelectedItems: FList^[I].FSelectedIndex := I; end; end; end; class procedure TdxMasterViewList.Error(const Msg: string; Data: Integer); function ReturnAddr: Pointer; asm MOV EAX,[EBP+4] end; begin raise EListError.CreateFmt(Msg, [Data]) at ReturnAddr; end; function TdxMasterViewList.First: TdxMasterViewNode; begin Result := Get(0); end; function TdxMasterViewList.IndexOf(Item: TdxMasterViewNode): Integer; begin case FMode of lmItems: Result := Item.FIndex; lmAbsoluteItems: Result := Item.FAbsoluteIndex; lmSelectedItems: Result := Item.FSelectedIndex; else Result := -1; end; end; procedure TdxMasterViewList.Insert(Index: Integer; Item: TdxMasterViewNode); var I: Integer; begin if (Index < 0) or (Index > FCount) then Error(SListIndexError, Index); if FCount = FCapacity then Grow; if Index < FCount then begin if FInsertionLockCount = 0 then for I := Index to FCount - 1 do case FMode of lmItems: Inc(FList^[I].FIndex); lmAbsoluteItems: Inc(FList^[I].FAbsoluteIndex); lmSelectedItems: Inc(FList^[I].FSelectedIndex); end; System.Move(FList^[Index], FList^[Index + 1], (FCount - Index) * SizeOf(Pointer)); end; FList^[Index] := Item; case FMode of lmItems: Item.FIndex := Index; lmAbsoluteItems: Item.FAbsoluteIndex := Index; lmSelectedItems: Item.FSelectedIndex := Index; end; Inc(FCount); end; function TdxMasterViewList.Last: TdxMasterViewNode; begin Result := Get(FCount - 1); end; procedure TdxMasterViewList.Move(CurIndex, NewIndex: Integer); var Item: Pointer; begin if CurIndex <> NewIndex then begin if (NewIndex < 0) or (NewIndex >= FCount) then Error(SListIndexError, NewIndex); Item := Get(CurIndex); Delete(CurIndex); Insert(NewIndex, Item); end; end; function TdxMasterViewList.Remove(Item: TdxMasterViewNode): Integer; begin Result := IndexOf(Item); if Result <> -1 then Delete(Result); end; procedure QuickSort(SortList: PdxMasterViewNodeList; L, R: Integer; SCompare: TdxMasterViewListSortCompare); var I, J: Integer; P, T: Pointer; begin repeat I := L; J := R; P := SortList^[(L + R) shr 1]; repeat while SCompare(SortList^[I], P) < 0 do Inc(I); while SCompare(SortList^[J], P) > 0 do Dec(J); if I <= J then begin T := SortList^[I]; SortList^[I] := SortList^[J]; SortList^[J] := T; Inc(I); Dec(J); end; until I > J; if L < J then QuickSort(SortList, L, J, SCompare); L := I; until I >= R; end; procedure TdxMasterViewList.Sort(Compare: TdxMasterViewListSortCompare; TopBound, BottomBound: Integer); var I: Integer; begin if (FList <> nil) and (FCount > 0) then begin QuickSort(FList, TopBound, BottomBound, Compare); for I := TopBound to BottomBound do with FList^[I] do case FMode of lmItems: FIndex := I; lmAbsoluteItems: FAbsoluteIndex := I; lmSelectedItems: FSelectedIndex := I; end; end; end; { TdxMasterViewNode } constructor TdxMasterViewNode.Create(AControl: TdxMasterView; ALevel: TdxMasterViewLevel; AParentNode: TdxMasterViewNode; AIndex: Integer; ANodeType: TdxMasterViewNodeType; const AID, AKeyValue: Variant); var I: Integer; PValue: PVariant; begin inherited Create; FControl := AControl; FLevel := ALevel; FNodeType := ANodeType; FID := AID; FKeyValue := AKeyValue; FAbsoluteIndex := -1; FGroupIndex := -1; FIndex := -1; FItems := TdxMasterViewList.Create(Self, lmItems); FSelectedIndex := -1; if FNodeType = ntData then begin FValues := TStringList.Create; if FLevel <> nil then begin FValues.Capacity := FLevel.ColumnCount; for I := 0 to FValues.Capacity - 1 do begin New(PValue); FValues.AddObject('', TObject(PValue)); end; end; end; if AParentNode <> nil then AParentNode.AddItem(AIndex, Self); end; destructor TdxMasterViewNode.Destroy; var I: Integer; begin FLevel.DoNodeDeleted(Self); Clear; if FParentNode <> nil then with FParentNode do begin Invalidate(nil, vpAllAndBelow); RemoveItem(Self); end; FControl.DoNodeDeleted(Self); DestroySummaries; if FNodeType = ntData then begin for I := 0 to FValues.Count - 1 do Dispose(PVariant(FValues.Objects[I])); FValues.Free; end; FItems.Free; inherited; end; function TdxMasterViewNode.GetBounds: TRect; begin Result := FViewInfo.Bounds; end; function TdxMasterViewNode.GetContentBounds: TRect; begin with FViewInfo, Result do begin Result := Bounds; if Top <> Bottom then begin Inc(Top, GroupByBoxSize); if (HeaderSize <> 0) and FLevel.Horizontal then Inc(Top, HeaderSize); Dec(Bottom, FooterSize); if Top > Bottom then Top := Bottom; end; end; end; function TdxMasterViewNode.GetContentBoundsWithoutPreview: TRect; begin with Result do begin Result := ContentBounds; if IsRectEmpty(Result) then Exit; with FLevel do Bottom := Top + VisibleRowCount * ContentRealHeight; end; end; function TdxMasterViewNode.GetCount: Integer; begin Result := FItems.Count; end; function TdxMasterViewNode.GetCountInLevel(ALevel: TdxMasterViewLevel): Integer; var Node: TdxMasterViewNode; I: Integer; begin if (FNodeType = ntData) and (FLevel.Count = 1) and (ALevel = FLevel[0]) or (FNodeType in [ntCaption, ntGroup]) and (ALevel = FLevel) then Result := Count else begin Result := 0; Node := FirstInLevel[ALevel]; if Node <> nil then begin Result := Node.Index; for I := Result to Count - 1 do if Items[I].Level <> ALevel then begin Result := I - Result; Exit; end; Result := Count - Result; end; end; end; function TdxMasterViewNode.GetFirst: TdxMasterViewNode; begin if Count = 0 then Result := nil else Result := Items[0]; end; function TdxMasterViewNode.GetFirstInLevel(ALevel: TdxMasterViewLevel): TdxMasterViewNode; var I: Integer; begin if (FNodeType = ntData) and (FLevel.Count = 1) and (ALevel = FLevel[0]) or (FNodeType in [ntCaption, ntGroup]) and (ALevel = FLevel) then Result := First else begin for I := 0 to Count - 1 do begin Result := Items[I]; if Result.Level = ALevel then Exit; end; Result := nil; end; end; function TdxMasterViewNode.GetFocused: Boolean; begin Result := FControl.FocusedNode = Self; end; function TdxMasterViewNode.GetFooterBounds: TRect; begin with FViewInfo do if FooterSize = 0 then SetRectEmpty(Result) else begin Result := Bounds; Result.Top := Result.Bottom - FooterSize; end; end; function TdxMasterViewNode.GetFullBounds: TRect; begin Result := FViewInfo.Bounds; with Result do begin Left := 0; Right := FControl.ClientWidth; end; end; function TdxMasterViewNode.GetFullContentBounds: TRect; begin Result := GetContentBounds; with Result do if Top <> Bottom then begin Left := 0; Right := FControl.ClientWidth; end; end; function TdxMasterViewNode.GetFullFooterBounds: TRect; begin Result := GetFooterBounds; with Result do if Top <> Bottom then begin Left := 0; Right := FControl.ClientWidth; end; end; function TdxMasterViewNode.GetFullGroupByBoxBounds: TRect; begin Result := GetGroupByBoxBounds; with Result do if Top <> Bottom then begin //Left := 0; Right := FControl.ClientWidth; end; end; function TdxMasterViewNode.GetFullHeaderBounds: TRect; begin Result := GetHeaderBounds; with Result do if Top <> Bottom then begin Left := 0; Right := FControl.ClientWidth; end; end; function TdxMasterViewNode.GetGroupByBoxBounds: TRect; begin with FViewInfo do if GroupByBoxSize = 0 then SetRectEmpty(Result) else begin Result := Bounds; if FNodeType = ntData then Dec(Result.Left, FLevel.GroupColumnCount * (LevelIndent + FLevel.ExtLineWidth)) else Dec(Result.Left, GroupIndex * (LevelIndent + FLevel.ExtLineWidth)); Result.Bottom := Result.Top + GroupByBoxSize; end; end; function TdxMasterViewNode.GetGroupByBoxColumnBounds(Index: Integer): TRect; var I: Integer; R: TRect; begin if Index = -1 then Index := 0; Result := GroupByBoxBounds; R := Result; with Result do begin Inc(Left, GroupByBoxLeftOffset); Inc(Top, GroupByBoxTopOffset); for I := 0 to Index do begin Right := Left + GroupByBoxColumnWidth;//FLevel.GroupColumns[I].GetHeaderBestFitWidth(DC, True) + 40; Bottom := Top + FLevel.HeaderRealHeight; if I = Index then Break; Left := Right + GroupByBoxHorOffset; Inc(Top, FLevel.HeaderRealHeight div 2 + GroupByBoxVerOffset); end; end; end; function TdxMasterViewNode.GetGroupIndex: Integer; var Node: TdxMasterViewNode; begin Result := -1; Node := Self; while Node.NodeType = ntGroup do begin Inc(Result); Node := Node.ParentNode; end; end; function TdxMasterViewNode.GetHasChildren: Boolean; begin Result := Count <> 0; end; function TdxMasterViewNode.GetHeaderBounds: TRect; begin with FViewInfo do if HeaderSize = 0 then SetRectEmpty(Result) else begin Result := Bounds; Inc(Result.Top, GroupByBoxSize); Result.Bottom := Result.Top + HeaderSize; if Result.Bottom > Bounds.Bottom - FooterSize then Result.Bottom := Bounds.Bottom - FooterSize; end; end; function TdxMasterViewNode.GetHeaderColBounds(Index: Integer): TRect; var I: Integer; begin if FViewInfo.HeaderSize = 0 then SetRectEmpty(Result) else with Result do begin Result := HeaderBounds; if FLevel.HasExpandButton then Inc(Left, LevelIndent); Right := Left; for I := 0 to Index do begin if I <> 0 then Inc(Right, FLevel.Layout.ContentWidths[I - 1]); Left := Right; Inc(Right, FLevel.Layout.HeaderWidths[I]); end; end; end; function TdxMasterViewNode.GetHeaderRowBounds(Index: Integer): TRect; var I: Integer; begin if FViewInfo.HeaderSize = 0 then Exit; Result := HeaderBounds; Result.Bottom := Result.Top + FLevel.HeaderRealHeight; for I := 0 to Index - 1 do OffsetRect(Result, 0, FLevel.HeaderRealHeight); end; function TdxMasterViewNode.GetHorizontal: Boolean; begin Result := FLevel.Horizontal or (FNodeType <> ntData); end; function TdxMasterViewNode.GetIsEditing: Boolean; begin Result := FLevel.FEditingNode = Self; end; function TdxMasterViewNode.GetIsFirst: Boolean; begin Result := Index = 0; end; function TdxMasterViewNode.GetIsFirstInLevel: Boolean; begin Result := (Index = 0) or (FParentNode.FItems.List^[Index - 1].Level <> FLevel); end; function TdxMasterViewNode.GetIsLast: Boolean; begin Result := Index = FParentNode.FItems.Count - 1; end; function TdxMasterViewNode.GetIsLastInLevel: Boolean; begin Result := IsLast or (FParentNode.FItems.List^[Index + 1].Level <> FLevel); end; function TdxMasterViewNode.GetItem(Index: Integer): TdxMasterViewNode; begin Result := FItems[Index]; end; function TdxMasterViewNode.GetLast: TdxMasterViewNode; begin if Count = 0 then Result := nil else Result := Items[Count - 1]; end; function TdxMasterViewNode.GetLastInLevel(ALevel: TdxMasterViewLevel): TdxMasterViewNode; var I: Integer; begin for I := Count - 1 downto 0 do begin Result := Items[I]; if Result.Level = ALevel then Exit; end; Result := nil; end; function TdxMasterViewNode.GetLeftSpaceBounds: TRect; begin Result := Bounds; with Result do begin Right := Left; Left := 0; end; end; function TdxMasterViewNode.GetNodeWithSummaryData(ALevel: TdxMasterViewLevel): TdxMasterViewNode; begin Result := GetParentNode(ALevel, False).ParentNode; end; function TdxMasterViewNode.GetParentNodes(Index: Integer): TdxMasterViewNode; var I: Integer; begin Result := Self; for I := 0 to Index - 1 do begin Result := Result.ParentNode; if Result = nil then Break; end; end; function TdxMasterViewNode.GetParentNodeWithData: TdxMasterViewNode; begin Result := FParentNode; while Result.NodeType <> ntData do Result := Result.ParentNode; end; function TdxMasterViewNode.GetPreviewBounds: TRect; begin if FViewInfo.PreviewSize = 0 then SetRectEmpty(Result) else with Result do begin Result := ContentBounds; if HasExpandButton then Inc(Left, LevelIndent); with FLevel do Inc(Top, VisibleRowCount * ContentRealHeight); Bottom := Top + FViewInfo.PreviewSize; end; end; function TdxMasterViewNode.GetRightSpaceBounds: TRect; begin Result := Bounds; with Result do if Top <> Bottom then begin Left := Right; Right := FControl.ClientWidth; end; end; function TdxMasterViewNode.GetSelected: Boolean; begin Result := not FControl.MultiSelect and Focused or FControl.MultiSelect and ((SelectedIndex <> -1) or InSelectedPeriod); end; function TdxMasterViewNode.GetString(Index: Integer): string; begin Result := FValues[Index]; end; function TdxMasterViewNode.GetSubFooterBounds(AParentIndex: Integer): TRect; var I: Integer; begin if (FViewInfo.FooterSize = 0) or not HasFooter(AParentIndex) then SetRectEmpty(Result) else begin Result := GetFooterBounds; Result.Bottom := Result.Top; for I := 0 to FLevel.ParentCount - 2 do with Result do begin with FLevel.Parents[I] do begin if Self.HasFooter(I) then Inc(Bottom, RowCount * FooterHeight); if I = AParentIndex then Break; if (I <> 0) or (FNodeType = ntData) then Dec(Left, (Byte(ShowCaption) + GroupColumnCount) * (LevelIndent + ExtLineWidth)) else if FNodeType = ntGroup then Dec(Left, (Byte(ShowCaption) + GroupIndex) * (LevelIndent + ExtLineWidth)); end; with FLevel.Parents[I + 1] do begin Dec(Left, LevelIndent + ExtLineWidth + LevelSeparatorWidth); Right := Left + VisibleWidth; if (LevelSeparatorWidth <> 0) and HasLevelSeparator(I) then Inc(Bottom, LevelSeparatorWidth); if (RowSeparatorWidth <> 0) and HasRowSeparator(I) then Inc(Bottom, RowSeparatorWidth); Top := Bottom; end; end; end; end; function TdxMasterViewNode.GetValue(Index: Integer): Variant; begin Result := PVariant(FValues.Objects[Index])^; end; function TdxMasterViewNode.GetValueCount: Integer; begin if FValues <> nil then Result := FValues.Count else Result := 0; end; function TdxMasterViewNode.GetVisible: Boolean; begin Result := (0 <= AbsoluteIndex) and (AbsoluteIndex < FControl.TopIndex + FControl.FLastPartVisibleItemCount); end; procedure TdxMasterViewNode.SetExpanded(Value: Boolean); begin if FExpanded <> Value then if Value then Expand(False) else Collapse(False); end; procedure TdxMasterViewNode.SetFocused(Value: Boolean); begin Control.FocusedIndex := AbsoluteIndex; end; procedure TdxMasterViewNode.SetSelected(Value: Boolean); begin if FControl.MultiSelect and (Selected <> Value) and (not Value or FControl.DoSelectNode(Self)) or not FControl.MultiSelect and not Value and (SelectedIndex <> -1) then begin if Value then FControl.AddSelectedItem(Self) else FControl.RemoveSelectedItem(Self); NodeChanged(False); FControl.DoSelectionChanged; end; end; procedure TdxMasterViewNode.AddItem(AIndex: Integer; Value: TdxMasterViewNode); begin if AIndex = -1 then AIndex := GetAvailIndex(Value.Level.Index); FItems.Insert(AIndex, Value); with Value do begin FParentNode := Self; FGroupIndex := GetGroupIndex; end; end; procedure TdxMasterViewNode.RemoveItem(Value: TdxMasterViewNode); begin FItems.Remove(Value); CheckExpanded; //FControl.DoNodeDeleted(Value); with Value do begin FParentNode := nil; FGroupIndex := -1; end; end; procedure TdxMasterViewNode.SetHasLevelSeparator(var EndLevelData: Integer; ALevelParentIndex: Integer); begin EndLevelData := EndLevelData or (1 shl ALevelParentIndex); end; procedure TdxMasterViewNode.SetHasRowSeparator(var EndLevelData: Integer; ALevelParentIndex: Integer); begin EndLevelData := EndLevelData or (1 shl (ALevelParentIndex + 16)); end; function TdxMasterViewNode.Add(ALevel: TdxMasterViewLevel; AIndex: Integer; ANodeType: TdxMasterViewNodeType; const AID, AKeyValue: Variant): TdxMasterViewNode; begin Result := TdxMasterViewNode.Create(FControl, ALevel, Self, AIndex, ANodeType, AID, AKeyValue); end; procedure TdxMasterViewNode.AddToList(ALevel: TdxMasterViewLevel; OtherNodesList, GroupNodesList: TList; ProcessRoot: Boolean); var I: Integer; Node: TdxMasterViewNode; begin if ProcessRoot then if FNodeType <> ntGroup then begin OtherNodesList.Add(Self); Exit; end else GroupNodesList.Add(Self); Node := FirstInLevel[ALevel]; if Node = nil then Exit; I := Node.Index; for I := I to I + CountInLevel[ALevel] - 1 do Items[I].AddToList(ALevel, OtherNodesList, GroupNodesList, True); end; procedure TdxMasterViewNode.AddVisibleToList(List: TdxMasterViewList); var I: Integer; begin if FParentNode <> nil then begin List.Add(Self); FLevel.FVisible := True; // := FNodeType = ntData ? end; if Expanded then for I := 0 to Count - 1 do Items[I].AddVisibleToList(List); end; function TdxMasterViewNode.CanDelete: Boolean; begin Result := (FNodeType = ntData) and FLevel.CanDelete; end; function TdxMasterViewNode.CanExpand: Boolean; begin Result := HasChildren; if not Result then case FNodeType of ntData: Result := FLevel.LoadDataOnExpand; ntCaption: Result := FLevel.SmartLoad; end; end; procedure TdxMasterViewNode.ChangeExpanded(Value: Boolean); begin if FExpanded <> Value then begin FExpanded := Value; if Value then FTriedToExpand := True; end; end; procedure TdxMasterViewNode.CheckChildNodes(ALevel: TdxMasterViewLevel; NeedCheckParentNodes: Boolean); begin if (FNodeType = ntGroup) and (Count = 0) then CheckParentNodes else if FirstInLevel[ALevel] <> nil then FirstInLevel[ALevel].UpdateData(False, False, True); if NeedCheckParentNodes and (FNodeType = ntGroup) then ParentNodes[GroupIndex + 1].CheckChildNodes(FLevel, False); end; procedure TdxMasterViewNode.CheckExpanded; begin if (FParentNode <> nil) and not HasChildren then ChangeExpanded(False); end; procedure TdxMasterViewNode.CheckParentNodes; var DetailKeyValue: Variant; BaseNode, AParentNode: TdxMasterViewNode; ALevel: TdxMasterViewLevel; begin case FNodeType of ntData: begin if not FControl.SyncMove then Exit; // find base data node if FLevel.IsTop then BaseNode := FControl.FItems else begin DetailKeyValue := FLevel.GetCurDetailKeyValue; if VarAreEqual(ParentNodeWithData.KeyValue[FLevel.Index], DetailKeyValue) then BaseNode := ParentNodeWithData else BaseNode := FControl.NodeFromKeyValue(FLevel.Parent, FLevel.Index, DetailKeyValue); end; // find parentnode AParentNode := FLevel.GetParentNode(BaseNode, nil, nil, nil, nil, True); if AParentNode = nil then begin AParentNode := FParentNode; ALevel := FLevel; Free; AParentNode.CheckChildNodes(ALevel, False); end else if FParentNode <> AParentNode then begin with FParentNode do begin RemoveItem(Self); CheckChildNodes(Self.Level, False); end; with AParentNode do begin AddItem(-1, Self); CheckChildNodes(Self.Level, True); end; end; end; ntGroup: if Count = 0 then begin AParentNode := Self; repeat BaseNode := AParentNode; AParentNode := AParentNode.ParentNode; BaseNode.Free; until (AParentNode.NodeType <> ntGroup) or (AParentNode.Count <> 0); AParentNode.Invalidate(nil, vpAllAndBelow); end; end; end; procedure TdxMasterViewNode.Clear; var I: Integer; begin FControl.BeginSelection; try for I := Count - 1 downto 0 do Items[I].Free; finally FControl.EndSelection; end; end; procedure TdxMasterViewNode.ClearValues; var I: Integer; begin if FNodeType = ntData then begin FID := Unassigned; FKeyValue := Unassigned; for I := 0 to Level.ColumnCount - 1 do begin FValues[I] := ''; PVariant(FValues.Objects[I])^ := Unassigned; end; end; end; function TdxMasterViewNode.ContainsDataInChildren(ALevel: TdxMasterViewLevel): Boolean; begin Result := (FNodeType = ntData) and (FLevel = ALevel.Parent) and not ALevel.ShowCaption or (FNodeType in [ntCaption, ntGroup]) and (FLevel = ALevel); end; procedure TdxMasterViewNode.DoSmartLoadChanged(ALevel: TdxMasterViewLevel); var I: Integer; begin if not ALevel.SmartLoad then if HasDataInChildren(ALevel) and not FExpanded and (CountInLevel[ALevel] = 0) then UpdateData(False, True, False) else if ALevel.HasAsParent(FLevel) then for I := 0 to Count - 1 do Items[I].DoSmartLoadChanged(ALevel); end; function CompareItems(Item1, Item2: TdxMasterViewNode): Integer; var I: Integer; S: string; // to avoid the bug with optimization begin S := ''; with Item1.ParentNode, FControl do for I := 0 to FSortingColumnCount - 1 do if FSortingData^[I] <> nil then begin SortV1 := FSortingData^[I]^[Item1.Index]; SortV2 := FSortingData^[I]^[Item2.Index]; //if VarIsArray(SortV1^) or VarIsArray(SortV2^) then Continue; //20..30 ms try if SortV1^ <> SortV2^ then begin if VarIsNull(SortV1^) then Result := -1 else if VarIsNull(SortV2^) then Result := 1 else if (mobAnsiCompareStrOnSorting in OptionsBehavior) and (VarType(SortV1^) = varString) then begin SortS1 := SortV1^; SortS2 := SortV2^; Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(SortS1), Length(SortS1), PChar(SortS2), Length(SortS2)) - 2; end else if SortV1^ < SortV2^ then Result := -1 else Result := 1; if FSortingDesc^[I] then Result := -Result; Exit; end; except on EVariantError do ; // 10..20 ms end; end; Result := Item1.Index - Item2.Index; end; procedure TdxMasterViewNode.DoSorting(ALevel: TdxMasterViewLevel); var I, J, CI, TopBound, BottomBound, AGroupIndex: Integer; UseUpperCase: PBoolArray; IsDataNodes: Boolean; // ft,lt:integer; begin if ContainsDataInChildren(ALevel) then begin if FItems.Count = 0 then Exit; //ft:=gettickcount; AGroupIndex := GroupIndex; IsDataNodes := (FNodeType in [ntData, ntCaption]) and (ALevel.GroupColumnCount = 0) or (FNodeType = ntGroup) and (AGroupIndex = ALevel.GroupColumnCount - 1); with FControl, Self do begin TopBound := -1; BottomBound := -2; FSortingColumnCount := ALevel.SortedColumnCount; GetMem(UseUpperCase, FSortingColumnCount * SizeOf(Boolean)); GetMem(FSortingDesc, FSortingColumnCount * SizeOf(Boolean)); GetMem(FSortingData, FSortingColumnCount * SizeOf(PdxMVSortingValues)); try for I := 0 to FSortingColumnCount - 1 do if IsDataNodes or (ALevel.SortedColumns[I] = ALevel.GroupColumns[AGroupIndex + 1]) then begin with ALevel.SortedColumns[I] do begin CI := Index; FSortingDesc^[I] := SortOrder = soDescending; end; GetMem(FSortingData^[I], FItems.Count * SizeOf(PVariant)); TopBound := -1; BottomBound := FItems.Count - 1; for J := 0 to BottomBound do if FItems[J].Level = ALevel then begin if TopBound = -1 then begin TopBound := J; UseUpperCase[I] := (mobCaseInsensitiveSorting in OptionsBehavior) and (IsDataNodes and (VarType(PVariant(FItems[J].FValues.Objects[CI])^) = varString) or not IsDataNodes and (VarType(FItems[J].GroupValue) = varString)); end; if UseUpperCase[I] then begin New(FSortingData^[I]^[J]); if IsDataNodes then FSortingData^[I]^[J]^ := VarAnsiUpperCase(PVariant(FItems[J].FValues.Objects[CI])^) else FSortingData^[I]^[J]^ := VarAnsiUpperCase(FItems[J].GroupValue); end else if IsDataNodes then FSortingData^[I]^[J] := PVariant(FItems[J].FValues.Objects[CI]) else FSortingData^[I]^[J] := @FItems[J].GroupValue; end else if TopBound <> -1 then begin BottomBound := J - 1; Break; end; end else FSortingData^[I] := nil; if TopBound <> -1 then FItems.Sort(CompareItems, TopBound, BottomBound); finally for I := 0 to FSortingColumnCount - 1 do if FSortingData^[I] <> nil then begin if (TopBound <> -1) and UseUpperCase[I] then for J := TopBound to BottomBound do Dispose(FSortingData^[I]^[J]); FreeMem(FSortingData^[I], FItems.Count * SizeOf(PVariant)); end; FreeMem(FSortingData, FSortingColumnCount * SizeOf(PdxMVSortingValues)); FreeMem(FSortingDesc, FSortingColumnCount * SizeOf(Boolean)); FreeMem(UseUpperCase, FSortingColumnCount * SizeOf(Boolean)); end; end; //lt:=gettickcount; //application.messagebox(pchar(inttostr(lt-ft)), '', 0); if IsDataNodes then Exit; end; if ALevel.HasAsParent(FLevel) then for I := 0 to Count - 1 do Items[I].DoSorting(ALevel); end; procedure TdxMasterViewNode.Draw(DC: HDC); begin if RectVisible(DC, LeftSpaceBounds) then FLevel.DrawIndent(DC, LeftSpaceBounds, Self); if (FViewInfo.FooterSize <> 0) and RectVisible(DC, FullFooterBounds) then begin FLevel.DrawFooter(DC, FooterBounds, Self); if vioClipped in FViewInfo.Options then with FullFooterBounds do ExcludeClipRect(DC, Left, Top, Right, Bottom); end; if (FViewInfo.GroupByBoxSize <> 0) and RectVisible(DC, FullGroupByBoxBounds) then FLevel.DrawGroupByBox(DC, GroupByBoxBounds, Self); if (FViewInfo.HeaderSize <> 0) and RectVisible(DC, FullHeaderBounds) then FLevel.DrawHeader(DC, HeaderBounds, Self); if RectVisible(DC, FullContentBounds) then FLevel.DrawContent(DC, ContentBounds, Self); end; function TdxMasterViewNode.GetAvailIndex(AChildLevelIndex: Integer): Integer; begin Result := Count; if Result <> 0 then for Result := 0 to Result - 1 do if Items[Result].Level.Index > AChildLevelIndex then Break; end; function TdxMasterViewNode.GetLastSubChild: TdxMasterViewNode; begin if Count = 0 then Result := nil else begin Result := Items[Count - 1]; while Result.Expanded do Result := Result[Result.Count - 1]; end; end; function TdxMasterViewNode.GetLevelIndex: Integer; var Node: TdxMasterViewNode; begin Result := -2; Node := Self; repeat Inc(Result); Node := Node.ParentNode; until Node = nil; end; function TdxMasterViewNode.HasDataInChildren(ALevel: TdxMasterViewLevel): Boolean; begin Result := (FNodeType = ntData) and (FLevel = ALevel.Parent) or (FNodeType = ntCaption) and (FLevel = ALevel); end; function TdxMasterViewNode.InSelectedPeriod: Boolean; begin Result := (mvsSelectPeriod in FControl.State) and FControl.DoSelectNode(Self) and IsBetween(Self, FControl.FSelectionAnchor, FControl.FLastSelectedNode); end; procedure TdxMasterViewNode.Invalidate(Column: TdxMasterViewColumn; VPart: TdxMasterViewVPart); var R: TRect; begin if FControl.HandleAllocated and (FControl.FUpdateLockCount = 0) then begin //!!!! if (FControl.TopIndex <= AbsoluteIndex) and (AbsoluteIndex < FControl.TopIndex + FControl.FLastPartVisibleItemCount) then case VPart of vpGroupByBox: if HasGroupByBox(R.Left) then if Column = nil then R := GroupByBoxBounds else R := Column.GetGroupByBoxBounds(Self) else Exit; vpHeader: if HasHeader(FControl.TopIndex, R.Left) then if Column = nil then R := FullHeaderBounds else R := Column.GetHeaderBounds(Self) else Exit; vpContent: if (Column = nil) or (FNodeType = ntGroup) and (Column = FLevel.GroupColumns[FGroupIndex]) then R := FullContentBounds else R := Column.GetContentBounds(Self); vpFooter: if Column = nil then R := FullFooterBounds else R := Column.GetFooterBounds(Self); vpAll, vpAllAndBelow: if Column = nil then begin R := FullBounds; if VPart = vpAllAndBelow then R.Bottom := FControl.ClientHeight; end else begin Invalidate(Column, vpGroupByBox); Invalidate(Column, vpHeader); Invalidate(Column, vpContent); Invalidate(Column, vpFooter); Exit; end; end else if VPart = vpAllAndBelow then R := FControl.ClientRect else Exit; InvalidateRect(FControl.Handle, @R, False); end; end; procedure TdxMasterViewNode.MoveData(ALevel: TdxMasterViewLevel; FromIndex, ToIndex: Integer); var PValue: PVariant; I: Integer; begin if (FNodeType = ntData) and (FLevel = ALevel) then if FromIndex = -1 then begin New(PValue); FValues.InsertObject(ToIndex, '', TObject(PValue)); end else if ToIndex = -1 then begin Dispose(PVariant(FValues.Objects[FromIndex])); FValues.Delete(FromIndex); end else FValues.Move(FromIndex, ToIndex) else if ALevel.HasAsParent(FLevel) then for I := 0 to Count - 1 do Items[I].MoveData(ALevel, FromIndex, ToIndex); end; procedure TdxMasterViewNode.NodeChanged(HardRefresh: Boolean); begin if Control <> nil then if HardRefresh then else Invalidate(nil, vpContent); end; procedure TdxMasterViewNode.Sync; var Node: TdxMasterViewNode; AFreezeCount: Integer; begin if FNodeType = ntData then begin Node := Self; repeat if Node.NodeType = ntData then with Node.Level do if not VarAreEqual(GetCurIDValue, Node.ID) then begin if DataSet.State in dsEditModes then DataSet.Post; AFreezeCount := FControl.FreezeDataSet(Node.Level); try DataSet.Locate(ID, Node.ID, []); finally if DataLink.FreezeCount = AFreezeCount then FControl.UnfreezeDataSet(Node.Level); end; end; Node := Node.ParentNode; until Node.ParentNode = nil; end; end; procedure TdxMasterViewNode.SyncPos; begin if not FControl.Frozen and FControl.SyncMove then Sync; end; function CompareItemsByID(Node1, Node2: Pointer): Integer; begin Result := -VarCompare(TdxMasterViewNode(Node1).ID, TdxMasterViewNode(Node2).ID); end; function CompareGroupItemsByPtr(Node1, Node2: Pointer): Integer; begin Result := Integer(Node1) - Integer(Node2); end; function CompareGroupItemsByGroupIndex(Node1, Node2: Pointer): Integer; begin if TdxMasterViewNode(Node1).GroupIndex < TdxMasterViewNode(Node2).GroupIndex then Result := -1 else if TdxMasterViewNode(Node1).GroupIndex = TdxMasterViewNode(Node2).GroupIndex then Result := 0 else Result := 1; end; procedure TdxMasterViewNode.UpdateData(UpdateSelf, UpdateChildren, UpdateOther: Boolean); const NoneUpdate = 0; SoftUpdate = 1; HardUpdate = 2; var PrevCursor: TCursor; AID, AKeyValue: Variant; S: string; LockSelection: Boolean; I: Integer; LevelUpdateState: PIntArray; procedure CancelLevel(ALevel: TdxMasterViewLevel; ProcessRoot: Boolean); var I: Integer; begin if ProcessRoot or (ALevel <> FLevel) then with ALevel do if (DataSet <> nil) and (DataSet.State in dsEditModes) then DataSet.Cancel; for I := 0 to ALevel.Count - 1 do CancelLevel(ALevel[I], True); end; procedure UpdateNode(const Node: TdxMasterViewNode); var I: Integer; begin with Node, Level do begin Node.FID := AID; Node.FKeyValue := AKeyValue; Node.FPreviewText := GetCurPreviewText(Node); for I := 0 to FColumns.Count - 1 do with TdxMasterViewColumn(FColumns[I]) do if Field <> nil then with Field do begin S := DisplayText; FValues[I] := S; if (DataType <> ftString) or (S = '') or Assigned(OnGetText) or (EditMask <> ''){!} then PVariant(FValues.Objects[I])^ := Value else PVariant(FValues.Objects[I])^ := S; end else begin FValues[I] := ''; PVariant(FValues.Objects[I])^ := Null;//Unassigned; end; if Node <> Self then CalcSummaries(True); end; end; procedure UpdateLevels(AItems: TdxMasterViewNode); var RestItems, RestGroupItems: TList; I, FirstIndex, AvailIndex, APos, L, R, C, CResult: Integer; ALevel: TdxMasterViewLevel; LastLevelToLoad, AExpanded, UseLock, ALoadAll, AFound, UnassignedID, Accept: Boolean; KeyValue: Variant; Node, AParentNode: TdxMasterViewNode; procedure BeginDeletion(const UseItems: Boolean); begin with FControl do begin FAbsoluteItems.BeginDeletion; FSelectedItems.BeginDeletion; end; if UseItems then AItems.FItems.BeginDeletion; end; procedure EndDeletion(const UseItems: Boolean); begin if UseItems then AItems.FItems.EndDeletion; with FControl do begin FSelectedItems.EndDeletion; FAbsoluteItems.EndDeletion; end; end; function NodeFromID(const AID: Variant): TdxMasterViewNode; begin with RestItems do begin APos := -1; Result := nil; R := Count - 1; if R = -1 then Exit; L := 0; UnassignedID := VarIsEmpty(AID); while L <= R do begin C := (L + R) div 2; with TdxMasterViewNode(List^[C]) do if UnassignedID and VarIsEmpty(ID) then CResult := 0 else try CResult := -VarCompare(AID, ID); except Exit; end; case CResult of 0: begin APos := C; Result := List^[C]; Exit; end; -1: R := C - 1; 1: L := C + 1; end; end; end; end; procedure UpdateOneLevel; var I, J: Integer; function GetNewIndex: Integer; begin if AParentNode.NodeType = ntGroup then Result := AParentNode.Count else begin Result := AvailIndex; Inc(AvailIndex); end; end; begin AExpanded := AItems.Expanded; AItems.AddToList(ALevel, RestItems, RestGroupItems, False); UseLock := (AItems.NodeType = ntData) and not ALevel.IsLast or (RestGroupItems.Count >= RestItems.Count); if (not ALevel.ShowCaption or (AItems.NodeType = ntCaption)) and (RestItems.Count <> 0) then begin J := ALevel.GroupColumnCount; AParentNode := TdxMasterViewNode(RestItems[0]).ParentNode; if (J = 0) and (AParentNode <> AItems) or (J <> 0) and (AParentNode = AItems) then begin if UseLock then AItems.FItems.BeginDeletion; try for I := RestItems.Count - 1 downto 0 do begin Node := TdxMasterViewNode(RestItems[I]); Node.ParentNode.RemoveItem(Node); end; finally if UseLock then AItems.FItems.EndDeletion; end; end; if not ALevel.SmartReload then RestItems.Sort(CompareItemsByID); I := RestGroupItems.Count; if (J = 0) and (I <> 0) then begin if not LockSelection then begin LockSelection := True; FControl.BeginSelection; end; if UseLock then BeginDeletion(True); try for I := I - 1 downto 0 do TdxMasterViewNode(RestGroupItems[I]).Free; RestGroupItems.Clear; finally if UseLock then EndDeletion(True); end; end; end; AvailIndex := AItems.GetAvailIndex(ALevel.Index); Node := AItems.FirstInLevel[ALevel]; if Node = nil then FirstIndex := AvailIndex else FirstIndex := Node.Index; RestGroupItems.Sort(CompareGroupItemsByPtr); if UseLock then AItems.FItems.BeginInsertion; try if not AExpanded and ALevel.SmartLoad then Exit; with ALevel do begin if ShowCaption and (AItems.NodeType <> ntCaption) then begin Node := NodeFromID(Unassigned); if Node = nil then begin Node := AItems.Add(ALevel, AvailIndex, ntCaption, Unassigned, Unassigned); Inc(AvailIndex); end else RestItems.Delete(APos); UpdateLevels(Node); Exit; end; if not Active then Exit; if SmartReload and (RestItems.Count <> 0) and (TdxMasterViewNode(RestItems[0]).NodeType <> ntCaption) then begin if LevelUpdateState[AbsoluteIndex] = NoneUpdate then LevelUpdateState[AbsoluteIndex] := SoftUpdate; for I := 0 to RestItems.Count - 1 do begin Node := TdxMasterViewNode(RestItems[I]); AParentNode := GetParentNode(AItems, Node, @FirstIndex, @AvailIndex, RestGroupItems, False); if Node.ParentNode <> AParentNode then begin if Node.ParentNode <> nil then for J := RestItems.Count - 1 downto 0 do begin Node := TdxMasterViewNode(RestItems[J]); Node.ParentNode.RemoveItem(Node); end; AParentNode.AddItem(GetNewIndex, Node); end; Node.CalcSummaries(False); UpdateLevels(Node); end; RestItems.Clear; Exit; end; if LevelUpdateState[AbsoluteIndex] <> HardUpdate then begin BeginLoad; LevelUpdateState[AbsoluteIndex] := HardUpdate; end; ALoadAll := DontFilterRecords; if not ALoadAll then if AItems.NodeType = ntData then KeyValue := AItems.KeyValue[Index] else KeyValue := AItems.ParentNode.KeyValue[Index]; with DataSet do begin if ALoadAll then begin First; AFound := not EOF; end else try AFound := Locate(DetailKey, KeyValue, []); except AFound := False; end; while AFound do begin AID := GetCurIDValue; AKeyValue := GetCurKeyValue; Accept := True; if Assigned(FOnFilterRecord) then FOnFilterRecord(ALevel, AID, KeyValue, Accept); if Accept then begin AParentNode := GetParentNode(AItems, nil{!!!}, @FirstIndex, @AvailIndex, RestGroupItems, False); Node := NodeFromID(AID); if Node = nil then Node := AParentNode.Add(ALevel, GetNewIndex, ntData, AID, AKeyValue) else begin RestItems.Delete(APos); if Node.ParentNode <> AParentNode then begin if Node.ParentNode <> nil then Node.ParentNode.RemoveItem(Node); AParentNode.AddItem(GetNewIndex, Node); end; end; UpdateNode(Node); UpdateLevels(Node); end; Next; AFound := not EOF; if AFound and not ALoadAll then AFound := VarAreEqual(GetCurDetailKeyValue, KeyValue); end; end; end; finally if UseLock then AItems.FItems.EndInsertion; RestGroupItems.Sort(CompareGroupItemsByGroupIndex); if (RestItems.Count <> 0) or (RestGroupItems.Count <> 0) then begin if not LockSelection then begin LockSelection := True; FControl.BeginSelection; end; AFound := //(AItems.NodeType = ntData) and ((RestItems.Count <> 0) and (TdxMasterViewNode(RestItems[0]).ParentNode = AItems) or (RestGroupItems.Count <> 0) and (TdxMasterViewNode(RestGroupItems[0]).ParentNode = AItems)); BeginDeletion(AFound); try for I := 0 to RestItems.Count - 1 do TdxMasterViewNode(RestItems[I]).Free; RestItems.Clear; for I := RestGroupItems.Count - 1 downto 0 do TdxMasterViewNode(RestGroupItems[I]).Free; RestGroupItems.Clear; finally EndDeletion(AFound); end; end; if LastLevelToLoad then with AItems do begin FExpanded := AExpanded; CheckExpanded; end; end; end; begin if (AItems.NodeType = ntData) and not AItems.Level.HasChildren then Exit; RestItems := TList.Create; RestItems.Capacity := 10; RestGroupItems := TList.Create; RestGroupItems.Capacity := 10; try if AItems.NodeType = ntData then for I := 0 to AItems.Level.Count - 1 do begin ALevel := AItems.Level[I]; LastLevelToLoad := ALevel.IsLast; UpdateOneLevel; end else begin ALevel := AItems.Level; LastLevelToLoad := True; UpdateOneLevel; end; finally RestGroupItems.Free; RestItems.Free; AItems.FinalizeSummaries(nil); end; end; //var // ft,lt:integer; begin if (FControl.FLayoutLockCount <> 0) or ([csLoading, csDestroying] * FControl.ComponentState <> []) then Exit; // ft:=gettickcount; if UpdateChildren then CancelLevel(FLevel, FNodeType <> ntData); with FControl do begin if FUpdatingData then Exit; FUpdatingData := True; end; try PrevCursor := Screen.Cursor; if FControl.ShowHourGlassCursor then Screen.Cursor := crHourGlass; FControl.BeginUpdate; try if (FParentNode <> nil) and UpdateSelf then begin if FLevel.Active then with FLevel do begin AID := GetCurIDValue; AKeyValue := GetCurKeyValue; UpdateNode(Self); end else ClearValues; if not UpdateChildren then begin FControl.FDontInvalidate := True; Invalidate(nil, vpContent); end; end; if UpdateChildren then if (FNodeType = ntData) and FLevel.HasChildren or (FNodeType = ntCaption) then begin FControl.BeginTopNodeChange; try I := FControl.AbsoluteLevelCount * SizeOf(Integer); GetMem(LevelUpdateState, I); FillChar(LevelUpdateState^, I, 0); try UpdateLevels(Self); finally for I := 0 to FControl.AbsoluteLevelCount - 1 do with FControl.AbsoluteLevels[I] do case LevelUpdateState[AbsoluteIndex] of SoftUpdate: DoSorting; HardUpdate: EndLoad(True); end; FreeMem(LevelUpdateState); if LockSelection then FControl.EndSelection; end; finally FControl.EndTopNodeChange; end; end else Clear; if (FLevel.Parent <> nil) and UpdateOther then with FParentNode do begin DoSorting(Self.FLevel); if HasSummariesData then begin InitializeSummaries(Self.FLevel); try I := FirstInLevel[Self.Level].Index; for I := I to I + CountInLevel[Self.Level] - 1 do Items[I].CalcSummaries(False); finally FinalizeSummaries(Self.FLevel); end; end; if FControl.FDontInvalidate then Invalidate(nil, vpAllAndBelow); end; finally FControl.EndUpdate; if FControl.ShowHourGlassCursor then Screen.Cursor := PrevCursor; end; finally FControl.FUpdatingData := False; end; // lt:=gettickcount; // application.mainform.caption:=inttostr(lt-ft); // application.messagebox(pchar(inttostr(lt-ft)),'TdxMasterViewNode.UpdateData',0); end; procedure TdxMasterViewNode.UpdateDataForLevel(ALevel: TdxMasterViewLevel); var I: Integer; begin if (FNodeType = ntData) and (FLevel = ALevel.Parent) then UpdateData(False, True, False) else if ALevel.HasAsParent(FLevel) then begin I := 0; while I < Count do with Items[I] do begin UpdateDataForLevel(ALevel); I := Index + 1; end; end; end; procedure TdxMasterViewNode.CreateSummaries; var I: Integer; begin if (FNodeType = ntData) and FLevel.HasChildren or (FNodeType <> ntData) then begin if FNodeType = ntData then I := FLevel.Count else I := 1; GetMem(FSummaries, I * SizeOf(PdxMVSummaryValues)); if FNodeType = ntData then for I := 0 to I - 1 do with FLevel[I] do if FSummaryColumns.Count <> 0 then GetMem(FSummaries[I], FSummaryColumns.Count * SizeOf(Extended)) else FSummaries[I] := nil else if FLevel.FSummaryColumns.Count <> 0 then GetMem(FSummaries[0], FLevel.FSummaryColumns.Count * SizeOf(Extended)) else FSummaries[0] := nil end; end; procedure TdxMasterViewNode.DestroySummaries; var I: Integer; begin if FSummaries <> nil then begin if FNodeType = ntData then for I := 0 to FLevel.Count - 1 do if FSummaries[I] <> nil then FreeMem(FSummaries[I]) else else if FSummaries[0] <> nil then FreeMem(FSummaries[0]); FreeMem(FSummaries); FSummaries := nil; end; end; procedure TdxMasterViewNode.CheckSummaries; begin if FSummaries = nil then CreateSummaries; InitializeSummaries(nil); end; procedure TdxMasterViewNode.InitializeSummaries(ALevel: TdxMasterViewLevel); var AIndex: Integer; Column: TdxMasterViewColumn; procedure InitializeOne(ALevel: TdxMasterViewLevel); var I: Integer; begin with ALevel do for I := 0 to FSummaryColumns.Count - 1 do begin Column := TdxMasterViewColumn(FSummaryColumns[I]); case Column.SummaryType of stMin: FSummaries[AIndex][I] := MaxExtended; stMax: FSummaries[AIndex][I] := MinExtended; else FSummaries[AIndex][I] := 0; end; Column.DoBeforeCalcSummary(Self, DataSet, FSummaries[AIndex][I]); end end; begin if FSummariesInitialized then Exit; if ALevel = nil then if FNodeType = ntData then for AIndex := 0 to FLevel.Count - 1 do InitializeOne(FLevel[AIndex]) else begin AIndex := 0; InitializeOne(FLevel); end else begin if FNodeType = ntData then AIndex := ALevel.Index else AIndex := 0; InitializeOne(ALevel); end; FSummariesInitialized := True; end; procedure TdxMasterViewNode.CalcSummaries(UseRealData: Boolean); const NumberVarTypes = [varSmallint, varInteger, varSingle, varDouble, varCurrency, varDate, varByte]; var AIndex, I: Integer; SummaryValue: Extended; SummaryVariant: Variant; Column: TdxMasterViewColumn; begin if FNodeType = ntData then begin if FParentNode.NodeType = ntData then AIndex := FLevel.Index else AIndex := 0; with FLevel do for I := 0 to FSummaryColumns.Count - 1 do with TdxMasterViewColumn(FSummaryColumns[I]) do begin if (SummaryField = nil) or (SummaryType = stCount) then SummaryValue := 0 else begin if UseRealData then SummaryVariant := SummaryField.Value else begin Column := ColumnByFieldName(SummaryFieldName); if Column = nil then SummaryVariant := Null else SummaryVariant := Values[Column.Index]; end; if VarType(SummaryVariant) in NumberVarTypes then SummaryValue := SummaryVariant else SummaryValue := 0; end; DoCalcSummary(Self, DataSet, SummaryValue); with FParentNode do case SummaryType of stSum, stAverage: FSummaries[AIndex][I] := FSummaries[AIndex][I] + SummaryValue; stMin: if SummaryValue < FSummaries[AIndex][I] then FSummaries[AIndex][I] := SummaryValue; stMax: if SummaryValue > FSummaries[AIndex][I] then FSummaries[AIndex][I] := SummaryValue; end; end; end; end; procedure TdxMasterViewNode.FinalizeSummaries(ALevel: TdxMasterViewLevel); var AIndex, C: Integer; Column: TdxMasterViewColumn; procedure FinalizeOne(ALevel: TdxMasterViewLevel); var I: Integer; begin with ALevel do for I := 0 to FSummaryColumns.Count - 1 do begin Column := TdxMasterViewColumn(FSummaryColumns[I]); case Column.SummaryType of stCount: FSummaries[AIndex][I] := CountInLevel[ALevel]; stAverage: begin C := CountInLevel[ALevel]; if C = 0 then FSummaries[AIndex][I] := 0 else FSummaries[AIndex][I] := FSummaries[AIndex][I] / C; end; end; Column.DoAfterCalcSummary(Self, DataSet, FSummaries[AIndex][I]); end end; begin if ALevel = nil then for AIndex := 0 to Count - 1 do Items[AIndex].FinalizeSummaries(nil); if not FSummariesInitialized then Exit; if ALevel = nil then if FNodeType = ntData then for AIndex := 0 to FLevel.Count - 1 do FinalizeOne(FLevel[AIndex]) else begin AIndex := 0; FinalizeOne(FLevel); end else begin if FNodeType = ntData then AIndex := ALevel.Index else AIndex := 0; FinalizeOne(ALevel); end; FSummariesInitialized := False; end; function TdxMasterViewNode.BeginLevel(var Size: Integer): Boolean; begin Result := Expanded; if Result and (FNodeType = ntData) then Size := Level.LevelSeparatorWidth else Size := 0; end; function TdxMasterViewNode.EndLevel(var Size, EndLevelCount, EndLevelData: Integer): Boolean; var ANode: TdxMasterViewNode; AlreadyInc: Boolean; I: Integer; begin Size := 0; EndLevelCount := 0; EndLevelData := 0; Result := not Expanded and IsLastInLevel; if Result then begin AlreadyInc := False; I := 0; ANode := Self; repeat with ANode do begin if not IsLastInLevel then Break; with Level do begin if (NodeType = ntData) and ShowFooter then Inc(Size, RowCount * FooterHeight); if ParentNode.Level <> Level then begin if Parent.Parent <> nil then begin Inc(Size, Parent.LevelSeparatorWidth); SetHasLevelSeparator(EndLevelData, I); if ANode.IsLast then begin Inc(Size, Parent.RowSeparatorWidth); SetHasRowSeparator(EndLevelData, I); end; end; if AlreadyInc then AlreadyInc := False else Inc(EndLevelCount); end else if NodeType = ntData then begin Inc(EndLevelCount); AlreadyInc := True; end; end; if not IsLast then Break; if ParentNode.Level <> Level then Inc(I); ANode := ParentNode; end; until ANode.ParentNode = nil; end; end; function TdxMasterViewNode.HasExpandButton: Boolean; begin Result := (FNodeType <> ntData) or FLevel.HasExpandButton; end; function TdxMasterViewNode.HasFooter(AParentIndex: Integer): Boolean; begin Result := (FViewInfo.FooterSize <> 0) and ((AParentIndex <> 0) or (FNodeType = ntData)); if Result and (AParentIndex <> -1) then with FLevel.Parents[AParentIndex] do Result := ShowFooter and (AParentIndex < FViewInfo.EndLevelCount); end; function TdxMasterViewNode.HasGroupByBox(var Size: Integer): Boolean; begin with FLevel do begin Result := ShowGroupByBox and (Self = FFirstVisibleNode); if Result then Size := GroupByBoxHeight else Size := 0; end; end; function TdxMasterViewNode.HasHeader(ATopIndex: Integer; var Size: Integer): Boolean; begin Result := (FNodeType = ntData) and FLevel.ShowHeader and (not FLevel.Horizontal or ((lohForFirstNode in FLevel.OptionsHeader) and IsFirstInLevel or (lohAfterExpandedNode in FLevel.OptionsHeader) and (Index <> 0) and FParentNode.FItems.List^[Index - 1].Expanded or (lohForFirstVisibleNode in FLevel.OptionsHeader) and (Self = FLevel.FFirstVisibleNodeWithData))); if Result then with FLevel do Size := VisibleRowCount * HeaderRealHeight else Size := 0; end; function TdxMasterViewNode.HasLevelSeparator(ALevelParentIndex: Integer): Boolean; begin Result := FViewInfo.EndLevelData and (1 shl ALevelParentIndex) <> 0; end; function TdxMasterViewNode.HasPreview: Boolean; begin with Level do Result := ShowPreview and ((FPreviewText <> '') or (PreviewLineCount <> 0)); end; function TdxMasterViewNode.HasRowSeparator(ALevelParentIndex: Integer): Boolean; begin Result := FViewInfo.EndLevelData and (1 shl (ALevelParentIndex + 16)) <> 0; end; function TdxMasterViewNode.HasSummariesData: Boolean; begin Result := FSummaries <> nil; end; function TdxMasterViewNode.CalcViewInfo(const ATopIndex: Integer; const R: TRect; const AIndent, AWidth: Integer; var AFooterSize: Integer; const StoreInfo: Boolean): Integer; var I, J: Integer; begin with FViewInfo do begin with FLevel do begin case FNodeType of ntData: begin if PreviewSize = 0 then if HasPreview then CalcPreviewLineCount(FPreviewDC, FPreviewText, AWidth - Byte(HasExpandButton) * LevelIndent, Self, I) else I := 0 else I := PreviewSize; Result := VisibleRowCount * ContentRealHeight + I; if (I <> 0) or (LineWidth = 0) then Inc(Result, ExtLineWidth); end; ntCaption: begin I := 0; Result := CaptionHeight + ExtLineWidth; end; ntGroup: begin I := 0; Result := GroupHeight + ExtLineWidth; end; else Result := 0; end; if StoreInfo then PreviewSize := I; if not FExpanded and (FNodeType = ntData) then Inc(Result, RowSeparatorWidth); if HasGroupByBox(I) then Inc(Result, I); if StoreInfo then GroupByBoxSize := I; if HasHeader(ATopIndex, I) and Horizontal then Inc(Result, I); if StoreInfo then HeaderSize := I; end; if BeginLevel(I) then begin Inc(Result, I); AFooterSize := 0; if StoreInfo then begin FooterSize := 0; EndLevelCount := 0; EndLevelData := 0; end; end else begin if EndLevel(AFooterSize, I, J) then Inc(Result, AFooterSize); if StoreInfo then begin FooterSize := AFooterSize; EndLevelCount := I; EndLevelData := J; end; end; if StoreInfo then begin with Bounds do begin Left := AIndent; Right := Left + AWidth; case FNodeType of ntCaption: with FLevel do Dec(Left, (1 + GroupColumnCount) * (LevelIndent + ExtLineWidth)); ntGroup: with FLevel do Dec(Left, (GroupColumnCount - GroupIndex) * (LevelIndent + ExtLineWidth)); end; Top := R.Top; Bottom := Top + Result; end; // Indent := AIndent; //:= Bounds.Left !!! Options := []; LevelIndex := GetLevelIndex; end; end; end; procedure TdxMasterViewNode.ClearViewInfo; begin SetRectEmpty(FViewInfo.Bounds); end; function TdxMasterViewNode.ContentColIndexFromX(X: Integer): Integer; begin Result := FLevel.Layout.ColFromX(X); end; function TdxMasterViewNode.ContentRowIndexFromY(Y: Integer): Integer; var R: TRect; begin R := FullContentBounds; if (FNodeType = ntData) and (R.Top <= Y) and (Y < R.Bottom) then begin Result := (Y - R.Top) div FLevel.ContentRealHeight; if Result >= FLevel.RowCount then Result := -1; end else Result := -1; end; function TdxMasterViewNode.FooterColIndexFromX(AParentIndex: Integer; X: Integer): Integer; begin Result := FLevel.Parents[AParentIndex].Layout.ColFromX(X); end; function TdxMasterViewNode.FooterRowIndexFromY(AParentIndex: Integer; Y: Integer): Integer; var R: TRect; begin R := SubFooterBounds[AParentIndex]; if (R.Top <= Y) and (Y < R.Bottom) then with FLevel.Parents[AParentIndex] do begin Result := (Y - R.Top) div FooterHeight; if Result > RowCount - 1 then Result := -1; end else Result := -1; end; function TdxMasterViewNode.HeaderColIndexFromX(X: Integer): Integer; begin Result := FLevel.Layout.ColFromX(X); end; function TdxMasterViewNode.HeaderRowIndexFromY(Y: Integer): Integer; begin Result := InternalHeaderRowIndexFromY(Y); if Result >= FLevel.VisibleRowCount then Result := -1; end; function TdxMasterViewNode.InternalHeaderRowIndexFromY(Y: Integer): Integer; var R: TRect; begin R := FullHeaderBounds; if R.Top = R.Bottom then Result := -1 else begin Result := (Y - R.Top) div FLevel.HeaderRealHeight; if Result < -1 then Result := -1; end; end; function TdxMasterViewNode.ShowExpandButton: Boolean; begin Result := HasChildren or not (lovNoButtonsWhenNoChildren in FLevel.OptionsView) or (not FTriedToExpand and CanExpand); end; procedure TdxMasterViewNode.Collapse(Recurse: Boolean); var CanAnimate: Boolean; Delta, AnimationAreaHeight, PrevBaseValue, BaseValue, I, PrevTopIndex, PrevTopNodeContentRectTop, AVisibleItemCount, APartVisibleItemCount: Integer; BaseNode: TdxMasterViewNode; PrevCursor: TCursor; R: TRect; DC: HDC; function GetBaseValue: Integer; begin if BaseNode = nil then if FExpanded then with GetLastSubChild do if Visible then Result := ContentBounds.Bottom else Result := 0 else Result := ContentBounds.Bottom else with BaseNode do if Visible then Result := ContentBounds.Top else Result := 0; if (Result = 0) or (Result > AnimationAreaHeight) then Result := AnimationAreaHeight; end; procedure PrepareAnimation1; begin CanAnimate := FControl.Animation and (FParentNode <> nil) and FExpanded and FControl.HandleAllocated and (FControl.FUpdateLockCount = 0); if CanAnimate then begin ProcessPaintMessages(FControl.Handle); if IsLast then begin with GetLastSubChild do if Visible then Delta := ContentBounds.Bottom else Delta := 0; BaseNode := FParentNode; while (BaseNode.ParentNode <> nil) and BaseNode.IsLast do BaseNode := BaseNode.ParentNode; if BaseNode.ParentNode = nil then BaseNode := nil else BaseNode := BaseNode.ParentNode[BaseNode.Index + 1]; end else begin with FParentNode[Index + 1] do if Visible then Delta := ContentBounds.Top else Delta := 0; BaseNode := FParentNode[Index + 1]; end; AnimationAreaHeight := FControl.GetAnimatableHeight; if Delta = 0 then Delta := AnimationAreaHeight; PrevBaseValue := GetBaseValue; end; end; procedure PrepareAnimation2; begin if CanAnimate then begin PrevTopIndex := FControl.TopIndex; PrevTopNodeContentRectTop := FControl.TopNode.ContentBounds.Top; R := FullBounds; end; end; procedure DoAnimation; var AnimationStep, AnimationIncreaseStep: Integer; UpdateR: TRect; begin if CanAnimate then begin AnimationStep := AnimationOriginalStep; FControl.CalcViewInfo(FControl.TopIndex, -1, AVisibleItemCount, APartVisibleItemCount, True); if FControl.TopIndex = PrevTopIndex then begin BaseValue := GetBaseValue; Dec(Delta, R.Bottom); R.Top := R.Bottom + AnimationStep; R.Bottom := AnimationAreaHeight; AnimationIncreaseStep := (R.Bottom - R.Top + Delta) div AnimationIncreaseStepDivisor1; FControl.BeginAnimation; DC := GetDC(FControl.Handle); try while (Delta > AnimationStep) and (R.Top < R.Bottom) do begin ScrollWindowEx(FControl.Handle, 0, -AnimationStep, @R, nil, 0, @UpdateR, 0); //FillRect(DC, UpdateR, FControl.Brush.Handle); Dec(Delta, AnimationStep); Inc(R.Top, AnimationIncreaseStep); Dec(R.Bottom, AnimationStep); Inc(AnimationStep, AnimationIncreaseStep); end; finally ReleaseDC(FControl.Handle, DC); FControl.EndAnimation; end; Invalidate(nil, vpAllAndBelow); end else begin Delta := ContentBounds.Bottom - R.Bottom; R.Top := PrevTopNodeContentRectTop; AnimationIncreaseStep := (R.Bottom - R.Top) div AnimationIncreaseStepDivisor2; FControl.BeginAnimation; DC := GetDC(FControl.Handle); try while Delta > AnimationStep do begin ScrollWindowEx(FControl.Handle, 0, AnimationStep, @R, nil, 0, @UpdateR, 0); //FillRect(DC, UpdateR, FControl.Brush.Handle); Dec(Delta, AnimationStep); Inc(R.Top, AnimationStep); Inc(R.Bottom, AnimationStep); Inc(AnimationStep, AnimationIncreaseStep); end; finally ReleaseDC(FControl.Handle, DC); FControl.EndAnimation; end; FControl.Invalidate; end; end; end; begin if not FExpanded or not FLevel.DoBeforeCollapse(Self) then Exit; PrepareAnimation1; PrevCursor := Screen.Cursor; if FControl.ShowHourGlassCursor then Screen.Cursor := crHourGlass; FControl.BeginUpdate; try if FParentNode <> nil then ChangeExpanded(False); if Recurse then for I := 0 to Count - 1 do Items[I].Collapse(True); finally try PrepareAnimation2; finally FControl.EndUpdate; if FControl.ShowHourGlassCursor then Screen.Cursor := PrevCursor; DoAnimation; FLevel.DoAfterCollapse(Self); end; end; end; procedure TdxMasterViewNode.Delete; var NewNode: TdxMasterViewNode; LevelUsed: PBoolArray; I: Integer; Control: TdxMasterView; function DeleteSelf: TdxMasterViewNode; var PrevNode: TdxMasterViewNode; begin Result := FParentNode; Free; while (Result.NodeType = ntGroup) and (Result.Count = 0) do begin PrevNode := Result; Result := Result.ParentNode; PrevNode.Free; end; end; procedure DeleteOne(ALevel: TdxMasterViewLevel; const AID, AKeyValue: Variant); var Node: TdxMasterViewNode; I: Integer; AFound: Boolean; begin if not LevelUsed[ALevel.AbsoluteIndex] then begin LevelUsed[ALevel.AbsoluteIndex] := True; ALevel.BeginLoad; end; Node := Control.NodeFromID(ALevel, AID); try if Node <> nil then Node.Sync; if ALevel.HasChildren then for I := 0 to ALevel.Count - 1 do with ALevel[I] do if CanDelete then with DataSet do begin AFound := Locate(DetailKey, AKeyValue[I], []); while AFound do begin DeleteOne(ALevel[I], GetCurIDValue, GetCurKeyValue); AFound := not EOF and VarAreEqual(GetCurDetailKeyValue, AKeyValue[I]); end; end; ALevel.DataSet.Delete; except if Node <> nil then Node.UpdateData(False, False, True); raise; end; if Node <> nil then if Node = Self then begin ALevel := FLevel; Node := DeleteSelf; if Node.CountInLevel[ALevel] <> 0 then Node.FirstInLevel[ALevel].UpdateData(False, False, True); end else Node.Free; end; begin if not CanDelete or (lodDeleteOnlyChildless in FLevel.OptionsDB) and FLevel.HasChildren and FindChildren then Exit; if AbsoluteIndex = -1 then NewNode := nil else if IsLast then if IsFirst then if FParentNode.ParentNode = nil then NewNode := nil else begin NewNode := FParentNode; while (NewNode.NodeType = ntGroup) and (NewNode.Count = 1) do begin if NewNode.ParentNode.Count <> 1 then begin if NewNode.IsLast then NewNode := NewNode.ParentNode[NewNode.Index - 1] else NewNode := NewNode.ParentNode[NewNode.Index + 1]; Break; end; NewNode := NewNode.ParentNode; end; end else NewNode := FParentNode[Index - 1] else NewNode := FParentNode[Index + 1]; I := FControl.AbsoluteLevelCount * SizeOf(Boolean); GetMem(LevelUsed, I); FillChar(LevelUsed^, I, 0); try Control := FControl; Control.BeginSelection; try Control.BeginUpdate; try DeleteOne(FLevel, FID, FKeyValue); finally try for I := 0 to Control.AbsoluteLevelCount - 1 do if LevelUsed[I] then Control.AbsoluteLevels[I].EndLoad(True); finally Control.EndUpdate; end; end; finally Control.EndSelection; end; finally FreeMem(LevelUsed); end; if NewNode <> nil then with NewNode do Control.FocusedIndex := AbsoluteIndex; end; procedure TdxMasterViewNode.Expand(Recurse: Boolean); var CanAnimate, PrevExpanded: Boolean; PrevCursor: TCursor; AnimationAreaHeight, I, AVisibleItemCount, APartVisibleItemCount, Delta: Integer; R: TRect; procedure PrepareAnimation; begin CanAnimate := FControl.Animation and (FParentNode <> nil) and not FExpanded and FControl.HandleAllocated and (FControl.FUpdateLockCount = 0); if CanAnimate then begin ProcessPaintMessages(FControl.Handle); AnimationAreaHeight := FControl.GetAnimatableHeight; R := FullContentBounds; end; end; procedure DoAnimation; var AnimationStep, AnimationIncreaseStep: Integer; DC: HDC; UpdateR: TRect; begin if CanAnimate and FExpanded then begin AnimationStep := AnimationOriginalStep; FControl.CalcViewInfo(FControl.TopIndex, -1, AVisibleItemCount, APartVisibleItemCount, True); R.Top := R.Bottom - (FLevel.ExtLineWidth + FLevel.RowSeparatorWidth); R.Bottom := AnimationAreaHeight - AnimationStep; if IsLast then with GetLastSubChild do if Visible then Delta := ContentBounds.Bottom else Delta := 0 else with FParentNode[Index + 1] do if Visible then Delta := ContentBounds.Top else Delta := 0; if Delta = 0 then Delta := AnimationAreaHeight; Dec(Delta, R.Top); AnimationIncreaseStep := (R.Bottom - R.Top + Delta) div AnimationIncreaseStepDivisor3; FControl.BeginAnimation; DC := GetDC(FControl.Handle); try while (Delta > AnimationStep) and (R.Top < R.Bottom) do begin ScrollWindowEx(FControl.Handle, 0, AnimationStep, @R, nil, 0, @UpdateR, 0); //FillRect(DC, UpdateR, FControl.Brush.Handle); Dec(Delta, AnimationStep); Inc(R.Top, AnimationStep); Dec(R.Bottom, AnimationIncreaseStep); Inc(AnimationStep, AnimationIncreaseStep); end; finally ReleaseDC(FControl.Handle, DC); FControl.EndAnimation; end; Invalidate(nil, vpAllAndBelow); end; end; procedure LockLevels(ALock: Boolean); var I: Integer; RootLevel: TdxMasterViewLevel; AllowDoAction: Boolean; procedure LockOne(ALevel: TdxMasterViewLevel); var I: Integer; begin if ALevel <> RootLevel then with ALevel do if SmartLoad or AllowDoAction then begin if ALock then BeginLoad else EndLoad(True); AllowDoAction := True; end; for I := 0 to ALevel.Count - 1 do LockOne(ALevel[I]); end; begin for I := 0 to FLevel.Count - 1 do begin RootLevel := FLevel[I]; AllowDoAction := RootLevel.SmartLoad; LockOne(RootLevel); end; end; begin if not CanExpand or IsEditing or not Expanded and not FLevel.DoBeforeExpand(Self) then Exit; PrepareAnimation; PrevExpanded := Expanded; PrevCursor := Screen.Cursor; if FControl.ShowHourGlassCursor then Screen.Cursor := crHourGlass; FControl.BeginUpdate; try ChangeExpanded(True); if FNodeType = ntCaption then if FLevel.SmartLoad and (Count = 0) then UpdateData(False, True, False) else else if FNodeType = ntData then for I := 0 to FLevel.Count - 1 do if FLevel[I].SmartLoad and (CountInLevel[FLevel[I]] = 0) then begin UpdateData(False, True, False); Break; end; CheckExpanded; if Recurse then begin LockLevels(True); try for I := 0 to Count - 1 do Items[I].Expand(True); finally LockLevels(False); end; end; finally FControl.EndUpdate; if FControl.ShowHourGlassCursor then Screen.Cursor := PrevCursor; DoAnimation; if Expanded <> PrevExpanded then FLevel.DoAfterExpand(Self); end; end; function TdxMasterViewNode.FindChildren: Boolean; var I: Integer; begin Result := HasChildren; if Result then Exit; case FNodeType of ntData: for I := 0 to FLevel.Count - 1 do with FLevel[I] do if SmartLoad and Active then begin BeginLoad; try Result := DataSet.Locate(DetailKey, FKeyValue[I], []); if Result then Break; finally EndLoad(False); end; end; ntCaption: with FLevel do if SmartLoad and Active then if ParentCount = 2 then with DataSet do Result := not (BOF and EOF) else begin BeginLoad; try Result := DataSet.Locate(DetailKey, FParentNode.KeyValue[Index], []); finally EndLoad(False); end; end; end; end; function TdxMasterViewNode.GetParentNode(ALevel: TdxMasterViewLevel; OnlyDataNodes: Boolean): TdxMasterViewNode; begin Result := Self; while (Result <> nil) and (Result.Level <> ALevel) do Result := Result.ParentNode; if Result <> nil then if OnlyDataNodes then begin while (Result.NodeType <> ntData) and (Result.ParentNode <> nil) and (Result.ParentNode.Level = ALevel) do Result := Result.ParentNode; if Result.NodeType <> ntData then Result := nil; end; { if Result <> nil then if OnlyDataNodes then if Result.NodeType <> ntData then Result := nil else else while (Result.ParentNode <> nil) and (Result.ParentNode.Level = ALevel) do Result := Result.ParentNode;} end; procedure TdxMasterViewNode.MakeVisible; var Node: TdxMasterViewNode; begin Node := FParentNode; while Node <> nil do begin Node.Expanded := True; Node := Node.ParentNode; end; end; procedure TdxMasterViewNode.LoadChildren(Recurse: Boolean); var PrevExpanded: Boolean; I: Integer; begin FControl.BeginUpdate; try PrevExpanded := Expanded; try Expanded := True; if Recurse then for I := 0 to Count - 1 do Items[I].LoadChildren(Recurse); finally Expanded := PrevExpanded; end; finally FControl.EndUpdate; end; end; function TdxMasterViewNode.NodeFromID(ALevel: TdxMasterViewLevel; const AID: Variant): TdxMasterViewNode; var I: Integer; begin for I := 0 to Count - 1 do begin Result := Items[I]; if Result.Level = ALevel then if Result.NodeType = ntData then if VarAreEqual(Result.ID, AID) then Exit else else if (Result.NodeType = ntCaption) and VarIsEmpty(AID) then Exit else begin Result := Result.NodeFromID(ALevel, AID); if Result <> nil then Exit; end; end; Result := nil; end; function TdxMasterViewNode.NodeFromKeyValue(ALevel: TdxMasterViewLevel; AChildIndex: Integer; const AKeyValue: Variant): TdxMasterViewNode; var I: Integer; begin for I := 0 to Count - 1 do begin Result := Items[I]; if Result.Level = ALevel then if Result.NodeType = ntData then if VarAreEqual(Result.KeyValue[AChildIndex], AKeyValue) then Exit else else begin Result := Result.NodeFromKeyValue(ALevel, AChildIndex, AKeyValue); if Result <> nil then Exit; end; end; Result := nil; end; { TdxMasterView } constructor TdxMasterView.Create(AOwner: TComponent); begin inherited; ControlStyle := ControlStyle - [csCaptureMouse]; ParentColor := False; TabStop := True; Width := 230; Height := 170; FAbsoluteItems := TdxMasterViewList.Create(nil, lmAbsoluteItems); FAbsoluteLevels := TList.Create; FArrowsColor := clLime; FBitmap := TBitmap.Create; FCustomizationFormPos := Point(-1, -1); FCustomizationFormRowCount := dxMVCustomizationFormRowCount; FLevels := TdxMasterViewLevel.Create(nil); FLevels.FControl := Self; FItems := TdxMasterViewNode.Create(Self, FLevels, nil, -1, ntData, Unassigned, Unassigned); FItems.ChangeExpanded(True); FOptionsBehavior := [mobUseIndent]; FOptionsDB := [modSyncMove]; FOptionsView := [movAnimation, movHideFocusRect, movKeepColumnWidths, movTransparentDragAndDrop, movUseBitmapToDrawPreview]; FSelectedItems := TdxMasterViewList.Create(FItems, lmSelectedItems); FStyles := TList.Create; end; destructor TdxMasterView.Destroy; begin try if not IsDesigning then begin if mobStoreInIniFile in FOptionsBehavior then SaveToRegIniFile(FIniFileName, riIniFile); if mobStoreInRegistry in FOptionsBehavior then SaveToRegIniFile(FRegistryPath, riRegistry); end; finally if dxMVDesigner <> nil then dxMVDesigner.Hide(Self); HighlightStyle := nil; InactiveStyle := nil; DestroyStyles; FStyles.Free; FItems.Free; FItems := nil; FLevels.Free; FLevels := nil; FSelectedItems.Free; FBitmap.Free; FAbsoluteLevels.Free; FAbsoluteItems.Free; inherited; end; end; function TdxMasterView.GetAbsoluteItemCount: Integer; begin Result := FAbsoluteItems.Count; end; function TdxMasterView.GetAbsoluteItem(Index: Integer): TdxMasterViewNode; begin Result := FAbsoluteItems[Index]; end; function TdxMasterView.GetAbsoluteLevelCount: Integer; begin Result := FAbsoluteLevels.Count; end; function TdxMasterView.GetAbsoluteLevel(Index: Integer): TdxMasterViewLevel; begin Result := FAbsoluteLevels[Index]; end; function TdxMasterView.GetAnimation: Boolean; begin Result := movAnimation in FOptionsView; end; function TdxMasterView.GetAutoColumnWidth: Boolean; begin Result := movAutoColumnWidth in FOptionsView; end; function TdxMasterView.GetCustomizationFormLevel: TdxMasterViewLevel; begin if FCustomizing then Result := TdxMVCustomizationForm(FCustomizationForm).ActiveLevel else Result := nil; end; function TdxMasterView.GetFocusedIndex: Integer; begin if FFocusedNode = nil then Result := -1 else Result := FFocusedNode.AbsoluteIndex; end; function TdxMasterView.GetHasFocus: Boolean; begin Result := Focused; end; function TdxMasterView.GetHighlightBrush: HBRUSH; begin if (HighlightStyle <> nil) and (svColor in HighlightStyle.AssignedValues) then Result := HighlightStyle.Brush else Result := GetSysColorBrush(COLOR_HIGHLIGHT); end; function TdxMasterView.GetHighlightColor: TColor; begin if (HighlightStyle <> nil) and (svColor in HighlightStyle.AssignedValues) then Result := HighlightStyle.Color else Result := clHighlight; end; function TdxMasterView.GetHighlightFontColor: TColor; begin if (HighlightStyle <> nil) and (svFont in HighlightStyle.AssignedValues) then Result := HighlightStyle.Font.Color else Result := clHighlightText; end; function TdxMasterView.GetInactiveBrush: HBRUSH; begin if (InactiveStyle <> nil) and (svColor in InactiveStyle.AssignedValues) then Result := InactiveStyle.Brush else Result := GetSysColorBrush(COLOR_BTNFACE); end; function TdxMasterView.GetInactiveColor: TColor; begin if (InactiveStyle <> nil) and (svColor in InactiveStyle.AssignedValues) then Result := InactiveStyle.Color else Result := clBtnFace; end; function TdxMasterView.GetInactiveFocusBrush: HBRUSH; begin if (InactiveStyle <> nil) and (svAnotherColor in InactiveStyle.AssignedValues) then Result := InactiveStyle.AnotherBrush else Result := 0; end; function TdxMasterView.GetInactiveFocusColor: TColor; begin if (InactiveStyle <> nil) and (svAnotherColor in InactiveStyle.AssignedValues) then Result := InactiveStyle.AnotherColor else Result := clNone; end; function TdxMasterView.GetInactiveFontColor: TColor; begin if (InactiveStyle <> nil) and (svFont in InactiveStyle.AssignedValues) then Result := InactiveStyle.Font.Color else Result := clBtnText; end; function TdxMasterView.GetIsDesigning: Boolean; begin Result := csDesigning in ComponentState; end; function TdxMasterView.GetIsDestroying: Boolean; begin Result := csDestroying in ComponentState; end; function TdxMasterView.GetIsLoading: Boolean; begin Result := csLoading in ComponentState; end; function TdxMasterView.GetMultiSelect: Boolean; begin Result := mobMultiSelect in FOptionsBehavior; end; function TdxMasterView.GetPartVisibleItemCount: Integer; var AVisibleItemCount: Integer; begin CalcViewInfo(TopIndex, -1, AVisibleItemCount, Result, True{False}); end; { function TdxMasterView.GetScrollableWidth: Integer; var I, Size: Integer; begin Result := 0; for I := 0 to LevelCount - 1 do with Levels[I] do if Visible then begin Size := Indent + VisibleWidth; if Size > Result then Result := Size; end; end; } function TdxMasterView.GetSelectedItem(Index: Integer): TdxMasterViewNode; begin if MultiSelect then Result := FSelectedItems[Index] else Result := FFocusedNode; end; function TdxMasterView.GetSelectedItemCount: Integer; begin if MultiSelect then Result := FSelectedItems.Count else Result := Byte(FFocusedNode <> nil); end; function TdxMasterView.GetShowHourGlassCursor: Boolean; begin Result := mobShowHourGlassCursor in FOptionsBehavior; end; function TdxMasterView.GetStyle(Index: Integer): TdxMasterViewStyle; begin Result := TdxMasterViewStyle(FStyles[Index]); end; function TdxMasterView.GetStyleCount: Integer; begin Result := FStyles.Count; end; function TdxMasterView.GetSyncMove: Boolean; begin Result := modSyncMove in FOptionsDB; end; function TdxMasterView.GetTopIndex: Integer; begin if FTopNode = nil then Result := -1 else Result := FTopNode.AbsoluteIndex; end; function TdxMasterView.GetTransparentDragAndDrop: Boolean; begin Result := movTransparentDragAndDrop in FOptionsView; end; function TdxMasterView.GetUseIndent: Boolean; begin Result := mobUseIndent in FOptionsBehavior; end; function TdxMasterView.GetVisibleItemCount: Integer; var APartVisibleItemCount: Integer; begin CalcViewInfo(TopIndex, -1, Result, APartVisibleItemCount, True{False}); end; function TdxMasterView.GetWidthForColumns: Integer; begin Result := ScrollableWidth; end; procedure TdxMasterView.SetCanUseSmartReload(Value: Boolean); var I: Integer; begin if not Value or (FLayoutLockCount = 0) then for I := 0 to AbsoluteLevelCount - 1 do if not Value or (AbsoluteLevels[I].FDataChangingLockCount = 0) then AbsoluteLevels[I].CanUseSmartReload := Value; end; procedure TdxMasterView.SetCustomizationFormLevel(Value: TdxMasterViewLevel); begin if FCustomizing then TdxMVCustomizationForm(FCustomizationForm).ActiveLevel := Value; end; procedure TdxMasterView.SetCustomizationFormRowCount(Value: Integer); begin if Value < 2 then Value := 2; FCustomizationFormRowCount := Value; end; procedure TdxMasterView.SetCustomizing(Value: Boolean); begin if (FCustomizing <> Value) and (HandleAllocated and IsWindowVisible(Handle) and (FLevels.Count <> 0) or not Value) then begin FCustomizing := Value; if FCustomizing then FCustomizationForm := TdxMVCustomizationForm.Create(Self) else begin with FCustomizationForm do if not (csDestroying in ComponentState) then Free; FCustomizationForm := nil; end; if Assigned(FOnCustomizing) then FOnCustomizing(Self, Value); end; end; procedure TdxMasterView.SetFocusedIndex(Value: Integer); begin if Value < 0 then Value := 0; if Value >= AbsoluteItemCount then Value := AbsoluteItemCount - 1; if FocusedIndex <> Value then ChangeFocusedIndex(Value); end; procedure TdxMasterView.SetFocusedNode(Value: TdxMasterViewNode); var PrevFocusedNode: TdxMasterViewNode; begin if FFocusedNode <> Value then begin PrevFocusedNode := FFocusedNode; FFocusedNode := Value; if not IsDestroying and Assigned(FOnFocusNode) then FOnFocusNode(Self, PrevFocusedNode, FFocusedNode); end; end; procedure TdxMasterView.SetHighlightStyle(Value: TdxMasterViewStyle); begin if FHighlightStyle <> Value then begin if FHighlightStyle <> nil then FHighlightStyle.RemoveConsumeType(Self, sctHighlight); FHighlightStyle := Value; if FHighlightStyle <> nil then FHighlightStyle.AddConsumeType(Self, sctHighlight); HighlightStyleChanged([]); end; end; procedure TdxMasterView.SetInactiveStyle(Value: TdxMasterViewStyle); begin if FInactiveStyle <> Value then begin if FInactiveStyle <> nil then FInactiveStyle.RemoveConsumeType(Self, sctInactive); FInactiveStyle := Value; if FInactiveStyle <> nil then FInactiveStyle.AddConsumeType(Self, sctInactive); InactiveStyleChanged([]); end; end; procedure TdxMasterView.SetLeftPos(Value: Integer); begin Value := CheckLeftPos(Value); if FLeftPos <> Value then begin if HandleAllocated then ScrollWindowEx(Handle, FLeftPos - Value, 0, nil, nil, 0, nil, SW_INVALIDATE); FLeftPos := Value; if HandleAllocated then UpdateWindow(Handle); if Assigned(FOnLeftPosChanged) then FOnLeftPosChanged(Self); end; end; procedure TdxMasterView.SetOptionsBehavior(Value: TdxMasterViewOptionsBehavior); var ChangedValues: TdxMasterViewOptionsBehavior; begin if FOptionsBehavior <> Value then begin Word(ChangedValues) := Word(Value) xor Word(FOptionsBehavior); if mobMultiSelect in ChangedValues then if not (mobMultiSelect in Value) then ClearSelectedItems; FOptionsBehavior := Value; if [mobAnsiCompareStrOnGrouping, mobCaseInsensitiveGrouping] * ChangedValues <> [] then begin CanUseSmartReload := False; UpdateData; end; if [mobAnsiCompareStrOnSorting, mobCaseInsensitiveSorting] * ChangedValues <> [] then DoSorting; if mobMultiSelect in ChangedValues then if FocusedNode <> nil then FocusedNode.NodeChanged(False); end; end; procedure TdxMasterView.SetOptionsDB(Value: TdxMasterViewOptionsDB); var ChangedValues: TdxMasterViewOptionsDB; begin if FOptionsDB <> Value then begin Byte(ChangedValues) := Byte(Value) xor Byte(FOptionsDB); FOptionsDB := Value; if modSyncMove in ChangedValues then if SyncMove and (FFocusedNode <> nil) then FFocusedNode.SyncPos; end; end; procedure TdxMasterView.SetOptionsView(Value: TdxMasterViewOptionsView); var ChangedValues: TdxMasterViewOptionsView; begin if FOptionsView <> Value then begin Word(ChangedValues) := Word(Value) xor Word(FOptionsView); FOptionsView := Value; if movAutoColumnWidth in ChangedValues then if not AutoColumnWidth and (movKeepColumnWidths in FOptionsView) then AssignColumnWidths else begin WidthChanged; Invalidate; end; if movDrawEndEllipsis in ChangedValues then Invalidate; if [movHideFocusRect, movHideSelection] * ChangedValues <> [] then InvalidateSelection; end; end; procedure TdxMasterView.SetScrollBars(Value: TdxMVScrollBars); begin if FScrollBars <> Value then begin FScrollBars := Value; RecreateWnd; end; end; procedure TdxMasterView.SetTopIndex(Value: Integer); begin Value := CheckTopIndex(Value); if TopIndex <> Value then if Value = -1 then TopNode := nil else TopNode := AbsoluteItems[Value]; end; procedure TdxMasterView.SetTopNode(Value: TdxMasterViewNode); begin if FTopNode <> Value then begin FTopNode := Value; if not IsDestroying then DoTopNodeChanged; end; end; procedure TdxMasterView.AddStyle(AStyle: TdxMasterViewStyle); begin FStyles.Add(AStyle); AStyle.FControl := Self; end; procedure TdxMasterView.RemoveStyle(AStyle: TdxMasterViewStyle); begin FStyles.Remove(AStyle); AStyle.FControl := nil; end; procedure TdxMasterView.AddSelectedItem(ANode: TdxMasterViewNode); begin FSelectedItems.Add(ANode); end; procedure TdxMasterView.RemoveSelectedItem(ANode: TdxMasterViewNode); begin FSelectedItems.Remove(ANode); end; procedure TdxMasterView.ClearSelectedItems; var I: Integer; begin if not MultiSelect then Exit; BeginSelection; try for I := SelectedItemCount - 1 downto 0 do SelectedItems[I].Selected := False; finally EndSelection; end; end; procedure ScrollTimerProc(Wnd: HWND; Msg: UINT; idEvent: UINT; Time: DWORD); stdcall; begin with TdxMasterView(FindControl(Wnd)) do begin case TdxMasterViewDirection(idEvent - ScrollLeftTimerId + 1) of dirLeft: Scroll(dirLeft); dirUp: begin FocusedIndex := FocusedIndex - 1; SelectPeriod(FocusedNode); end; dirRight: Scroll(dirRight); dirDown: begin FocusedIndex := FocusedIndex + 1; SelectPeriod(FocusedNode); end; end; end; end; procedure TdxMasterView.CreateScrollTimer(Direction: TdxMasterViewDirection); begin if FScrollTimer <> 0 then if TdxMasterViewDirection(FScrollTimer - ScrollLeftTimerId + 1) <> Direction then DestroyScrollTimer else Exit; FScrollTimer := SetTimer(Handle, ScrollLeftTimerId + Ord(Direction) - 1, ScrollTimeStep div (1 + Byte(Direction in [dirLeft, dirRight])), @ScrollTimerProc); end; procedure TdxMasterView.DestroyScrollTimer; begin if FScrollTimer <> 0 then begin KillTimer(Handle, FScrollTimer); FScrollTimer := 0; end; end; procedure TdxMasterView.WMCaptureChanged(var Message: TMessage); begin DestroyScrollTimer; if mvsSelectPeriod in FState then SelectPeriod(FSelectionAnchor); FSelectionAnchor := nil; FState := FState - [mvsCtrlClick, mvsIndentClick, mvsAnchorSelected, mvsSelectPeriod]; inherited; end; procedure TdxMasterView.WMDestroy(var Message: TMessage); begin Customizing := False; inherited; end; procedure TdxMasterView.WMEraseBkgnd(var Message: TWMEraseBkgnd); begin Message.Result := 1; { fillrect(Message.DC, ClientRect, createsolidbrush(clred)); Message.Result := 0; } end; procedure TdxMasterView.WMGetDlgCode(var Message: TWMGetDlgCode); begin Message.Result := DLGC_WANTARROWS or DLGC_WANTALLKEYS; end; procedure TdxMasterView.WMHScroll(var Message: TWMHScroll); var ScrollInfo: TScrollInfo; begin inherited; case Message.ScrollCode of SB_LINEUP: Scroll(dirLeft); SB_LINEDOWN: Scroll(dirRight); SB_PAGEUP: LeftPos := LeftPos - ClientWidth; SB_PAGEDOWN: LeftPos := LeftPos + ClientWidth; SB_THUMBTRACK: begin with ScrollInfo do begin cbSize := SizeOf(ScrollInfo); fMask := SIF_TRACKPOS; end; GetScrollInfo(Handle, SB_HORZ, ScrollInfo); LeftPos := ScrollInfo.nTrackPos; end; end; end; procedure TdxMasterView.WMKillFocus(var Message: TWMKillFocus); begin inherited; InvalidateSelection; end; procedure TdxMasterView.WMMouseActivate(var Message: TWMMouseActivate); begin inherited; if not IsDesigning and Enabled then SetFocus; end; procedure TdxMasterView.WMMouseWheel(var Message: TMessage); begin inherited; if SmallInt(HiWord(Message.wParam)) > 0 then Scroll(dirUp) else Scroll(dirDown); end; procedure TdxMasterView.WMNCCalcSize(var Message: TWMNCCalcSize); begin inherited; InflateRect(Message.CalcSize_Params^.rgrc[0], -1, -1); end; procedure TdxMasterView.WMNCHitTest(var Message: TWMNCHitTest); begin DefaultHandler(Message); FLastMousePos := SmallPointToPoint(Message.Pos); Windows.ScreenToClient(Handle, FLastMousePos); end; procedure TdxMasterView.WMNCPaint(var Message: TWMNCPaint); var R: TRect; DC: HDC; begin inherited; GetWindowRect(Handle, R); OffsetRect(R, -R.Left, -R.Top); DC := GetWindowDC(Handle); DrawEdge(DC, R, BDR_SUNKENOUTER, BF_RECT); if GetWindowLong(Handle, GWL_STYLE) and (WS_HSCROLL or WS_VSCROLL) = WS_HSCROLL or WS_VSCROLL then with R do begin Dec(Right); Dec(Bottom); Left := Right - GetSystemMetrics(SM_CXVSCROLL); Top := Bottom - GetSystemMetrics(SM_CYHSCROLL); FillRect(DC, R, COLOR_BTNFACE + 1); end; ReleaseDC(Handle, DC); end; procedure TdxMasterView.WMSetCursor(var Message: TWMSetCursor); var HitTestCode: TdxMasterViewHitTestCode; Node: TdxMasterViewNode; Column: TdxMasterViewColumn; RowIndex, ColIndex: Integer; ACursor: TCursor; begin HitTestCode := GetHitTestInfo(FLastMousePos, Node, Column, RowIndex, ColIndex); if (HitTestCode = htIndent) and not IsDesigning then ACursor := crdxMasterViewMirror else case HitTestCode of htHeaderRightEdge, htContentRightEdge: ACursor := crdxMasterViewHorSize; htHeaderTopEdge, htHeaderBottomEdge: ACursor := crdxMasterViewVerSize; else inherited; Exit; end; SetCursor(Screen.Cursors[ACursor]); end; procedure TdxMasterView.WMSetFocus(var Message: TWMSetFocus); begin InvalidateSelection; inherited; end; procedure TdxMasterView.WMSize(var Message: TWMSize); begin inherited; if AutoColumnWidth then CalcScrollableWidth(nil); end; procedure TdxMasterView.WMVScroll(var Message: TWMVScroll); var ScrollInfo: TScrollInfo; begin case Message.ScrollCode of SB_LINEUP: Scroll(dirUp); SB_LINEDOWN: Scroll(dirDown); SB_PAGEUP: ShowPrevPage(TopIndex, False); SB_PAGEDOWN: ShowNextPage(TopIndex + VisibleItemCount - 1, False); SB_THUMBTRACK: begin with ScrollInfo do begin cbSize := SizeOf(ScrollInfo); fMask := SIF_TRACKPOS; end; GetScrollInfo(Handle, SB_VERT, ScrollInfo); TopIndex := ScrollInfo.nTrackPos; end; end; end; procedure TdxMasterView.WMWindowPosChanged(var Message: TWMWindowPosChanged); begin inherited; if Message.WindowPos^.flags and SWP_HIDEWINDOW <> 0 then Customizing := False; end; procedure TdxMasterView.CMDesignHitTest(var Message: TCMDesignHitTest); var HitTestCode: TdxMasterViewHitTestCode; Node: TdxMasterViewNode; Column: TdxMasterViewColumn; RowIndex, ColIndex: Integer; begin HitTestCode := GetHitTestInfo(SmallPointToPoint(Message.Pos), Node, Column, RowIndex, ColIndex); Message.Result := Byte( (Message.Keys and MK_LBUTTON <> 0) and (HitTestCode in HeaderHitTests + [htExpandButton, htContentLeftEdge, htContentRightEdge]) or (Message.Keys and MK_MBUTTON <> 0)); end; procedure TdxMasterView.CMFontChanged(var Message: TMessage); var I: Integer; begin inherited; for I := 0 to AbsoluteLevelCount - 1 do AbsoluteLevels[I].FontsChanged; end; procedure TdxMasterView.CMSysColorChange(var Message: TMessage); var I: Integer; begin inherited; for I := 0 to StyleCount - 1 do Styles[I].SysColorChanged; end; procedure TdxMasterView.CreateParams(var Params: TCreateParams); begin inherited; with Params do begin if FScrollBars <> sbNone then Style := Style or WS_HSCROLL or WS_VSCROLL; end; end; procedure TdxMasterView.CreateWnd; begin inherited; SendMessage(Handle, CM_FONTCHANGED, 0, 0); end; procedure TdxMasterView.GetChildren(Proc: TGetChildProc; Root: TComponent); var I: Integer; begin for I := 0 to StyleCount - 1 do if Styles[I].Owner = Root then Proc(Styles[I]); for I := 0 to FLevels.Count - 1 do if FLevels[I].Owner = Root then Proc(FLevels[I]); end; procedure TdxMasterView.KeyDown(var Key: Word; Shift: TShiftState); var PrevFocusedNode, Node: TdxMasterViewNode; Level: TdxMasterViewLevel; I: Integer; begin inherited; PrevFocusedNode := FocusedNode; case Key of VK_ESCAPE: if GetCapture = Handle then ReleaseCapture; VK_UP: begin Node := GetPrevNode(FFocusedNode, (ssAlt in Shift) and (FFocusedNode <> nil)); if Node <> nil then Node.Focused := True; end; VK_DOWN: begin Node := GetNextNode(FFocusedNode, (ssAlt in Shift) and (FFocusedNode <> nil)); if Node <> nil then Node.Focused := True; end; VK_HOME: begin if (ssAlt in Shift) and (FFocusedNode <> nil) then Node := FFocusedNode.ParentNode.FirstInLevel[FFocusedNode.Level] else Node := nil; Node := GetNextNode(Node, Node <> nil); if Node <> nil then Node.Focused := True; end; VK_END: begin if (ssAlt in Shift) and (FFocusedNode <> nil) then Node := FFocusedNode.ParentNode.LastInLevel[FFocusedNode.Level] else Node := nil; Node := GetPrevNode(Node, Node <> nil); if Node <> nil then Node.Focused := True; end; VK_PRIOR: begin if (ssAlt in Shift) and (FFocusedNode <> nil) then Level := FFocusedNode.Level else Level := nil; Node := GetFirstNodeFromVisible(Level); I := TopIndex; if (Node <> nil) and (Node.AbsoluteIndex > I) then I := Node.AbsoluteIndex; if (TopIndex > 0) and ((FocusedIndex <= I) or (FocusedIndex > TopIndex + VisibleItemCount - 1)) then ShowPrevPage(FocusedIndex, Level <> nil); Node := GetFirstNodeFromVisible(Level); if Node <> nil then Node.Focused := True; end; VK_NEXT: begin if (ssAlt in Shift) and (FFocusedNode <> nil) then Level := FFocusedNode.Level else Level := nil; Node := GetLastNodeFromVisible(Level); I := TopIndex + VisibleItemCount - 1; if (Node <> nil) and (Node.AbsoluteIndex < I) then I := Node.AbsoluteIndex; if (FocusedIndex < TopIndex) or (FocusedIndex >= I) then ShowNextPage(FocusedIndex, Level <> nil); Node := GetLastNodeFromVisible(Level); if Node <> nil then Node.Focused := True; end; VK_ADD, VK_RIGHT: if (FocusedNode <> nil) and not FocusedNode.Expanded and FocusedNode.CanExpand then FocusedNode.Expanded := True else if Key = VK_RIGHT then Scroll(dirRight); VK_SUBTRACT, VK_LEFT: if (FocusedNode <> nil) and FocusedNode.Expanded then FocusedNode.Expanded := False else if Key = VK_LEFT then Scroll(dirLeft); VK_MULTIPLY: if FocusedNode <> nil then { if FocusedNode.Expanded then FocusedNode.Collapse(True) else} FocusedNode.Expand(True); VK_SPACE: if MultiSelect and (FocusedNode <> nil) then with FocusedNode do Selected := not Selected; VK_INSERT: begin if FocusedNode = nil then if FLevels.Count = 0 then Level := nil else Level := FLevels[0] else Level := FocusedNode.Level; //!!! if (Level <> nil) and Level.CanInsert then begin ClearSelection; Level.DataSet.Insert; end; end; VK_DELETE: if (Shift = []) or (Shift = [ssCtrl]) then DeleteSelection; end; if MultiSelect and (AbsoluteItemCount <> 0) and (FocusedNode <> PrevFocusedNode) and (Key in [VK_UP, VK_DOWN, VK_HOME, VK_END, VK_PRIOR, VK_NEXT]) then if ssShift in Shift then begin if FSelectionAnchor = nil then if PrevFocusedNode = nil then FSelectionAnchor := AbsoluteItems[0] else FSelectionAnchor := PrevFocusedNode; BeginSelection; try ClearSelection; SelectItems(FSelectionAnchor.AbsoluteIndex, FocusedIndex, True); finally EndSelection; end; end else begin FSelectionAnchor := nil; if not (ssCtrl in Shift) then begin BeginSelection; try ClearSelection; if FocusedNode <> nil then FocusedNode.Selected := True; finally EndSelection; end; end; end; end; procedure TdxMasterView.Loaded; begin inherited; try if not IsDesigning then begin if mobStoreInIniFile in FOptionsBehavior then LoadFromRegIniFile(FIniFileName, riIniFile); if mobStoreInRegistry in FOptionsBehavior then LoadFromRegIniFile(FRegistryPath, riRegistry); end; finally UpdateData; end; end; procedure TdxMasterView.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); const NonDragableHitTests = HeaderHitTests + [htNone, htLeftSpace, htRightSpace, htGroupByBox, htExpandButton, htContentRightEdge, htFooter]; DragSources: array[Boolean] of TdxMasterViewDragSource = (dsHeader, dsGroupByBox); var HitTestCode: TdxMasterViewHitTestCode; Node, PrevFocusedNode: TdxMasterViewNode; Column: TdxMasterViewColumn; PrevFocusedIndex, RowIndex, ColIndex: Integer; NewSortOrder: TdxMasterViewSortOrder; LockSelection, PrevSelected: Boolean; begin inherited; HitTestCode := GetHitTestInfo(Point(X, Y), Node, Column, RowIndex, ColIndex); if (Button = mbLeft) or (Button = mbRight) and (HitTestCode in [htContent, htContentLeftEdge, htPreview]) then begin if Node <> nil then begin case HitTestCode of htHeader: if Column <> nil then if not (ssDouble in Shift) then begin Column.Pressed := True; case DragDetect(Handle) of ddDrag: try DoColumnMoving(Node, Column, Point(X, Y), DragSources[RowIndex = -1]); finally Column.Pressed := False; end; ddNone: with Column do begin Pressed := False; if CanSorting then begin if ssCtrl in Shift then NewSortOrder := soNone else if SortOrder in [soNone, soDescending] then NewSortOrder := soAscending else NewSortOrder := soDescending; if [ssCtrl, ssShift] * Shift = [] then with Level do begin BeginSorting; ClearSorting(Column); end; SortOrder := NewSortOrder; if [ssCtrl, ssShift] * Shift = [] then Level.EndSorting; Modified; end; end; else Column.Pressed := False; end; end else else if ssDouble in Shift then Node.Level.RemoveFreeSpace; htHeaderRightEdge: if Column.Level.Horizontal then if ssDouble in Shift then Column.ApplyBestFit else DoColumnHorSizing(Column, True) else if ssDouble in Shift then begin Column.Level.Layout.HeaderWidthAssigned[Column.ColIndex] := False; Modified; end else DoColumnHorSizing(Column, False); htHeaderTopEdge, htHeaderBottomEdge: if not (ssDouble in Shift) then DoColumnVerSizing(Node, Column, HitTestCode); htIndent, htContent, htContentLeftEdge, htPreview, htOther: begin Shift := Shift - [ssLeft, ssRight]; PrevFocusedIndex := FocusedIndex; PrevFocusedNode := FocusedNode; if ssDouble in Shift then begin FocusedIndex := Node.AbsoluteIndex; if lobDblClkExpanding in Node.Level.OptionsBehavior then with Node do Expanded := not Expanded; end else if MultiSelect then if (Shift = []) or (Shift = [ssCtrl]) then begin FocusedIndex := Node.AbsoluteIndex; LockSelection := (Shift = []) and (SelectedItemCount > 0) and (not Node.Selected or (Button <> mbRight) and (DragMode = dmManual)); if LockSelection then begin BeginSelection; ClearSelection; end; try FSelectionAnchor := Node; PrevSelected := Node.Selected; Node.Selected := True; finally if LockSelection then EndSelection; end; if Shift = [ssCtrl] then Include(FState, mvsCtrlClick); if HitTestCode = htIndent then Include(FState, mvsIndentClick); if (FState * [mvsCtrlClick, mvsIndentClick] <> []) and not PrevSelected then Include(FState, mvsAnchorSelected); if (Button = mbLeft) and (HitTestCode <> htIndent) and (DragMode = dmAutomatic) then begin if DragDetect(Handle) = ddDrag then BeginDrag(False) else if (Shift = []) and (Button <> mbRight) and PrevSelected and (SelectedItemCount > 1) then begin BeginSelection; try ClearSelection; Node.Selected := True; finally EndSelection; end; end; Exit; end; if FState * [mvsCtrlClick, mvsIndentClick] <> [] then SetCapture(Handle); end else begin FocusedIndex := Node.AbsoluteIndex; if Shift = [ssShift] then begin if FSelectionAnchor = nil then if PrevFocusedNode = nil then FSelectionAnchor := AbsoluteItems[0] else FSelectionAnchor := PrevFocusedNode; BeginSelection; try ClearSelection; SelectItems(FSelectionAnchor.AbsoluteIndex, Node.AbsoluteIndex, True); finally EndSelection; end; end else if Shift = [ssCtrl, ssShift] then SelectItems(PrevFocusedIndex, Node.AbsoluteIndex, True) else begin FSelectionAnchor := nil; BeginSelection; try ClearSelection; Node.Selected := True; finally EndSelection; end; end; end else begin if (Shift = []) and (HitTestCode = htIndent) then begin Include(FState, mvsIndentClick); SetCapture(Handle); end; FocusedIndex := Node.AbsoluteIndex; if (Button = mbLeft) and (HitTestCode <> htIndent) and (DragMode = dmAutomatic) and (DragDetect(Handle) = ddDrag) then BeginDrag(False); end; end; htExpandButton: Node.Expanded := not Node.Expanded; htContentRightEdge: if not Column.Level.Horizontal then if ssDouble in Shift then Column.ApplyBestFit else DoColumnHorSizing(Column, True); end; end end else begin ReleaseCapture; if Button = mbMiddle then DoScrolling; end; end; procedure TdxMasterView.MouseMove(Shift: TShiftState; X, Y: Integer); var Node: TdxMasterViewNode; Column: TdxMasterViewColumn; RowIndex, ColIndex: Integer; begin inherited; GetHitTestInfo(Point(X, Y), Node, Column, RowIndex, ColIndex); if not (mvsSelectPeriod in FState) and (mvsIndentClick in FState) then begin Include(FState, mvsSelectPeriod); FLastSelectedNode := FSelectionAnchor; end; if mvsSelectPeriod in FState then begin if (Node = nil) and (Y < 0) or (Node = TopNode) and (Y < Node.ContentBounds.Top) then CreateScrollTimer(dirUp) else if (Node = nil) and (Y >= ClientHeight) or (Y >= AbsoluteItems[TopIndex + VisibleItemCount - 1].ContentBounds.Bottom) then CreateScrollTimer(dirDown) else if X < 0 then CreateScrollTimer(dirLeft) else if X >= ClientWidth then CreateScrollTimer(dirRight) else DestroyScrollTimer; if Node <> nil then begin FocusedIndex := Node.AbsoluteIndex; SelectPeriod(Node); end; end; end; procedure TdxMasterView.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var Node: TdxMasterViewNode; Column: TdxMasterViewColumn; RowIndex, ColIndex: Integer; begin inherited; if (GetCapture = Handle) and not (mvsScrolling in FState) then begin if mvsSelectPeriod in FState then begin Exclude(FState, mvsSelectPeriod); if MultiSelect then SelectItems(FSelectionAnchor.AbsoluteIndex, FLastSelectedNode.AbsoluteIndex, True); end else begin GetHitTestInfo(Point(X, Y), Node, Column, RowIndex, ColIndex); if (mvsCtrlClick in FState) and (Node = FSelectionAnchor) and FSelectionAnchor.Selected and not (mvsAnchorSelected in FState) then FSelectionAnchor.Selected := False; end; ReleaseCapture; end; end; procedure TdxMasterView.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation = opRemove) and (AComponent is TdxMasterViewStyle) then begin if HighlightStyle = AComponent then HighlightStyle := nil; if InactiveStyle = AComponent then InactiveStyle := nil; end; end; procedure TdxMasterView.Paint; var DC: HDC; R: TRect; FromIndex, AVisibleItemCount, APartVisibleItemCount, I: Integer; (* var ft,lt{,c}:integer; *) begin if FUpdateLockCount > 0 then Exit; //ft:=gettickcount; //for c:=1 to 10 do //begin FPainting := True; try FromIndex := TopIndex; SetScrollBarsInfo(AVisibleItemCount, APartVisibleItemCount); BeforePaint; for I := 0 to AbsoluteLevelCount - 1 do AbsoluteLevels[I].BeforePaint; try DC := Canvas.Handle; R := ClientRect; if APartVisibleItemCount <> 0 then begin for I := FromIndex to FromIndex + APartVisibleItemCount - 1 do AbsoluteItems[I].Draw(DC); R.Top := AbsoluteItems[FromIndex + APartVisibleItemCount - 1].Bounds.Bottom; FillRect(DC, R, Brush.Handle); end else FillRect(DC, R, Brush.Handle); finally for I := 0 to AbsoluteLevelCount - 1 do AbsoluteLevels[I].AfterPaint; AfterPaint; LeftPos := LeftPos; // for checking LeftPos TopIndex := TopIndex; // for checking TopIndex end; finally FPainting := False; end; //end; {lt:=gettickcount; application.mainform.caption :=inttostr(lt-ft);} end; procedure TdxMasterView.SetName(const NewName: TComponentName); var OldName: TComponentName; procedure RenameComponents(Mode: Integer); var I: Integer; Component: TComponent; ComponentName, NamePrefix: TComponentName; begin if Mode = 1 then I := StyleCount else I := AbsoluteLevelCount; for I := 0 to I - 1 do begin if Mode = 1 then Component := Styles[I] else Component := AbsoluteLevels[I]; if Component.Owner = Owner then begin ComponentName := Component.Name; if Length(ComponentName) > Length(OldName) then begin NamePrefix := Copy(ComponentName, 1, Length(OldName)); if CompareText(OldName, NamePrefix) = 0 then begin System.Delete(ComponentName, 1, Length(OldName)); System.Insert(NewName, ComponentName, 1); try Component.Name := ComponentName; except on EComponentError do {Ignore rename errors }; end; end; end; end; end; end; begin OldName := Name; inherited; { In design mode the name of the columns should track the level name } if IsDesigning and (Name <> OldName) then begin RenameComponents(1); RenameComponents(2); end; if dxMVDesigner <> nil then dxMVDesigner.Changed(Self, [rcName]); end; procedure TdxMasterView.WndProc(var Message: TMessage); begin with TWMMouse(Message) do if (DragMode = dmAutomatic) and not IsDesigning and not Dragging and ((Msg = WM_LBUTTONDOWN) or (Msg = WM_LBUTTONDBLCLK)) then begin if not IsControlMouseMsg(TWMMouse(Message)) then begin ControlState := ControlState + [csLButtonDown]; Dispatch(Message); end; Exit; end; inherited; end; procedure TdxMasterView.BeginAnimation; begin FAnimation := True; ValidateRect(Handle, nil); end; procedure TdxMasterView.EndAnimation; begin FAnimation := False; end; procedure TdxMasterView.BeforePaint; begin FBtnHighlightPen := CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT)); FBtnShadowPen := CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); end; procedure TdxMasterView.AfterPaint; begin DeleteObject(FBtnShadowPen); DeleteObject(FBtnHighlightPen); end; procedure TdxMasterView.BeginTopNodeChange; begin if FTopNodeChangeLockCount = 0 then FPrevTopNode := FTopNode; Inc(FTopNodeChangeLockCount); end; procedure TdxMasterView.EndTopNodeChange; begin if FTopNodeChangeLockCount > 0 then begin Dec(FTopNodeChangeLockCount); if (FTopNodeChangeLockCount = 0) and (FTopNode <> FPrevTopNode) then DoTopNodeChanged; end; end; procedure TdxMasterView.CalcScrollableWidth(CallingLevel: TdxMasterViewLevel); var PrevScrollableWidth, I, Size: Integer; function IsLocked: Boolean; var I: Integer; begin Result := True; for I := 0 to AbsoluteLevelCount - 1 do if AbsoluteLevels[I].FAssignWidthsLockCount <> 0 then Exit; Result := False; end; begin if FCalculatingScrollableWidth or IsLocked then Exit; FCalculatingScrollableWidth := True; try PrevScrollableWidth := FScrollableWidth; FScrollableWidth := 0; for I := 0 to AbsoluteLevelCount - 1 do with AbsoluteLevels[I] do if Visible then begin Size := Indent + Layout.GetVisibleWidth; if Size > FScrollableWidth then FScrollableWidth := Size; end; if AutoColumnWidth and (FScrollableWidth <> PrevScrollableWidth) then for I := 0 to AbsoluteLevelCount - 1 do with AbsoluteLevels[I] do if (AbsoluteLevels[I] <> CallingLevel) and Visible then WidthChanged; finally FCalculatingScrollableWidth := False; end; end; procedure TdxMasterView.CalcViewInfo(ATopIndex: Integer; AHeight: Integer; var AVisibleItemCount, APartVisibleItemCount: Integer; CalcNodesViewInfo: Boolean); var Indents, Widths: PIntArray; I, J, FooterSize: Integer; R: TRect; Node: TdxMasterViewNode; PartialVisibility: Boolean; begin if ATopIndex = -1 then ATopIndex := 0; AVisibleItemCount := 0; APartVisibleItemCount := 0; GetMem(Indents, AbsoluteLevelCount * SizeOf(Integer)); GetMem(Widths, AbsoluteLevelCount * SizeOf(Integer)); try for I := 0 to AbsoluteLevelCount - 1 do with AbsoluteLevels[I] do begin FFirstVisibleNode := nil; FFirstVisibleNodeWithData := nil; Widths[I] := VisibleWidth; Indents[I] := Indent - LeftPos; FPreviewDC := GetDC(Handle); FPreviewPrevFont := SelectObject(FPreviewDC, PreviewFont.Handle); end; Windows.GetClientRect(Handle, R); if AHeight = -1 then AHeight := ClientHeight; if CalcNodesViewInfo then begin FItems.FViewInfo.Bounds.Right := R.Right; { for I := 0 to ATopIndex - 1 do with FAbsoluteItems.List^[I] do begin ClearViewInfo; FViewInfo.Bounds.Right := R.Right; end;} end; for I := ATopIndex to AbsoluteItemCount - 1 do begin Node := FAbsoluteItems.List^[I]; with Node, Level do begin if (FFirstVisibleNode = nil) and (NodeType <> ntCaption) then FFirstVisibleNode := Node; if (FFirstVisibleNodeWithData = nil) and (NodeType = ntData) then FFirstVisibleNodeWithData := Node; J := CalcViewInfo(ATopIndex, R, Indents[AbsoluteIndex], Widths[AbsoluteIndex], FooterSize, CalcNodesViewInfo); end; PartialVisibility := R.Top + J > AHeight; Inc(R.Top, J); if R.Top >= AHeight then begin APartVisibleItemCount := I - ATopIndex + 1; AVisibleItemCount := APartVisibleItemCount - Byte(PartialVisibility); if AVisibleItemCount = 0 then AVisibleItemCount := 1; Exit; end; end; APartVisibleItemCount := AbsoluteItemCount - ATopIndex; AVisibleItemCount := APartVisibleItemCount; finally (* if CalcNodesViewInfo then for I := ATopIndex + APartVisibleItemCount to AbsoluteItemCount - 1 do FAbsoluteItems.List^[I].ClearViewInfo;*) for I := 0 to AbsoluteLevelCount - 1 do with AbsoluteLevels[I] do begin SelectObject(FPreviewDC, FPreviewPrevFont); ReleaseDC(Handle, FPreviewDC); FPreviewDC := 0; end; FreeMem(Widths); FreeMem(Indents); if CalcNodesViewInfo then begin FLastVisibleItemCount := AVisibleItemCount; FLastPartVisibleItemCount := APartVisibleItemCount; end; end; end; procedure TdxMasterView.AssignColumnWidths; var I: Integer; begin for I := 0 to AbsoluteLevelCount - 1 do AbsoluteLevels[I].AssignColumnWidths; end; function TdxMasterView.GetAnimatableHeight: Integer; begin Result := ClientHeight; end; procedure TdxMasterView.SetScrollBarsInfo( var AVisibleItemCount, APartVisibleItemCount: Integer); var AStyle, HorSize, VerSize: Integer; HasHorScrollBar, HasVerScrollBar: Boolean; ScrollInfo: TScrollInfo; function AlwaysShowHorScrollBar: Boolean; begin Result := (FScrollBars = sbHorizontal) or (FScrollBars = sbBoth); end; function AlwaysShowVerScrollBar: Boolean; begin Result := (FScrollBars = sbVertical) or (FScrollBars = sbBoth); end; function GetHasHorScrollBar: Boolean; begin Result := (FScrollBars <> sbNone) and (AlwaysShowHorScrollBar or (ScrollableWidth > HorSize)); end; function GetHasVerScrollBar: Boolean; begin Result := (FScrollBars <> sbNone) and (AlwaysShowVerScrollBar or (AbsoluteItemCount > AVisibleItemCount)); end; begin // sbAuto, sbHorizontal, sbVertical, sbBoth, sbNone AStyle := GetWindowLong(Handle, GWL_STYLE); HorSize := ClientWidth; if AStyle and WS_VSCROLL <> 0 then Inc(HorSize, GetSystemMetrics(SM_CXVSCROLL)); VerSize := ClientHeight; if AStyle and WS_HSCROLL <> 0 then Inc(VerSize, GetSystemMetrics(SM_CYHSCROLL)); HasHorScrollBar := GetHasHorScrollBar; if HasHorScrollBar then Dec(VerSize, GetSystemMetrics(SM_CYHSCROLL)); CalcViewInfo(TopIndex, VerSize, AVisibleItemCount, APartVisibleItemCount, True); HasVerScrollBar := GetHasVerScrollBar; if HasVerScrollBar then begin Dec(HorSize, GetSystemMetrics(SM_CXVSCROLL)); if not HasHorScrollBar then begin HasHorScrollBar := GetHasHorScrollBar; if HasHorScrollBar then begin Dec(VerSize, GetSystemMetrics(SM_CYHSCROLL)); CalcViewInfo(TopIndex, VerSize, AVisibleItemCount, APartVisibleItemCount, True); end; end; end; if FScrollBars <> sbNone then begin with ScrollInfo do begin cbSize := SizeOf(ScrollInfo); fMask := SIF_ALL; if AlwaysShowHorScrollBar then fMask := fMask or SIF_DISABLENOSCROLL; nMin := 0; nMax := ScrollableWidth - 1; nPage := HorSize; nPos := LeftPos; end; SetScrollInfo(Handle, SB_HORZ, ScrollInfo, True); with ScrollInfo do begin fMask := SIF_ALL; if AlwaysShowVerScrollBar then fMask := fMask or SIF_DISABLENOSCROLL; nMin := 0; nMax := AbsoluteItemCount - 1; nPage := AVisibleItemCount; nPos := TopIndex; end; SetScrollInfo(Handle, SB_VERT, ScrollInfo, True); end; end; procedure TdxMasterView.WidthChanged; var I: Integer; begin for I := 0 to AbsoluteLevelCount - 1 do AbsoluteLevels[I].WidthChanged; end; function TdxMasterView.CanScroll(Direction: TdxMasterViewDirection): Boolean; begin case Direction of dirLeft: Result := LeftPos <> 0; dirRight: Result := LeftPos + ClientWidth < ScrollableWidth; dirUp: Result := TopIndex <> 0; dirDown: Result := TopIndex + VisibleItemCount < AbsoluteItemCount; else Result := False; end; end; function TdxMasterView.CheckLeftPos(Value: Integer): Integer; begin Result := Value; if HandleAllocated then begin Value := ScrollableWidth - ClientWidth; if Result > Value then Result := Value; end; if Result < 0 then Result := 0; end; function TdxMasterView.CheckTopIndex(Value: Integer): Integer; var FirstCall: Boolean; AVisibleItemCount, APartVisibleItemCount: Integer; begin Result := Value; if HandleAllocated and (Result > 0) then begin Value := AbsoluteItemCount - 1; FirstCall := True; repeat if FirstCall and FPainting then AVisibleItemCount := FLastVisibleItemCount else CalcViewInfo(Result, -1, AVisibleItemCount, APartVisibleItemCount, False); if Value > Result + AVisibleItemCount - 1 then begin if not FirstCall then Inc(Result); Break; end; FirstCall := False; Dec(Result); if Result = -1 then Break; until False; end; if Result < 0 then if AbsoluteItemCount > 0 then Result := 0 else Result := -1; if Result >= AbsoluteItemCount then Result := AbsoluteItemCount - 1; end; procedure TdxMasterView.CheckFocusedNode; var NonExpandedNode, Node: TdxMasterViewNode; begin if FocusedNode = nil then Exit; //!!! NonExpandedNode := nil; Node := FocusedNode; while Node.ParentNode <> nil do begin if not Node.Expanded then NonExpandedNode := Node; Node := Node.ParentNode; end; if NonExpandedNode <> nil then if CanFocusNode(NonExpandedNode) then NonExpandedNode.Focused := True else FocusedNode := nil; end; procedure TdxMasterView.DestroyStyles; begin while StyleCount <> 0 do Styles[0].Free; end; procedure TdxMasterView.DoAfterUpdateData(Level: TdxMasterViewLevel); begin if Assigned(FOnAfterUpdateData) then FOnAfterUpdateData(Self, Level); end; procedure TdxMasterView.DoBeforeUpdateData(Level: TdxMasterViewLevel); begin if Assigned(FOnBeforeUpdateData) then FOnBeforeUpdateData(Self, Level); end; procedure TdxMasterView.DoColumnMoving(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; const InitialP: TPoint; DragSource: TdxMasterViewDragSource); const ArrowWidth = 11; ArrowHeight = 9; Cursors: array[Boolean] of Integer = (crdxMasterViewNoDrop, crdxMasterViewRemove); type PColors = ^TColors; TColors = array[0..MaxInt - 1] of Byte; var Level: TdxMasterViewLevel; PrevP, P, Offset: TPoint; R: TRect; W, H, TempBitmapW, TempBitmapH, AColIndex, ARowIndex, ActiveRowIndex, ActiveColIndex: Integer; DC, AlphaBlendDC, TempDC, SaveDC, BDC, SaveArrowsDC: HDC; AlphaBlendBitmap, PrevAlphaBlendBitmap, TempBitmap, SaveB, B, SaveArrowsB: HBITMAP; BI: TBitmapInfo; DColors, SColors: PColors; ArrowsBrush: HBRUSH; Accept, Success, AForceMove, ActiveForceMove, AInsert, ActiveInsert: Boolean; CaptureWnd: HWND; Msg: TMsg; HitTestCode: TdxMasterViewHitTestCode; ActiveNode, ANode: TdxMasterViewNode; AColumn: TdxMasterViewColumn; procedure PrepareAlphaBlend; begin with BI.bmiHeader do begin biSize := SizeOf(BI.bmiHeader); biWidth := W; biHeight := -H; biPlanes := 1; biBitCount := 32; biCompression := BI_RGB; end; AlphaBlendDC := CreateCompatibleDC(DC); AlphaBlendBitmap := CreateCompatibleBitmap(DC, W, H); PrevAlphaBlendBitmap := SelectObject(AlphaBlendDC, AlphaBlendBitmap); TempBitmapW := W; TempBitmapH := H; TempDC := CreateCompatibleDC(DC); TempBitmap := CreateCompatibleBitmap(DC, TempBitmapW, TempBitmapH); TempBitmap := SelectObject(TempDC, TempBitmap); GetMem(DColors, 4 * W * H); GetMem(SColors, 4 * W * H); B := SelectObject(BDC, B); GetDIBits(DC, B, 0, H, SColors, BI, DIB_RGB_COLORS); B := SelectObject(BDC, B); end; procedure UnprepareAlphaBlend; begin FreeMem(SColors, 4 * W * H); FreeMem(DColors, 4 * W * H); DeleteObject(SelectObject(TempDC, TempBitmap)); DeleteDC(TempDC); DeleteObject(SelectObject(AlphaBlendDC, PrevAlphaBlendBitmap)); DeleteDC(AlphaBlendDC); end; procedure DrawDraggingColumn(P: TPoint); var SaveR, R, R1, R2: TRect; CurOffset: TPoint; Rgn, Rgn1, Rgn2: HRGN; procedure MakeAlphaBlend; const AlphaConst = 127; var I, J, K: Integer; begin GetDIBits(DC, AlphaBlendBitmap, 0, H, DColors, BI, DIB_RGB_COLORS); for I := 0 to W - 1 do for J := 0 to H - 1 do begin K := (J * W + I) * 4; Inc(DColors^[K], MulDiv(AlphaConst, SColors^[K] - DColors^[K], 255)); Inc(DColors^[K + 1], MulDiv(AlphaConst, SColors^[K + 1] - DColors^[K + 1], 255)); Inc(DColors^[K + 2], MulDiv(AlphaConst, SColors^[K + 2] - DColors^[K + 2], 255)); end; SetDIBits(DC, AlphaBlendBitmap, 0, H, DColors, BI, DIB_RGB_COLORS); end; function GetTempBitmapBounds: TRect; begin with Result do begin if P.X < PrevP.X then begin Left := P.X; Right := PrevP.X; end else begin Left := PrevP.X; Right := P.X; end; if P.Y < PrevP.Y then begin Top := P.Y; Bottom := PrevP.Y; end else begin Top := PrevP.Y; Bottom := P.Y; end; Inc(Right, W); Inc(Bottom, H); end; end; procedure CheckTempBitmapSize(const R: TRect); var RequireRecreate: Boolean; begin RequireRecreate := False; if R.Right - R.Left > TempBitmapW then RequireRecreate := True else if R.Bottom - R.Top > TempBitmapH then RequireRecreate := True; if RequireRecreate then begin DeleteObject(SelectObject(TempDC, TempBitmap)); TempBitmapW := R.Right - R.Left; TempBitmapH := R.Bottom - R.Top; TempBitmap := CreateCompatibleBitmap(DC, TempBitmapW, TempBitmapH); TempBitmap := SelectObject(TempDC, TempBitmap); end; end; begin if P.X = MaxInt then BitBlt(DC, PrevP.X - Offset.X, PrevP.Y - Offset.Y, W, H, SaveDC, 0, 0, SRCCOPY) else begin Dec(P.X, Offset.X); Dec(P.Y, Offset.Y); if PrevP.X = MaxInt then begin BitBlt(SaveDC, 0, 0, W, H, DC, P.X, P.Y, SRCCOPY); if TransparentDragAndDrop then begin BitBlt(AlphaBlendDC, 0, 0, W, H, DC, P.X, P.Y, SRCCOPY); MakeAlphaBlend; BitBlt(DC, P.X, P.Y, W, H, AlphaBlendDC, 0, 0, SRCCOPY); end; end else begin Dec(PrevP.X, Offset.X); Dec(PrevP.Y, Offset.Y); if TransparentDragAndDrop then begin R := GetTempBitmapBounds; CheckTempBitmapSize(R); with R do begin BitBlt(TempDC, 0, 0, R.Right - R.Left, R.Bottom - R.Top, DC, Left, Top, SRCCOPY); BitBlt(TempDC, PrevP.X - Left, PrevP.Y - Top, W, H, SaveDC, 0, 0, SRCCOPY); BitBlt(SaveDC, 0, 0, W, H, TempDC, P.X - Left, P.Y - Top, SRCCOPY); BitBlt(AlphaBlendDC, 0, 0, W, H, SaveDC, 0, 0, SRCCOPY); MakeAlphaBlend; BitBlt(TempDC, P.X - Left, P.Y - Top, W, H, AlphaBlendDC, 0, 0, SRCCOPY); BitBlt(DC, Left, Top, Right - Left, Bottom - Top, TempDC, 0, 0, SRCCOPY); end; end else begin SaveR := Bounds(PrevP.X, PrevP.Y, W, H); R := Bounds(P.X, P.Y, W, H); Rgn := CreateRectRgn(0, 0, 0, 0); Rgn1 := CreateRectRgnIndirect(SaveR); Rgn2 := CreateRectRgnIndirect(R); CombineRgn(Rgn, Rgn1, Rgn2, RGN_DIFF); SelectClipRgn(DC, Rgn); BitBlt(DC, PrevP.X, PrevP.Y, W, H, SaveDC, 0, 0, SRCCOPY); SelectClipRgn(DC, 0); R1 := Rect(0, 0, W, H); R2 := R1; CurOffset := Point(P.X - PrevP.X, P.Y - PrevP.Y); OffsetRect(R2, -CurOffset.X, -CurOffset.Y); with R2 do BitBlt(SaveDC, Left, Top, Right - Left, Bottom - Top, SaveDC, 0, 0, SRCCOPY); CombineRgn(Rgn, Rgn2, Rgn1, RGN_DIFF); OffsetRgn(Rgn, -P.X, -P.Y); SelectClipRgn(SaveDC, Rgn); BitBlt(SaveDC, 0, 0, W, H, DC, P.X, P.Y, SRCCOPY); SelectClipRgn(SaveDC, 0); DeleteObject(Rgn2); DeleteObject(Rgn1); DeleteObject(Rgn); end; end; if not TransparentDragAndDrop then BitBlt(DC, P.X, P.Y, W, H, BDC, 0, 0, SRCCOPY); Inc(P.X, Offset.X); Inc(P.Y, Offset.Y); end; PrevP := P; end; procedure SetActivePlace(NodeValue: TdxMasterViewNode; ARowIndex, AColIndex: Integer; AForceMove, AInsert: Boolean); procedure CalcArrowsRects(var TopArrowRect, BottomArrowRect: TRect); begin if ActiveRowIndex = -1 then // GroupBy Box begin TopArrowRect := ActiveNode.GroupByBoxColumnBounds[ActiveColIndex - Byte(ActiveColIndex = Level.GroupColumnCount)]; if ActiveColIndex = 0 then Dec(TopArrowRect.Left, GroupByBoxHorOffset div 2) else if ActiveColIndex = Level.GroupColumnCount then TopArrowRect.Left := TopArrowRect.Right + GroupByBoxHorOffset div 2 else OffsetRect(TopArrowRect, -GroupByBoxHorOffset div 2, -(Level.HeaderRealHeight div 2 + GroupByBoxVerOffset) div 2); with TopArrowRect do begin if Left < 0 then Left := 0; if Left >= ClientWidth then Left := ClientWidth - 1; end; TopArrowRect.Right := TopArrowRect.Left + 1; InflateRect(TopArrowRect, ArrowWidth div 2, ArrowHeight); MapWindowPoints(Handle, 0, TopArrowRect, 2); BottomArrowRect := TopArrowRect; with TopArrowRect do Bottom := Top + ArrowHeight; with BottomArrowRect do Top := Bottom - ArrowHeight; end else if Level.Horizontal then begin TopArrowRect := ActiveNode.HeaderRowBounds[ActiveRowIndex]; if ActiveInsert then begin with TopArrowRect do begin if Left < 0 then Left := 0; if Right > ClientWidth then Right := ClientWidth; if ActiveRowIndex <> 0 then Dec(Top); Bottom := Top + 1; InflateRect(TopArrowRect, -(Right - Left) div 4, ArrowWidth div 2); end; MapWindowPoints(Handle, 0, TopArrowRect, 2); BottomArrowRect := TopArrowRect; with TopArrowRect do Right := Left + ArrowHeight; with BottomArrowRect do Left := Right - ArrowHeight; end else begin with Level, TopArrowRect do if ActiveColIndex > 0 then Left := Layout.Columns[ActiveRowIndex, ActiveColIndex - 1]. GetHeaderBounds(ActiveNode).Right; with TopArrowRect do begin if Left < 0 then Left := 0; if Left >= ClientWidth then Left := ClientWidth - 1; Right := Left + 1; end; InflateRect(TopArrowRect, ArrowWidth div 2, ArrowHeight); MapWindowPoints(Handle, 0, TopArrowRect, 2); BottomArrowRect := TopArrowRect; with TopArrowRect do Bottom := Top + ArrowHeight; with BottomArrowRect do Top := Bottom - ArrowHeight; end; end else begin if (ActiveColIndex = Level.Layout.Count) (*or (ActiveRowIndex = Level.Layout[ActiveColIndex].Count) and (ActiveRowIndex = 0){!?}*) then with TopArrowRect do begin with Level.Layout do begin Left := ColOffsets[ActiveColIndex]; Right := Left + FMovingColumn.Width; end; Top := ActiveNode.ContentBounds.Top; Bottom := Top + 1; end else if ActiveRowIndex = Level.Layout[ActiveColIndex].Count then begin TopArrowRect := Level.Layout.Columns[ActiveRowIndex - 1, ActiveColIndex].GetHeaderBounds(ActiveNode); with TopArrowRect do begin Top := Bottom; Inc(Bottom); end; end else begin TopArrowRect := Level.Layout.Columns[ActiveRowIndex, ActiveColIndex].GetHeaderBounds(ActiveNode); with TopArrowRect do Bottom := Top + 1; end; with TopArrowRect do begin if Left < 0 then Left := 0; if Left >= ClientWidth then Left := ClientWidth - 1; if Right < 0 then Right := 0; if Right >= ClientWidth then Right := ClientWidth - 1; if ActiveInsert then begin Right := Left + 1; Bottom := ActiveNode.ContentBoundsWithoutPreview.Bottom; InflateRect(TopArrowRect, ArrowWidth div 2, ArrowHeight) end else InflateRect(TopArrowRect, ArrowHeight, ArrowWidth div 2); end; MapWindowPoints(Handle, 0, TopArrowRect, 2); BottomArrowRect := TopArrowRect; if ActiveInsert then begin with TopArrowRect do Bottom := Top + ArrowHeight; with BottomArrowRect do Top := Bottom - ArrowHeight; end else begin with TopArrowRect do Right := Left + ArrowHeight; with BottomArrowRect do Left := Right - ArrowHeight; end; end; end; procedure HideArrows; var R1, R2: TRect; begin CalcArrowsRects(R1, R2); with R1 do BitBlt(DC, Left, Top, Right - Left, Bottom - Top, SaveArrowsDC, 0, 0, SRCCOPY); with R2 do BitBlt(DC, Left, Top, Right - Left, Bottom - Top, SaveArrowsDC, 0, ArrowWidth, SRCCOPY); end; procedure ShowArrows; var R1, R2: TRect; P: array[1..7] of TPoint; PrevPen: HPEN; PrevBrush: HBRUSH; begin CalcArrowsRects(R1, R2); with R1 do BitBlt(SaveArrowsDC, 0, 0, Right - Left, Bottom - Top, DC, Left, Top, SRCCOPY); with R2 do BitBlt(SaveArrowsDC, 0, ArrowWidth, Right - Left, Bottom - Top, DC, Left, Top, SRCCOPY); PrevPen := SelectObject(DC, GetStockObject(BLACK_PEN)); PrevBrush := SelectObject(DC, ArrowsBrush); if (ActiveRowIndex <> -1) and // not GroupBy Box (Level.Horizontal and ActiveInsert or not Level.Horizontal and not ActiveInsert) then begin with R1 do begin P[1] := Point(Left, Top + 3); P[2] := Point(Left + 3, Top + 3); P[3] := Point(Left + 3, Top); P[4] := Point(Right - 1, Top + 5); P[5] := Point(Left + 3, Bottom - 1); P[6] := Point(Left + 3, Bottom - 4); P[7] := Point(Left, Bottom - 4); end; Polygon(DC, P, 7); with R2 do begin P[1].X := Right - 1; P[2].X := Right - 4; P[3].X := Right - 4; P[4].X := Left; P[5].X := Right - 4; P[6].X := Right - 4; P[7].X := Right - 1; end; Polygon(DC, P, 7); end else begin with R1 do begin P[1] := Point(Left + 3, Top); P[2] := Point(Right - 4, Top); P[3] := Point(Right - 4, Top + 3); P[4] := Point(Right - 1, Top + 3); P[5] := Point(Left + 5, Bottom - 1); P[6] := Point(Left, Top + 3); P[7] := Point(Left + 3, Top + 3); end; Polygon(DC, P, 7); with R2 do begin P[1].Y := Bottom - 1; P[2].Y := Bottom - 1; P[3].Y := Bottom - 4; P[4].Y := Bottom - 4; P[5].Y := Top; P[6].Y := Bottom - 4; P[7].Y := Bottom - 4; end; Polygon(DC, P, 7); end; SelectObject(DC, PrevBrush); SelectObject(DC, PrevPen); end; function CanInsert: Boolean; begin if ActiveRowIndex = -1 then // GroupBy Box Result := (FMovingColumn.GroupIndex = -1) or (ActiveColIndex < FMovingColumn.GroupIndex) or (FMovingColumn.GroupIndex < ActiveColIndex - 1) else if FMovingColumn.Visible then if Level.Horizontal then Result := (FMovingColumn.RowIndex <> ActiveRowIndex) or ActiveForceMove or ActiveInsert or (ActiveColIndex < FMovingColumn.ColIndex) or (FMovingColumn.ColIndex + 1 < ActiveColIndex) else Result := ActiveInsert and (not Level.Layout.OneOnLine(FMovingColumn) or (ActiveColIndex < FMovingColumn.ColIndex) or (FMovingColumn.ColIndex + 1 < ActiveColIndex)) or not ActiveInsert and ((ActiveColIndex <> FMovingColumn.ColIndex) or (ActiveRowIndex < FMovingColumn.RowIndex) or (ActiveRowIndex > FMovingColumn.RowIndex + FMovingColumn.RowCount)) else Result := True; end; begin if (ActiveNode <> NodeValue) or (ActiveRowIndex <> ARowIndex) or (ActiveColIndex <> AColIndex) or (ActiveForceMove <> AForceMove) or (ActiveInsert <> AInsert) then begin if PrevP.X <> MaxInt then DrawDraggingColumn(Point(MaxInt, 0)); if (ActiveNode <> nil) and (ActiveColIndex <> -1) and CanInsert then HideArrows; ActiveNode := NodeValue; ActiveRowIndex := ARowIndex; ActiveColIndex := AColIndex; ActiveForceMove := AForceMove; ActiveInsert := AInsert; if (ActiveNode <> nil) and (ActiveColIndex <> -1) and CanInsert then ShowArrows; if PrevP.X <> MaxInt then DrawDraggingColumn(PrevP); end; end; procedure DestroyScrollTimer; begin if FScrollTimer <> 0 then begin KillTimer(Handle, FScrollTimer); FScrollTimer := 0; end; end; procedure CreateScrollTimer(Direction: TdxMasterViewDirection); begin if FScrollTimer <> 0 then if TdxMasterViewDirection(FScrollTimer - ScrollLeftTimerId + 1) <> Direction then DestroyScrollTimer else Exit; FScrollTimer := SetTimer(Handle, ScrollLeftTimerId + Ord(Direction) - 1, ScrollTimeStep div 2, nil); end; function CheckInsertStatus: Boolean; var I: Integer; CurNode: TdxMasterViewNode; R: TRect; begin Result := False; if not Level.Horizontal then Exit; for I := TopIndex to TopIndex + PartVisibleItemCount - 1 do begin CurNode := AbsoluteItems[I]; if CurNode.Level = Level then begin R := CurNode.HeaderBounds; if IsRectEmpty(R) or PtInRect(R, P) then Continue; InflateRect(R, 0, InsertZoneWidth); if PtInRect(R, P) then begin Accept := True; ANode := CurNode; R.Bottom := R.Top + InsertZoneWidth; if PtInRect(R, P) then ARowIndex := 0 else ARowIndex := Level.Layout.Count; AColIndex := 0; AInsert := True; Result := True; Break; end; end; end; end; procedure HideDragImages; begin ANode := ActiveNode; ARowIndex := ActiveRowIndex; AColIndex := ActiveColIndex; AForceMove := ActiveForceMove; AInsert := ActiveInsert; DrawDraggingColumn(Point(MaxInt, 0)); SetActivePlace(nil, -1, -1, False, False); end; begin if Column = nil then Exit; Level := Column.Level; if DragSource = dsCustomizingForm then GetCursorPos(P) else begin P := InitialP; Windows.ClientToScreen(Handle, P); end; Accept := Column.CanMoving; if Assigned(FOnBeforeColumnMoving) then begin Windows.ScreenToClient(Handle, P); FOnBeforeColumnMoving(Self, Column, P, -1, -1, False, Accept); Windows.ClientToScreen(Handle, P); end; if not Accept then Exit; FMovingColumnNode := Node; FMovingColumn := Column; Include(FState, mvsColumnMoving); if Customizing then TdxMVCustomizationForm(FCustomizationForm).ActiveLevel := Column.Level; if DragSource = dsCustomizingForm then begin with Column do begin W := Width; H := GetHeaderHeight;//Level.HeaderRealHeight; end; Offset := Point(W div 2, H div 2); end else begin if DragSource = dsHeader then R := Column.GetHeaderBounds(Node) else R := Column.GetGroupByBoxBounds(Node); MapWindowPoints(Handle, 0, R, 2); with Offset, R do begin X := P.X - Left; Y := P.Y - Top; W := Right - Left; H := Bottom - Top; end; end; DC := GetDC(0); SaveDC := CreateCompatibleDC(DC); SaveB := CreateCompatibleBitmap(DC, W, H); SaveB := SelectObject(SaveDC, SaveB); BDC := CreateCompatibleDC(DC); B := CreateCompatibleBitmap(DC, W, H); B := SelectObject(BDC, B); SaveArrowsDC := CreateCompatibleDC(DC); SaveArrowsB := CreateCompatibleBitmap(DC, ArrowWidth, 2 * ArrowWidth); SaveArrowsB := SelectObject(SaveArrowsDC, SaveArrowsB); ArrowsBrush := CreateSolidBrush(ColorToRGB(FArrowsColor)); BeforePaint; Level.BeforePaint; R := Rect(0, 0, W, H); DrawEdge(BDC, R, BDR_RAISEDINNER, BF_RECT); InflateRect(R, -1, -1); Column.DrawHeader(BDC, R, Node); Level.AfterPaint; AfterPaint; PrepareAlphaBlend; PrevP.X := MaxInt; DrawDraggingColumn(P); Success := False; ActiveNode := nil; ActiveRowIndex := -1; ActiveColIndex := -1; ActiveForceMove := False; ActiveInsert := False; CaptureWnd := Handle; SetCapture(CaptureWnd); try while GetCapture = CaptureWnd do begin case Integer(GetMessage(Msg, 0, 0, 0)) of -1: Break; 0: begin PostQuitMessage(Msg.wParam); Break; end; end; try case Msg.message of WM_KEYDOWN, WM_KEYUP: if Msg.wParam = VK_ESCAPE then Break; WM_RBUTTONDOWN, WM_MBUTTONDOWN: Break; WM_MOUSEMOVE: begin P := SmallPointToPoint(TSmallPoint(Msg.lParam)); Windows.ClientToScreen(Msg.hwnd, P); if (P.X <> PrevP.X) or (P.Y <> PrevP.Y) then begin Windows.ScreenToClient(Handle, P); HitTestCode := GetHitTestInfo(P, ANode, AColumn, ARowIndex, AColIndex); AForceMove := False; AInsert := False; if PtInRect(ClientRect, P) and (HitTestCode <> htCustomizationForm) then if CanScroll(dirLeft) and (P.X < ColumnMovingHScrollZone) then CreateScrollTimer(dirLeft) else if CanScroll(dirRight) and (P.X >= ClientWidth - ColumnMovingHScrollZone) then CreateScrollTimer(dirRight) else DestroyScrollTimer else DestroyScrollTimer; Accept := (ANode <> nil) and (ANode.Level = Level) and not CheckInsertStatus and ((HitTestCode = htGroupByBox) and FMovingColumn.CanGrouping(AColIndex) or ANode.HasHeader(TopIndex, R.Left) and ((HitTestCode in HeaderHitTests) or (HitTestCode = htRightSpace) and (ARowIndex <> -1) or not Level.Horizontal and (HitTestCode in LeftHitTests + ContentHitTests) and ((HitTestCode <> htExpandButton) or (ARowIndex <> -1)))); if Accept then if HitTestCode = htGroupByBox then // else if HitTestCode = htRightSpace then if not Level.Horizontal then AInsert := True else else if not Level.Horizontal and (HitTestCode in [htHeaderLeftEdge] + LeftHitTests + ContentHitTests) then begin if HitTestCode <> htHeaderLeftEdge then Inc(AColIndex); ARowIndex := 0; AInsert := True; end else if AColumn = nil then if Level.Horizontal then begin AColIndex := Level.Layout.GetNearestCol(ARowIndex, P.X - ANode.HeaderBounds.Left); AForceMove := FMovingColumn.Visible and (ARowIndex = FMovingColumn.RowIndex) {and (AColIndex = 0)}; end else ARowIndex := Level.Layout[AColIndex].Count else begin R := AColumn.GetHeaderBounds(ANode); if Level.Horizontal then AColIndex := Level.Layout[ARowIndex].IndexOf(AColumn) + Byte(P.X >= (R.Left + R.Right) div 2) else ARowIndex := AColumn.RowIndex + Byte(P.Y >= (R.Top + R.Bottom) div 2) * AColumn.RowCount; end else begin ANode := nil; ARowIndex := -1; AColIndex := -1; if HitTestCode <> htCustomizationForm then CheckInsertStatus; end; if Assigned(FOnColumnMoving) then begin FOnColumnMoving(Self, FMovingColumn, P, ARowIndex, AColIndex, AInsert, Accept); if not Accept then begin ARowIndex := -1; AColIndex := -1; end; end; SetActivePlace(ANode, ARowIndex, AColIndex, AForceMove, AInsert); if Accept or (HitTestCode = htCustomizationForm) and (DragSource = dsCustomizingForm) then SetCursor(Screen.Cursors[crDefault]) else SetCursor(Screen.Cursors[Cursors[ (DragSource <> dsGroupByBox) and FMovingColumn.CanHiding or (DragSource = dsGroupByBox) and FMovingColumn.CanGrouping(AColIndex)]]); Windows.ClientToScreen(Handle, P); DrawDraggingColumn(P); end; end; WM_LBUTTONUP: begin Success := True; Break; end; WM_TIMER: if (Msg.hwnd = Handle) and (Msg.wParam in [ScrollLeftTimerId, ScrollRightTimerId]) and CanScroll(TdxMasterViewDirection(FScrollTimer - ScrollLeftTimerId + 1)) then begin HideDragImages; if Msg.wParam = ScrollLeftTimerId then Scroll(dirLeft) else Scroll(dirRight); GetCursorPos(P); Windows.ScreenToClient(Handle, P); PostMessage(Handle, WM_MOUSEMOVE, 0, LPARAM(PointToSmallPoint(P))); end; end; finally TranslateMessage(Msg); DispatchMessage(Msg); end; end; finally if GetCapture = CaptureWnd then ReleaseCapture; DestroyScrollTimer; HideDragImages; UnprepareAlphaBlend; DeleteObject(ArrowsBrush); DeleteObject(SelectObject(SaveArrowsDC, SaveArrowsB)); DeleteDC(SaveArrowsDC); DeleteObject(SelectObject(BDC, B)); DeleteDC(BDC); DeleteObject(SelectObject(SaveDC, SaveB)); DeleteDC(SaveDC); ReleaseDC(0, DC); if Success then begin Accept := True; if Assigned(FOnAfterColumnMoving) then begin GetCursorPos(P); Windows.ScreenToClient(Handle, P); FOnAfterColumnMoving(Self, FMovingColumn, P, ARowIndex, AColIndex, AInsert, Accept); end; if Accept then begin with FMovingColumn do if ARowIndex = -1 then if AColIndex = -1 then if DragSource = dsGroupByBox then if CanGrouping(-1) then begin GroupIndex := -1; if not Visible and Hidden then Visible := True; end else else if CanHiding then Visible := False else else if CanGrouping(AColIndex) then begin if (GroupIndex <> -1) and (AColIndex > GroupIndex) then Dec(AColIndex); if GroupIndex <> AColIndex then begin GroupIndex := AColIndex; {if (DragSource = dsHeader) and (locHideColumnOnGrouping in Level.OptionsCustomize) then Visible := False;} end; end else else begin Level.BeginLayout; try if Level.Horizontal then begin Success := Visible and (RowIndex <= ARowIndex) and (ARowIndex <= RowIndex + RowCount - 1) and (ColIndex < AColIndex); if AInsert and (ARowIndex = 0) then Level.Layout.Insert(0, RowCount); RowIndex := ARowIndex; if (ColIndex <> AColIndex - Byte(Success)) or AForceMove then ColIndex := AColIndex - Byte(Success); end else begin Success := Visible and (ColIndex = AColIndex) and (RowIndex < ARowIndex); if AInsert then Level.Layout.Insert(AColIndex, 1); ColIndex := AColIndex; RowIndex := ARowIndex - Byte(Success) * RowCount; end; if (DragSource = dsGroupByBox) and CanGrouping(-1) then GroupIndex := -1; Visible := True; finally Level.EndLayout; end; end; Modified; end; end; Exclude(FState, mvsColumnMoving); FMovingColumn := nil; FMovingColumnNode := nil; end; end; procedure TdxMasterView.DoColumnHorSizing(Column: TdxMasterViewColumn; ContentSizing: Boolean); var P, PrevP: TPoint; PrevWidths, PrevHeaderWidths: PIntArray; I, Offset, Delta, PrevLeftPos, AWidth: Integer; Success: Boolean; CaptureWnd: HWND; Msg: TMsg; begin if Column = nil then Exit; FSizingColumn := Column; Include(FState, mvsColumnSizing); with Column.Level do begin if ContentSizing or AutoColumnWidth then begin I := VisibleColumnCount; GetMem(PrevWidths, I * SizeOf(Integer)); for I := 0 to I - 1 do with VisibleColumns[I] do if cvWidth in AssignedValues then PrevWidths[I] := Width else PrevWidths[I] := -1; end else PrevWidths := nil; if not ContentSizing then begin I := Layout.Count; GetMem(PrevHeaderWidths, I * SizeOf(Integer)); for I := 0 to I - 1 do with Layout do if HeaderWidthAssigned[I] then PrevHeaderWidths[I] := HeaderWidths[I] else PrevHeaderWidths[I] := -1; end else PrevHeaderWidths := nil; end; GetCursorPos(PrevP); P := Point(Column.Offset, 0); Windows.ClientToScreen(Handle, P); Offset := P.X; if not Column.Level.Horizontal and ContentSizing then Inc(Offset, Column.Level.Layout.HeaderWidths[Column.ColIndex]); if ContentSizing then Delta := Column.VisibleWidth else Delta := Column.Level.Layout.HeaderWidths[Column.ColIndex]; Dec(Delta, PrevP.X - Offset); PrevLeftPos := LeftPos; Success := False; CaptureWnd := Handle; SetCapture(CaptureWnd); try while GetCapture = CaptureWnd do begin case Integer(GetMessage(Msg, 0, 0, 0)) of -1: Break; 0: begin PostQuitMessage(Msg.wParam); Break; end; end; if LeftPos <> PrevLeftPos then begin PrevP.X := MaxInt; Dec(Offset, LeftPos - PrevLeftPos); PrevLeftPos := LeftPos; end; try case Msg.message of WM_KEYDOWN, WM_KEYUP: if Msg.wParam = VK_ESCAPE then Break; WM_RBUTTONDOWN, WM_MBUTTONDOWN: Break; WM_MOUSEMOVE: begin P := SmallPointToPoint(TSmallPoint(Msg.lParam)); Windows.ClientToScreen(Msg.hwnd, P); if P.X <> PrevP.X then begin AWidth := P.X - Offset + Delta; if ContentSizing then Column.VisibleWidth := AWidth else Column.Level.Layout.ChangeHeaderWidth(Column.ColIndex, AWidth, True); UpdateWindow(Handle); PrevP := P; end; end; WM_LBUTTONUP: begin Success := True; Break; end; end; finally TranslateMessage(Msg); DispatchMessage(Msg); end; end; finally if GetCapture = CaptureWnd then ReleaseCapture; try if Success then Modified else with Column.Level do begin BeginAssignWidths; try if PrevWidths <> nil then for I := 0 to VisibleColumnCount - 1 do with VisibleColumns[I] do if PrevWidths[I] = -1 then AssignedValues := AssignedValues - [cvWidth] else Width := PrevWidths[I]; if PrevHeaderWidths <> nil then for I := 0 to Layout.Count - 1 do if PrevHeaderWidths[I] = -1 then Layout.HeaderWidthAssigned[I] := False else Layout.HeaderWidths[I] := PrevHeaderWidths[I]; finally EndAssignWidths; end; end; finally if PrevWidths <> nil then FreeMem(PrevWidths); if PrevHeaderWidths <> nil then FreeMem(PrevHeaderWidths); Exclude(FState, mvsColumnSizing); FSizingColumn := nil; end; end; end; procedure TdxMasterView.DoColumnVerSizing(Node: TdxMasterViewNode; Column: TdxMasterViewColumn; Edge: TdxMasterViewHitTestCode); var Level: TdxMasterViewLevel; ColumnsPlaces: TList; P, PrevP: TPoint; Delta, PrevNodeBoundsTop, ARowIndex, ARowCount, AColIndex: Integer; Success: Boolean; CaptureWnd: HWND; Msg: TMsg; ANode: TdxMasterViewNode; AColumn: TdxMasterViewColumn; begin if Column = nil then Exit; Level := Column.Level; FSizingColumn := Column; Include(FState, mvsColumnSizing); Level.SaveVisibleColumnsPlaces(ColumnsPlaces); GetCursorPos(PrevP); Windows.ScreenToClient(Handle, PrevP); Delta := 0; PrevNodeBoundsTop := Node.Bounds.Top; Success := False; CaptureWnd := Handle; SetCapture(CaptureWnd); try while GetCapture = CaptureWnd do begin case Integer(GetMessage(Msg, 0, 0, 0)) of -1: Break; 0: begin PostQuitMessage(Msg.wParam); Break; end; end; Inc(Delta, Node.Bounds.Top - PrevNodeBoundsTop); PrevNodeBoundsTop := Node.Bounds.Top; try case Msg.message of WM_KEYDOWN, WM_KEYUP: if Msg.wParam = VK_ESCAPE then Break; WM_RBUTTONDOWN, WM_MBUTTONDOWN: Break; WM_MOUSEMOVE: begin P := SmallPointToPoint(TSmallPoint(Msg.lParam)); MapWindowPoints(Msg.hwnd, Handle, P, 1); if P.Y <> PrevP.Y then begin if Node.ViewInfo.HeaderSize = 0 then begin GetHitTestInfo(P, ANode, AColumn, ARowIndex, AColIndex); if (ANode <> nil) and (ANode.Level = Level) and (ANode.ViewInfo.HeaderSize <> 0) then Node := ANode else begin Success := True; Break; end; end; ARowIndex := Node.InternalHeaderRowIndexFromY(P.Y + Delta); with Column do begin if Edge = htHeaderTopEdge then if ARowIndex = -1 then begin ARowIndex := RowIndex + RowCount - MaxRowCount; if ARowIndex < 0 then ARowIndex := 0; ARowCount := RowIndex + RowCount - ARowIndex; end else begin if ARowIndex < RowIndex + RowCount - MaxRowCount then ARowIndex := RowIndex + RowCount - MaxRowCount else if ARowIndex > RowIndex + RowCount - MinRowCount then ARowIndex := RowIndex + RowCount - MinRowCount; ARowCount := RowIndex + RowCount - ARowIndex; end else begin if ARowIndex = -1 then ARowCount := MinRowCount else ARowCount := ARowIndex - RowIndex + 1; ARowIndex := RowIndex; end; CheckRowCount(ARowCount); end; if Column.RowIndex <> ARowIndex then Level.Layout.ChangeRowAndRowCount(Column, ARowIndex, ARowCount, True) else Column.RowCount := ARowCount; PrevP := P; end; end; WM_LBUTTONUP: begin Success := True; Break; end; end; finally TranslateMessage(Msg); DispatchMessage(Msg); end; end; finally if GetCapture = CaptureWnd then ReleaseCapture; if Success then begin Level.FreeVisibleColumnsPlaces(ColumnsPlaces); Modified; end else Level.RestoreVisibleColumnsPlaces(ColumnsPlaces); Exclude(FState, mvsColumnSizing); FSizingColumn := nil; end; end; procedure TdxMasterView.DoNodeDeleted(Node: TdxMasterViewNode); var NewNode: TdxMasterViewNode; begin if FSelectionAnchor = Node then FSelectionAnchor := nil; if FFocusedNode = Node then FocusedNode := nil; if FTopNode = Node then begin NewNode := GetNextNode(Node, False); if NewNode = nil then NewNode := GetPrevNode(Node, False); end else NewNode := nil; Node.Selected := False; FAbsoluteItems.Remove(Node); if FTopNode = Node then TopNode := NewNode; end; procedure TdxMasterView.DoScrolling; const ScrollTimeStep = 20; ScrollValueStep = 5; MaxSpeed = 12; var BreakOnMouseUp: Boolean; AllowHorScrolling, AllowVerScrolling: Boolean; P, PrevP: TPoint; AnchorPos: TPoint; AnchorSize: Integer; AnchorWnd: HWND; Direction: TdxMasterViewDirection; Speed: Integer; TimerHits: Integer; Timer: UINT; CaptureWnd: HWND; Msg: TMsg; function CreateScrollingAnchorWnd: HWND; var B: TBitmap; W, H: Integer; Rgn: HRGN; DC: HDC; function GetResourceBitmapName: string; begin if AllowHorScrolling and AllowVerScrolling then Result := 'DXMASTERVIEWFULLSCROLLBITMAP' else if AllowHorScrolling then Result := 'DXMASTERVIEWHORSCROLLBITMAP' else Result := 'DXMASTERVIEWVERSCROLLBITMAP'; end; begin B := TBitmap.Create; B.LoadFromResourceName(HInstance, GetResourceBitmapName); W := B.Width; H := B.Height; AnchorSize := W; with AnchorPos do Result := CreateWindow('STATIC', nil, WS_POPUP, X - W div 2, Y - H div 2, W, H, Handle, 0, HInstance, nil); Rgn := CreateEllipticRgn(0, 0, W + 1, H + 1); SetWindowRgn(Result, Rgn, True); SetWindowPos(Result, 0, 0, 0, 0, 0, SWP_NOZORDER or SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW or SWP_NOACTIVATE); DC := GetWindowDC(Result); BitBlt(DC, 0, 0, W, H, B.Canvas.Handle, 0, 0, SRCCOPY); Rgn := CreateEllipticRgn(0, 0, W + 1, H + 1); FrameRgn(DC, Rgn, GetSysColorBrush(COLOR_WINDOWTEXT), 1, 1); DeleteObject(Rgn); ReleaseDC(Result, DC); B.Free; end; procedure CalcDirectionAndSpeed(const P: TPoint); var DeltaX, DeltaY, SpeedValue: Integer; function GetNeutralZone: TRect; begin with AnchorPos do Result := Bounds(X - AnchorSize div 2, Y - AnchorSize div 2, AnchorSize, AnchorSize); if not AllowHorScrolling then begin Result.Left := 0; Result.Right := Screen.Width; end; if not AllowVerScrolling then begin Result.Top := 0; Result.Bottom := Screen.Height; end; end; begin if PtInRect(GetNeutralZone, P) then begin Direction := dirNone; Speed := 0; Exit; end else begin BreakOnMouseUp := True; DeltaX := P.X - AnchorPos.X; DeltaY := P.Y - AnchorPos.Y; if AllowHorScrolling and (not AllowVerScrolling or (Abs(DeltaX) > Abs(DeltaY))) then begin if DeltaX < 0 then Direction := dirLeft else Direction := dirRight; SpeedValue := Abs(DeltaX); end else begin if DeltaY < 0 then Direction := dirUp else Direction := dirDown; SpeedValue := Abs(DeltaY); end; end; Dec(SpeedValue, AnchorSize div 2); Speed := 1 + SpeedValue div ScrollValueStep; if Speed > MaxSpeed then Speed := MaxSpeed; end; procedure SetMouseCursor; var Cursor: TCursor; begin case Direction of dirLeft: Cursor := crdxMasterViewLeftScroll; dirUp: Cursor := crdxMasterViewUpScroll; dirRight: Cursor := crdxMasterViewRightScroll; dirDown: Cursor := crdxMasterViewDownScroll; else if AllowHorScrolling and AllowVerScrolling then Cursor := crdxMasterViewFullScroll else if AllowHorScrolling then Cursor := crdxMasterViewHorScroll else Cursor := crdxMasterViewVerScroll; end; SetCursor(Screen.Cursors[Cursor]); end; begin Include(FState, mvsScrolling); BreakOnMouseUp := False; AllowHorScrolling := GetWindowLong(Handle, GWL_STYLE) and WS_HSCROLL <> 0; AllowVerScrolling := GetWindowLong(Handle, GWL_STYLE) and WS_VSCROLL <> 0; GetCursorPos(PrevP); AnchorPos := PrevP; AnchorWnd := CreateScrollingAnchorWnd; Direction := dirNone; SetMouseCursor; Speed := 1; TimerHits := 0; Timer := SetTimer(0, 0, ScrollTimeStep, nil); CaptureWnd := Handle; SetCapture(CaptureWnd); try while GetCapture = CaptureWnd do begin case Integer(GetMessage(Msg, 0, 0, 0)) of -1: Break; 0: begin PostQuitMessage(Msg.wParam); Break; end; end; if (Msg.message = WM_PAINT) and (Msg.hwnd = AnchorWnd) then begin ValidateRect(AnchorWnd, nil); Continue; end; if (Msg.message >= WM_MOUSEFIRST) and (Msg.message <= WM_MOUSELAST) and (Msg.message <> WM_MOUSEMOVE) and (Msg.message <> WM_MBUTTONUP) then Break; try case Msg.message of WM_KEYDOWN, WM_KEYUP: if Msg.wParam = VK_ESCAPE then Break; WM_MOUSEMOVE: begin P := SmallPointToPoint(TSmallPoint(Msg.lParam)); Windows.ClientToScreen(Msg.hwnd, P); if (P.X <> PrevP.X) or (P.Y <> PrevP.Y) then begin CalcDirectionAndSpeed(P); SetMouseCursor; PrevP := P; end; end; WM_MBUTTONUP: if BreakOnMouseUp then Break; WM_TIMER: if UINT(Msg.wParam) = Timer then begin Inc(TimerHits); if TimerHits mod (MaxSpeed - Speed + 1) = 0 then Scroll(Direction); end; end; finally TranslateMessage(Msg); DispatchMessage(Msg); end; end; finally if GetCapture = CaptureWnd then ReleaseCapture; KillTimer(0, Timer); DestroyWindow(AnchorWnd); Exclude(FState, mvsScrolling); end; end; procedure TdxMasterView.DoSelectionChanged; begin if (FSelectionLockCount <> 0) or not MultiSelect or IsDestroying then Exit; if Assigned(FOnSelectionChanged) then FOnSelectionChanged(Self); end; function TdxMasterView.DoSelectNode(Node: TdxMasterViewNode): Boolean; begin Result := True; if Assigned(FOnSelectNode) then FOnSelectNode(Self, Node, Result); end; procedure TdxMasterView.DoSorting; var I: Integer; begin BeginUpdate; try for I := 0 to AbsoluteLevelCount - 1 do AbsoluteLevels[I].DoSorting; finally EndUpdate; end; end; procedure TdxMasterView.DoTopNodeChanged; begin if FTopNodeChangeLockCount <> 0 then Exit; Invalidate; if Assigned(FOnTopNodeChanged) then FOnTopNodeChanged(Self); end; procedure TdxMasterView.HighlightStyleChanged(Values: TdxMasterViewStyleValues); begin if HasFocus then InvalidateSelection; end; procedure TdxMasterView.InactiveStyleChanged(Values: TdxMasterViewStyleValues); begin if not HasFocus then InvalidateSelection; end; function TdxMasterView.CanFocusNode(Node: TdxMasterViewNode): Boolean; begin Result := True; if Assigned(FOnBeforeFocusNode) then FOnBeforeFocusNode(Self, Node, Result); end; procedure TdxMasterView.ChangeFocusedIndex(Value: Integer); var NewFocusedNode: TdxMasterViewNode; ATopIndex, AVisibleItemCount, APartVisibleItemCount, Delta, PrevDelta: Integer; begin if Value < 0 then Value := 0; if Value >= AbsoluteItemCount then Value := AbsoluteItemCount - 1; if Value = -1 then NewFocusedNode := nil else NewFocusedNode := AbsoluteItems[Value]; if not CanFocusNode(NewFocusedNode) then Exit; //try if NewFocusedNode <> nil then NewFocusedNode.SyncPos; //finally if FFocusedNode <> nil then FFocusedNode.Invalidate(nil, vpContent); FocusedNode := NewFocusedNode; if Value < TopIndex then TopIndex := Value else if Value >= TopIndex + VisibleItemCount then begin ATopIndex := Value - FLastVisibleItemCount + 1; Delta := 0; repeat CalcViewInfo(ATopIndex, -1, AVisibleItemCount, APartVisibleItemCount, False); PrevDelta := Delta; if Value > ATopIndex + AVisibleItemCount - 1 then Delta := 1 else Delta := -1; if (PrevDelta <> 0) and (Delta <> PrevDelta) then begin if Delta = 1 then Inc(ATopIndex); Break; end; Inc(ATopIndex, Delta); until False; TopIndex := ATopIndex; end; if FFocusedNode <> nil then FFocusedNode.Invalidate(nil, vpContent); //end; end; function TdxMasterView.GetFirstNodeFromVisible(Level: TdxMasterViewLevel): TdxMasterViewNode; var I: Integer; begin for I := TopIndex to AbsoluteItemCount - 1 do begin Result := AbsoluteItems[I]; if ((Level = nil) or (Result.Level = Level)) and CanFocusNode(Result) then Exit; end; for I := TopIndex - 1 downto 0 do begin Result := AbsoluteItems[I]; if ((Level = nil) or (Result.Level = Level)) and CanFocusNode(Result) then Exit; end; Result := nil; end; function TdxMasterView.GetLastNodeFromVisible(Level: TdxMasterViewLevel): TdxMasterViewNode; var I, Margin: Integer; begin Result := nil; Margin := TopIndex + VisibleItemCount - 1; for I := TopIndex to AbsoluteItemCount - 1 do begin if (I > Margin) and (Result <> nil) then Exit; if ((Level = nil) or (AbsoluteItems[I].Level = Level)) and CanFocusNode(AbsoluteItems[I]) then Result := AbsoluteItems[I]; end; end; function TdxMasterView.GetNextNode(Value: TdxMasterViewNode; OnSameLevel: Boolean): TdxMasterViewNode; var I: Integer; begin if (Value <> nil) and (Value.AbsoluteIndex = AbsoluteItemCount - 1) or (Value = nil) and OnSameLevel then Result := nil else if OnSameLevel then begin for I := Value.AbsoluteIndex + 1 to AbsoluteItemCount - 1 do begin Result := AbsoluteItems[I]; if (Result.Level = Value.Level) and CanFocusNode(Result) then Exit; end; Result := nil; end else begin if Value = nil then I := 0 else I := Value.AbsoluteIndex + 1; for I := I to AbsoluteItemCount - 1 do begin Result := AbsoluteItems[I]; if (Result <> nil) and CanFocusNode(Result) then Exit; end; Result := nil; end; end; function TdxMasterView.GetPrevNode(Value: TdxMasterViewNode; OnSameLevel: Boolean): TdxMasterViewNode; var I: Integer; begin if (Value <> nil) and (Value.AbsoluteIndex = 0) or (Value = nil) and OnSameLevel then Result := nil else if OnSameLevel then begin for I := Value.AbsoluteIndex - 1 downto 0 do begin Result := AbsoluteItems[I]; if (Result.Level = Value.Level) and CanFocusNode(Result) then Exit; end; Result := nil; end else begin if Value = nil then I := AbsoluteItemCount - 1 else I := Value.AbsoluteIndex - 1; for I := I downto 0 do begin Result := AbsoluteItems[I]; if (Result <> nil) and CanFocusNode(Result) then Exit; end; Result := nil; end; end; procedure TdxMasterView.ShowPrevPage(CurTopIndex: Integer; OnSameLevel: Boolean); var ATopIndex, LastVisibleIndex, AVisibleItemCount, APartVisibleItemCount: Integer; NodeVisible, PrevNodeVisible: Boolean; FirstVisibleNode, PrevFirstVisibleNode: TdxMasterViewNode; function IsNodeVisible: Boolean; var I: Integer; begin PrevFirstVisibleNode := FirstVisibleNode; Result := True; for I := ATopIndex to ATopIndex + AVisibleItemCount - 1 do begin FirstVisibleNode := AbsoluteItems[I]; if FirstVisibleNode.Level = FFocusedNode.Level then Exit; end; FirstVisibleNode := nil; Result := False; end; begin if CurTopIndex = 0 then Exit; if OnSameLevel then begin ATopIndex := TopIndex; AVisibleItemCount := VisibleItemCount; PrevNodeVisible := IsNodeVisible; if PrevNodeVisible then LastVisibleIndex := TopIndex else begin LastVisibleIndex := FocusedIndex; FirstVisibleNode := FFocusedNode; end; ATopIndex := CurTopIndex - 1; repeat CalcViewInfo(ATopIndex, -1, AVisibleItemCount, APartVisibleItemCount, False); NodeVisible := IsNodeVisible; if NodeVisible then begin if FirstVisibleNode <> PrevFirstVisibleNode then LastVisibleIndex := ATopIndex; if (ATopIndex + AVisibleItemCount - 1 <= FocusedIndex) and (LastVisibleIndex <> TopIndex) then begin if (ATopIndex + AVisibleItemCount - 1 < FocusedIndex) and PrevNodeVisible then Inc(ATopIndex); Break; end; end; PrevNodeVisible := NodeVisible; Dec(ATopIndex); if ATopIndex = -1 then begin ATopIndex := LastVisibleIndex; Break; end; until False; end else begin ATopIndex := CurTopIndex - 1; repeat if ATopIndex = 0 then Break; CalcViewInfo(ATopIndex, -1, AVisibleItemCount, APartVisibleItemCount, False); if ATopIndex + AVisibleItemCount - 1 <= CurTopIndex then begin if ATopIndex + AVisibleItemCount - 1 < CurTopIndex then Inc(ATopIndex); Break; end; Dec(ATopIndex); until False; if ATopIndex = CurTopIndex then Dec(ATopIndex); end; TopIndex := ATopIndex; end; procedure TdxMasterView.ShowNextPage(CurBottomIndex: Integer; OnSameLevel: Boolean); var ATopIndex, AVisibleItemCount, APartVisibleItemCount: Integer; LastVisibleNode, PrevLastVisibleNode: TdxMasterViewNode; function IsNodeVisible: Boolean; var I: Integer; begin PrevLastVisibleNode := LastVisibleNode; Result := True; for I := ATopIndex + AVisibleItemCount - 1 downto ATopIndex do begin LastVisibleNode := AbsoluteItems[I]; if LastVisibleNode.Level = FFocusedNode.Level then Exit; end; LastVisibleNode := nil; Result := False; end; begin if OnSameLevel then begin ATopIndex := TopIndex; AVisibleItemCount := VisibleItemCount; if not IsNodeVisible then LastVisibleNode := FFocusedNode; ATopIndex := CurBottomIndex; repeat CalcViewInfo(ATopIndex, -1, AVisibleItemCount, APartVisibleItemCount, False); if IsNodeVisible and (LastVisibleNode <> PrevLastVisibleNode) then Break; Inc(ATopIndex); if ATopIndex = AbsoluteItemCount then Exit; until False; end else begin ATopIndex := CurBottomIndex; if ATopIndex = TopIndex then Inc(ATopIndex); end; TopIndex := ATopIndex; end; procedure TdxMasterView.Scroll(Direction: TdxMasterViewDirection); begin case Direction of dirLeft: LeftPos := LeftPos - HScrollDelta; dirRight: LeftPos := LeftPos + HScrollDelta; dirUp: TopIndex := TopIndex - 1; dirDown: TopIndex := TopIndex + 1; end; end; procedure TdxMasterView.RefreshAbsoluteItems; var I: Integer; PrevVisibles: PBoolArray; NeedCalcScrollableWidth: Boolean; begin if FUpdateLockCount = 0 then begin GetMem(PrevVisibles, AbsoluteLevelCount * SizeOf(Boolean)); try for I := 0 to AbsoluteLevelCount - 1 do with AbsoluteLevels[I] do begin PrevVisibles[I] := Visible; FVisible := False; end; FAbsoluteItems.Clear; if FItems <> nil then FItems.AddVisibleToList(FAbsoluteItems); if AutoColumnWidth then begin NeedCalcScrollableWidth := False; for I := 0 to AbsoluteLevelCount - 1 do with AbsoluteLevels[I] do if Visible and not PrevVisibles[I] then WidthChanged else if not Visible and PrevVisibles[I] then NeedCalcScrollableWidth := True; if NeedCalcScrollableWidth then CalcScrollableWidth(nil); end else CalcScrollableWidth(nil); finally FreeMem(PrevVisibles, AbsoluteLevelCount * SizeOf(Boolean)); end; end; end; procedure TdxMasterView.RefreshAbsoluteLevels; procedure AddLevel(Level: TdxMasterViewLevel); var I: Integer; begin if Level <> FLevels then Level.FAbsoluteIndex := FAbsoluteLevels.Add(Level); for I := 0 to Level.Count - 1 do AddLevel(Level[I]); end; begin FAbsoluteLevels.Clear; AddLevel(FLevels); end; procedure TdxMasterView.SelectPeriod(ToNode: TdxMasterViewNode); var M1, M2, I: Integer; begin if MultiSelect and (FLastSelectedNode <> ToNode) then begin M1 := FLastSelectedNode.AbsoluteIndex; M2 := ToNode.AbsoluteIndex; if M1 > M2 then begin I := M1; M1 := M2; M2 := I; end; FLastSelectedNode := ToNode; for I := M1 to M2 do AbsoluteItems[I].Invalidate(nil, vpContent); end; end; procedure TdxMasterView.InvalidateSelection; var I: Integer; begin for I := 0 to SelectedItemCount - 1 do SelectedItems[I].Invalidate(nil, vpContent); if (FFocusedNode <> nil) and not FFocusedNode.Selected then FFocusedNode.Invalidate(nil, vpContent); end; procedure TdxMasterView.Modified; begin if not (csLoading in ComponentState) and (GetParentForm(Self) <> nil) and (GetParentForm(Self).Designer <> nil) then GetParentForm(Self).Designer.Modified; end; procedure TdxMasterView.Freeze; begin FFrozen := True; end; function TdxMasterView.FreezeDataSet(ALevel: TdxMasterViewLevel): Integer; var I: Integer; begin Result := 0; for I := 0 to AbsoluteLevelCount - 1 do with AbsoluteLevels[I] do if DataSet = ALevel.DataSet then Result := DataLink.Freeze; end; procedure TdxMasterView.Unfreeze; begin FFrozen := False; end; procedure TdxMasterView.UnfreezeDataSet(ALevel: TdxMasterViewLevel); var I: Integer; begin for I := 0 to AbsoluteLevelCount - 1 do with AbsoluteLevels[I] do if DataSet = ALevel.DataSet then DataLink.Unfreeze; end; procedure TdxMasterView.Invalidate; begin if HandleAllocated then InvalidateRect(Handle, nil, False); end; procedure TdxMasterView.DrawText(const DrawDC: HDC; var ARect: TRect; const Font: TFont; const Brush: HBRUSH; const TextColor, BkColor: TColor; const Alignment: TAlignment; const Text: string; const FillBackground, MakeRightSpace, AllowUseBitmap, MultiLine: Boolean); const Alignments: array[TAlignment] of Integer = (DT_LEFT, DT_RIGHT, DT_CENTER); EndEllipsis: array[Boolean] of Integer = (0, DT_END_ELLIPSIS); MultiLines: array[Boolean] of Integer = (DT_SINGLELINE or DT_VCENTER, DT_WORDBREAK); Opaques: array[Boolean] of Integer = (0, ETO_OPAQUE); var R: TRect; CanUseBitmap: Boolean; DC: HDC; PrevFont: HFONT; Size: TSize; X, Y: Integer; begin R := ARect; CanUseBitmap := (movUseBitmap in FOptionsView) and AllowUseBitmap; if CanUseBitmap then begin with R do begin OffsetRect(R, -Left, -Top); PrepareBitmap(Right, Bottom); end; DC := FBitmap.Canvas.Handle; end else DC := DrawDC; PrevFont := SelectObject(DC, Font.Handle); SetTextColor(DC, ColorToRGB(TextColor)); GetTextExtentPoint{32}(DC, PChar(Text), Length(Text), Size); if (movDrawEndEllipsis in FOptionsView) or MultiLine then begin if FillBackground then FillRect(DC, R, Brush); Inc(R.Left, 2); if MakeRightSpace then Dec(R.Right, 2) else Dec(R.Right, SortOrderMarkZoneWidth); if MultiLine then InflateRect(R, 0, -2); SetBkMode(DC, TRANSPARENT); Windows.DrawText(DC, PChar(Text), Length(Text), R, MultiLines[MultiLine] or DT_NOPREFIX or Alignments[Alignment] or Byte(not MultiLine and not (fsItalic in Font.Style) and (Size.cx <= R.Right - R.Left)) * DT_NOCLIP or EndEllipsis[not MultiLine and (movDrawEndEllipsis in FOptionsView)]); SetBkMode(DC, OPAQUE); end else begin with R, Size do begin if not MakeRightSpace then begin Dec(Right, SortOrderMarkZoneWidth); if FillBackground then FillRect(DC, Rect(Right, Top, Right + SortOrderMarkZoneWidth, Bottom), Brush); end; case Alignment of taLeftJustify: X := Left + 2; taRightJustify: X := Right - Byte(MakeRightSpace) * 2 - cx; else X := (Left + Right + Byte(not MakeRightSpace) * 2 - cx) div 2; end; if Right - Left - (1 + Byte(MakeRightSpace)) * 2 < cx then begin X := Left + 2; if MakeRightSpace then begin Dec(Right, 2); if FillBackground then FillRect(DC, Rect(Right, Top, Right + 2, Bottom), Brush); end; end; Y := (Top + Bottom - cy) div 2; end; if FillBackground then SetBkColor(DC, ColorToRGB(BkColor)) else SetBkMode(DC, TRANSPARENT); ExtTextOut(DC, X, Y, ETO_CLIPPED or Opaques[FillBackground], @R, PChar(Text), Length(Text), nil); if not FillBackground then SetBkMode(DC, OPAQUE); end; SelectObject(DC, PrevFont); if CanUseBitmap then with ARect do BitBlt(DrawDC, Left, Top, Right - Left, Bottom - Top, DC, 0, 0, SRCCOPY); end; function TdxMasterView.GetSelectionParams(Node: TdxMasterViewNode; ABrush: PBRUSH; ATextColor, ABkColor: PColor): Boolean; begin Result := Node.Selected and (HasFocus or not (movHideSelection in FOptionsView)); if Result then if HasFocus then begin if ABrush <> nil then ABrush^ := HighlightBrush; if ATextColor <> nil then ATextColor^ := HighlightFontColor; if ABkColor <> nil then ABkColor^ := HighlightColor; end else begin if ABrush <> nil then ABrush^ := InactiveBrush; if ATextColor <> nil then ATextColor^ := InactiveFontColor; if ABkColor <> nil then ABkColor^ := InactiveColor; end; end; procedure TdxMasterView.PrepareBitmap(const AWidth, AHeight: Integer); begin with FBitmap do begin if AWidth > Width then Width := AWidth; if AHeight > Height then Height := AHeight; end; end; procedure TdxMasterView.ApplyBestFit(Level: TdxMasterViewLevel; Column: TdxMasterViewColumn); var I, J: Integer; begin if Level = nil then if Column = nil then for I := 0 to AbsoluteLevelCount - 1 do for J := 0 to AbsoluteLevels[I].VisibleColumnCount - 1 do AbsoluteLevels[I].VisibleColumns[J].ApplyBestFit else Column.ApplyBestFit else for I := 0 to Level.VisibleColumnCount - 1 do Level.VisibleColumns[I].ApplyBestFit; end; procedure TdxMasterView.BeginLayout; begin Inc(FLayoutLockCount); BeginUpdate; end; procedure TdxMasterView.BeginSelection; begin Inc(FSelectionLockCount); end; procedure TdxMasterView.BeginUpdate; begin Inc(FUpdateLockCount); end; procedure TdxMasterView.CancelLayout; begin if FLayoutLockCount > 0 then begin Dec(FLayoutLockCount); CancelUpdate; end; end; procedure TdxMasterView.CancelUpdate; begin if FUpdateLockCount > 0 then Dec(FUpdateLockCount); FDontInvalidate := False; end; procedure TdxMasterView.ClearSelection; begin ClearSelectedItems; end; function TdxMasterView.CreateStyle(AStyleClass: TdxMasterViewStyleClass): TdxMasterViewStyle; begin Result := AStyleClass.Create(Owner); AddStyle(Result); end; procedure TdxMasterView.DeleteSelection; var PrevCursor: TCursor; List: TList; I, J: Integer; S: string; begin if SelectedItemCount = 0 then Exit; if SelectedItemCount = 1 then begin if not SelectedItems[0].CanDelete then Exit; with SelectedItems[0].Level do if (lodConfirmDelete in OptionsDB) and (Application.MessageBox( PChar(Format(DeleteConfirmText, [Caption])), PChar(DeleteConfirmCaptionText), MB_ICONQUESTION or MB_OKCANCEL) = ID_CANCEL) then Exit; SelectedItems[0].Delete; end else begin BeginSelection; List := TList.Create; try for I := 0 to AbsoluteLevelCount - 1 do if AbsoluteLevels[I].CanDelete then begin List.Clear; for J := 0 to SelectedItemCount - 1 do with SelectedItems[J] do if (Level.AbsoluteIndex = I) and CanDelete then List.Add(SelectedItems[J]); if List.Count = 0 then Continue; with AbsoluteLevels[I] do if lodConfirmDelete in OptionsDB then begin if List.Count = 1 then S := DeleteConfirmText else S := MultipleDeleteConfirmText; case Application.MessageBox( PChar(Format(S, [Caption])), PChar(DeleteConfirmCaptionText), MB_ICONQUESTION or MB_YESNOCANCEL) of ID_CANCEL: Break; ID_NO: Continue; end; end; PrevCursor := Screen.Cursor; if ShowHourGlassCursor then Screen.Cursor := crHourGlass; BeginUpdate; AbsoluteLevels[I].BeginLoad; try for J := 0 to List.Count - 1 do TdxMasterViewNode(List[J]).Delete; finally try AbsoluteLevels[I].EndLoad(True); finally EndUpdate; if ShowHourGlassCursor then Screen.Cursor := PrevCursor; end; end; end; finally List.Free; EndSelection; end; end; end; procedure TdxMasterView.EndLayout; begin if FLayoutLockCount > 0 then begin Dec(FLayoutLockCount); try if FLayoutLockCount = 0 then UpdateData; finally EndUpdate; end; end; end; procedure TdxMasterView.EndSelection; begin if FSelectionLockCount > 0 then begin Dec(FSelectionLockCount); if FSelectionLockCount = 0 then begin DoSelectionChanged; end; end; end; procedure TdxMasterView.EndUpdate; begin if FUpdateLockCount > 0 then begin Dec(FUpdateLockCount); if FUpdateLockCount = 0 then begin RefreshAbsoluteItems; TopIndex := CheckTopIndex(TopIndex); CheckFocusedNode; if not FDontInvalidate then Invalidate; end; end; FDontInvalidate := False; end; procedure TdxMasterView.FocusNode(Node: TdxMasterViewNode; ChangeSelection: Boolean); begin ChangeSelection := ChangeSelection and MultiSelect and ((FocusedNode = nil) or (SelectedItemCount = 1) and FocusedNode.Selected); if Node = nil then FocusedIndex := -1 else FocusedIndex := Node.AbsoluteIndex; if ChangeSelection and (FFocusedNode <> nil) and (FFocusedNode = Node) then begin BeginSelection; try ClearSelection; FFocusedNode.Selected := True; finally EndSelection; end; end; end; procedure TdxMasterView.FullCollapse; begin Items.Collapse(True); end; procedure TdxMasterView.FullExpand; {var ft,lt:integer;} begin // ft:=gettickcount; Items.Expand(True); // lt:=gettickcount; // application.messagebox(pchar(inttostr(lt-ft)),'',0); end; function TdxMasterView.GetHitTestInfo(const P: TPoint; var Node: TdxMasterViewNode; var Column: TdxMasterViewColumn; var RowIndex, ColIndex: Integer): TdxMasterViewHitTestCode; var FromIndex, AVisibleItemCount, APartVisibleItemCount, I, J, K: Integer; R, R1, R2: TRect; List: TList; function ProcessHeader(const R: TRect; var HitTestCode: TdxMasterViewHitTestCode): Boolean; var K, J: Integer; R1: TRect; AColumn: TdxMasterViewColumn; begin Result := PtInRect(R, P); if Result then begin RowIndex := Node.HeaderRowIndexFromY(P.Y); ColIndex := Node.HeaderColIndexFromX(P.X); HitTestCode := htHeader; with Node.Level do for K := 0 to VisibleColumnCount - 1 do begin Column := VisibleColumns[K]; R1 := Column.GetHeaderBounds(Node); if PtInRect(R1, P) then begin if P.X < R1.Left + ColumnSizingZoneWidth then begin if Horizontal then begin List := TList.Create; try if Layout.HasLeftNeighbours(Column, List, True) then for J := 0 to List.Count - 1 do with TdxMasterViewColumn(List[J]) do if CanHorSizing then with GetHeaderBounds(Node) do if (Top <= P.Y) and (P.Y < Bottom) then begin Column := List[J]; HitTestCode := htHeaderRightEdge; Exit; end; finally List.Free; end; end else if (Column.ColIndex > 0) and (RowIndex < Layout[Column.ColIndex - 1].Count) then begin AColumn := Layout.Columns[RowIndex, Column.ColIndex - 1]; if AColumn.CanHorSizing and AColumn.IsWidthLinked then begin Column := AColumn; Dec(ColIndex); HitTestCode := htContentRightEdge; Exit; end; end; HitTestCode := htHeaderLeftEdge; Exit; end else if (P.X >= R1.Right - ColumnSizingZoneWidth) and (Column.Level.Horizontal and Column.CanHorSizing and (not AutoColumnWidth or not Column.Level.Layout.OneOnLine(Column)) or not Column.Level.Horizontal and Column.Level.CanHorSizing) then begin HitTestCode := htHeaderRightEdge; Exit; end; if P.Y < R1.Top + ColumnSizingZoneWidth then if FHorizontal then if Column.CanVerSizing and ((Column.RowIndex <> 0) or (Column.RowCount <> Column.MinRowCount)) then HitTestCode := htHeaderTopEdge else Exit else if Column.RowIndex = 0 then Exit else begin AColumn := Layout.Columns[Column.RowIndex - 1, Column.ColIndex]; if AColumn.CanVerSizing then begin Column := AColumn; HitTestCode := htHeaderBottomEdge; end; end else if (P.Y >= R1.Bottom - ColumnSizingZoneWidth) and Column.CanVerSizing then HitTestCode := htHeaderBottomEdge; Exit; end; end; Column := nil; end; end; begin Column := nil; RowIndex := -1; ColIndex := -1; if Customizing and TdxMVCustomizationForm(FCustomizationForm).MouseInBounds(P) then begin Node := nil; Result := htCustomizationForm; Exit; end; if PtInRect(ClientRect, P) then begin FromIndex := TopIndex; if FromIndex <> -1 then begin CalcViewInfo(TopIndex, -1, AVisibleItemCount, APartVisibleItemCount, True); for I := FromIndex to FromIndex + APartVisibleItemCount - 1 do begin Node := AbsoluteItems[I]; R := Node.FullBounds; if PtInRect(R, P) then begin // work with footer if Node.ViewInfo.FooterSize <> 0 then for J := 0 to Node.ViewInfo.EndLevelCount - 1 do begin R1 := Node.SubFooterBounds[J]; if PtInRect(R1, P) then begin RowIndex := Node.FooterRowIndexFromY(J, P.Y); ColIndex := Node.FooterColIndexFromX(J, P.X); Result := htFooter; with Node.Level.Parents[J] do for K := 0 to VisibleColumnCount - 1 do begin Column := VisibleColumns[K]; R1 := Column.GetFooterBounds(Node); if PtInRect(R1, P) then Exit; end; Column := nil; Exit; end else begin R2 := R1; R2.Right := R1.Left; R2.Left := R.Left; if PtInRect(R2, P) then begin Result := htLeftSpace; Exit; end else begin R1.Left := R1.Right; R1.Right := R.Right; if PtInRect(R1, P) then begin Result := htRightSpace; Exit; end; end; end; end; // work with groupby box R1 := Node.GroupByBoxBounds; if PtInRect(R1, P) then begin Result := htGroupByBox; if mvsColumnMoving in FState then begin for J := 0 to Node.Level.GroupColumnCount - 1 do begin Column := Node.Level.GroupColumns[J]; with Column.GetGroupByBoxBounds(Node) do R1.Right := (Left + Right) div 2; if PtInRect(R1, P) then begin ColIndex := J; Exit; end; R1.Left := R1.Right; end; ColIndex := Node.Level.GroupColumnCount; end else for J := 0 to Node.Level.GroupColumnCount - 1 do begin Column := Node.Level.GroupColumns[J]; R1 := Column.GetGroupByBoxBounds(Node); if PtInRect(R1, P) then begin Result := htHeader; Exit; end; end; Column := nil; Exit; end; // work with left space R1 := Node.LeftSpaceBounds; if PtInRect(R1, P) then begin if UseIndent and not PtInRect(Node.FullHeaderBounds, P) then Result := htIndent else Result := htLeftSpace; Exit; end; // work with right space R1 := Node.RightSpaceBounds; if PtInRect(R1, P) then begin RowIndex := Node.HeaderRowIndexFromY(P.Y); if Node.Level.Horizontal then if RowIndex <> -1 then if Node.Level.RowCount = 0 then ColIndex := 0 else ColIndex := Node.Level.Layout[RowIndex].Count else else ColIndex := Node.Level.Layout.Count; Result := htRightSpace; Exit; end; // work with header if Node.Level.Horizontal then if ProcessHeader(Node.HeaderBounds, Result) then Exit else else for J := 0 to Node.Level.Layout.Count - 1 do if ProcessHeader(Node.HeaderColBounds[J], Result) then Exit; // work with content R1 := Node.ContentBounds; R2 := R1; if UseIndent then begin R2.Right := R2.Left + ExpandButtonIndent; if PtInRect(R2, P) then begin Result := htIndent; Exit; end; end; if Node.HasExpandButton and Node.ShowExpandButton then begin R2.Right := R2.Left + LevelIndent; if PtInRect(R2, P) then begin RowIndex := Node.ContentRowIndexFromY(P.Y); Result := htExpandButton; Exit; end; R1.Left := R2.Right; end; // R2 := R1; if Node.ViewInfo.PreviewSize <> 0 then begin R2 := Node.PreviewBounds; if PtInRect(R2, P) then begin Result := htPreview; Exit; end; end; RowIndex := Node.ContentRowIndexFromY(P.Y); ColIndex := Node.ContentColIndexFromX(P.X); if PtInRect(R1, P) and (RowIndex <> -1) then Result := htContent else Result := htOther; if RowIndex <> -1 then with Node.Level do for J := 0 to VisibleColumnCount - 1 do begin Column := VisibleColumns[J]; R1 := Column.GetContentBounds(Node); if PtInRect(R1, P) then begin if not Horizontal then if P.X < R1.Left + ColumnSizingZoneWidth then if ShowHeader then if Column.Level.CanHorSizing then Result := htHeaderRightEdge else else Result := htContentLeftEdge else if (P.X >= R1.Right - ColumnSizingZoneWidth) and Column.CanHorSizing and Column.IsWidthLinked and (not AutoColumnWidth or (Column.Level.Layout.Count <> 1)) then Result := htContentRightEdge; Exit; end; end; Column := nil; Exit; end; end; end; end; Node := nil; Result := htNone; end; procedure TdxMasterView.LoadFromRegIniFile(const AFileName: string; AMode: TdxMasterViewRegIniFileMode); var ARegIniFile: TdxMVRegIniFile; Section: string; I: Integer; procedure LoadLevel(ALevel: TdxMasterViewLevel); var LevelSection, ColumnSection: string; I, W: Integer; procedure ReadOptionView(const AName: string; AValue: TdxMasterViewLevelOptionView); begin with ALevel do if ARegIniFile.ReadBool(LevelSection, AName, AValue in OptionsView) then OptionsView := OptionsView + [AValue] else OptionsView := OptionsView - [AValue]; end; procedure ReadHeaderOption(const AName: string; AValue: TdxMasterViewLevelOptionHeader); begin with ALevel do if ARegIniFile.ReadBool(LevelSection, AName, AValue in OptionsHeader) then OptionsHeader := OptionsHeader + [AValue] else OptionsHeader := OptionsHeader - [AValue]; end; procedure LoadColumn(AColumn: TdxMasterViewColumn); begin ColumnSection := LevelSection + '\' + AColumn.Name; with ARegIniFile, AColumn do if SectionExists(ColumnSection) then begin GroupIndex := ReadInteger(ColumnSection, 'GroupIndex', GroupIndex); RowIndex := ReadInteger(ColumnSection, 'RowIndex', RowIndex); ColIndex := ReadInteger(ColumnSection, 'ColIndex', ColIndex); RowCount := ReadInteger(ColumnSection, 'RowCount', RowCount); SortIndex := ReadInteger(ColumnSection, 'SortIndex', SortIndex); SortOrder := TdxMasterViewSortOrder( ReadInteger(ColumnSection, 'SortOrder', Integer(SortOrder))); FLeaveSortOrder := ReadBool(ColumnSection, 'LeaveSortOrder', FLeaveSortOrder); Visible := ReadBool(ColumnSection, 'Visible', Visible); if ValueExists(ColumnSection, 'Width') then Width := ReadInteger(ColumnSection, 'Width', Width); end; end; begin if Section = '' then LevelSection := ALevel.Name else LevelSection := Section + '\' + ALevel.Name; with ARegIniFile, ALevel do if SectionExists(LevelSection) then begin ReadOptionView('ShowCaption', lovCaption); ReadOptionView('ShowFooter', lovFooter); ReadOptionView('ShowGrid', lovGrid); ReadOptionView('ShowGridWithPreview', lovGridWithPreview); ReadOptionView('ShowGroupByBox', lovGroupByBox); ReadOptionView('ShowHeader', lovHeader); ReadOptionView('ShowPreview', lovPreview); ReadOptionView('OccupyRestSpace', lovOccupyRestSpace); ReadHeaderOption('shForFirstNode', lohForFirstNode); ReadHeaderOption('shForFirstVisibleNode', lohForFirstVisibleNode); ReadHeaderOption('shAfterExpandedNode', lohAfterExpandedNode); ViewMode := TdxMasterViewViewMode( ReadInteger(LevelSection, 'ViewMode', Integer(ViewMode))); if not FHorizontal then begin I := 0; repeat W := ReadInteger(LevelSection, 'HeaderWidth' + IntToStr(I), -2); if W = -2 then Break; FLayout.Count := I + 1; if W <> -1 then FLayout.HeaderWidths[I] := W; Inc(I); until False; end; for I := 0 to ColumnCount - 1 do LoadColumn(Columns[I]); end; end; begin if AFileName = '' then Exit; for I := 0 to AbsoluteLevelCount - 1 do AbsoluteLevels[I].BeginLoading; try ARegIniFile := TdxMVRegIniFile.Create(AFileName, AMode); with ARegIniFile do try case AMode of riIniFile: Section := Name; riRegistry: Section := ''; end; if not SectionExists(Section) then Exit; if ReadBool(Section, 'Animation', movAnimation in OptionsView) then OptionsView := OptionsView + [movAnimation] else OptionsView := OptionsView - [movAnimation]; if ReadBool(Section, 'AutoColumnWidth', movAutoColumnWidth in OptionsView) then OptionsView := OptionsView + [movAutoColumnWidth] else OptionsView := OptionsView - [movAutoColumnWidth]; for I := 0 to AbsoluteLevelCount - 1 do LoadLevel(AbsoluteLevels[I]); finally Free; end; finally for I := AbsoluteLevelCount - 1 downto 0 do AbsoluteLevels[I].EndLoading; ShowFocusedNode; end; end; function TdxMasterView.NodeFromID(ALevel: TdxMasterViewLevel; const AID: Variant): TdxMasterViewNode; procedure FindNode(Root: TdxMasterViewNode); var I: Integer; begin with Root do if (NodeType = ntData) and (Level = ALevel.Parent) {or (NodeType = ntCaption) and (Level = ALevel)} then Result := NodeFromID(ALevel, AID) else for I := 0 to Count - 1 do begin FindNode(Items[I]); if Result <> nil then Break; end; end; begin Result := nil; if VarIsEmpty(AID) then Exit; FindNode(FItems); end; function TdxMasterView.NodeFromKeyValue(ALevel: TdxMasterViewLevel; AChildIndex: Integer; const AKeyValue: Variant): TdxMasterViewNode; procedure FindNode(Root: TdxMasterViewNode); var I: Integer; begin with Root do if (NodeType = ntData) and (Level = ALevel.Parent) {or (NodeType = ntCaption) and (Level = ALevel)} then Result := NodeFromKeyValue(ALevel, AChildIndex, AKeyValue) else for I := 0 to Count - 1 do begin FindNode(Items[I]); if Result <> nil then Break; end; end; begin Result := nil; if VarIsEmpty(AKeyValue) or (ALevel[AChildIndex].MasterKeyFieldCount = 0) then Exit; FindNode(FItems); end; procedure TdxMasterView.SaveToRegIniFile(const AFileName: string; AMode: TdxMasterViewRegIniFileMode); var ARegIniFile: TdxMVRegIniFile; Section: string; I: Integer; procedure SaveLevel(ALevel: TdxMasterViewLevel); var I, W: Integer; LevelSection, ColumnSection: string; procedure SaveColumn(AColumn: TdxMasterViewColumn); begin ColumnSection := LevelSection + '\' + AColumn.Name; with ARegIniFile, AColumn do begin WriteInteger(ColumnSection, 'GroupIndex', GroupIndex); WriteInteger(ColumnSection, 'RowIndex', RowIndex); WriteInteger(ColumnSection, 'ColIndex', ColIndex); WriteInteger(ColumnSection, 'RowCount', RowCount); WriteInteger(ColumnSection, 'SortIndex', SortIndex); WriteInteger(ColumnSection, 'SortOrder', Integer(SortOrder)); WriteBool(ColumnSection, 'LeaveSortOrder', FLeaveSortOrder); WriteBool(ColumnSection, 'Visible', Visible); if cvWidth in AssignedValues then WriteInteger(ColumnSection, 'Width', Width); end; end; begin if Section = '' then LevelSection := ALevel.Name else LevelSection := Section + '\' + ALevel.Name; with ARegIniFile, ALevel do begin WriteBool(LevelSection, 'ShowCaption', ShowCaption); WriteBool(LevelSection, 'ShowFooter', ShowFooter); WriteBool(LevelSection, 'ShowGrid', ShowGrid); WriteBool(LevelSection, 'ShowGridWithPreview', ShowGridWithPreview); WriteBool(LevelSection, 'ShowGroupByBox', ShowGroupByBox); WriteBool(LevelSection, 'ShowHeader', ShowHeader); WriteBool(LevelSection, 'ShowPreview', ShowPreview); WriteBool(LevelSection, 'OccupyRestSpace', OccupyRestSpace); WriteBool(LevelSection, 'shForFirstNode', lohForFirstNode in OptionsHeader); WriteBool(LevelSection, 'shForFirstVisibleNode', lohForFirstVisibleNode in OptionsHeader); WriteBool(LevelSection, 'shAfterExpandedNode', lohAfterExpandedNode in OptionsHeader); WriteInteger(LevelSection, 'ViewMode', Ord(ViewMode)); if not FHorizontal then for I := 0 to FLayout.Count - 1 do begin if FLayout.HeaderWidthAssigned[I] then W := FLayout.HeaderWidths[I] else W := -1; WriteInteger(LevelSection, 'HeaderWidth' + IntToStr(I), W); end; for I := 0 to ColumnCount - 1 do SaveColumn(Columns[I]); end; end; begin if AFileName = '' then Exit; ARegIniFile := TdxMVRegIniFile.Create(AFileName, AMode); with ARegIniFile do try case AMode of riIniFile: Section := Name; riRegistry: Section := ''; end; WriteBool(Section, 'Animation', movAnimation in FOptionsView); WriteBool(Section, 'AutoColumnWidth', movAutoColumnWidth in FOptionsView); for I := 0 to AbsoluteLevelCount - 1 do SaveLevel(AbsoluteLevels[I]); finally Free; end; end; procedure TdxMasterView.SelectItems(FromIndex, ToIndex: Integer; Select: Boolean); var I: Integer; begin if FromIndex < 0 then FromIndex := 0; if FromIndex >= AbsoluteItemCount then FromIndex := AbsoluteItemCount - 1; if ToIndex < 0 then ToIndex := 0; if ToIndex >= AbsoluteItemCount then ToIndex := AbsoluteItemCount - 1; if FromIndex > ToIndex then begin I := FromIndex; FromIndex := ToIndex; ToIndex := I; end; BeginSelection; try for I := FromIndex to ToIndex do AbsoluteItems[I].Selected := Select; finally EndSelection; end; end; procedure TdxMasterView.ShowFocusedNode; begin if (FocusedNode = nil) or (FUpdateLockCount <> 0) then Exit; CheckFocusedNode; if (FocusedIndex < TopIndex) or (FocusedIndex > TopIndex + VisibleItemCount - 1) then begin Freeze; try ChangeFocusedIndex(FocusedIndex); finally Unfreeze; end; end; end; procedure TdxMasterView.UpdateData; begin if FLevels <> nil then FLevels.LevelChanged(True); end; initialization Screen.Cursors[crdxMasterViewMirror] := LoadCursor(HInstance, 'DXMASTERVIEWMIRRORCURSOR'); Screen.Cursors[crdxMasterViewFullScroll] := LoadCursor(HInstance, 'DXMASTERVIEWFULLSCROLLCURSOR'); Screen.Cursors[crdxMasterViewHorScroll] := LoadCursor(HInstance, 'DXMASTERVIEWHORSCROLLCURSOR'); Screen.Cursors[crdxMasterViewVerScroll] := LoadCursor(HInstance, 'DXMASTERVIEWVERSCROLLCURSOR'); Screen.Cursors[crdxMasterViewUpScroll] := LoadCursor(HInstance, 'DXMASTERVIEWUPSCROLLCURSOR'); Screen.Cursors[crdxMasterViewRightScroll] := LoadCursor(HInstance, 'DXMASTERVIEWRIGHTSCROLLCURSOR'); Screen.Cursors[crdxMasterViewDownScroll] := LoadCursor(HInstance, 'DXMASTERVIEWDOWNSCROLLCURSOR'); Screen.Cursors[crdxMasterViewLeftScroll] := LoadCursor(HInstance, 'DXMASTERVIEWLEFTSCROLLCURSOR'); Screen.Cursors[crdxMasterViewHorSize] := LoadCursor(HInstance, 'DXMASTERVIEWHORSIZECURSOR'); Screen.Cursors[crdxMasterViewVerSize] := LoadCursor(HInstance, 'DXMASTERVIEWVERSIZECURSOR'); Screen.Cursors[crdxMasterViewRemove] := LoadCursor(HInstance, 'DXMASTERVIEWREMOVECURSOR'); Screen.Cursors[crdxMasterViewNoDrop] := LoadCursor(HInstance, 'DXMASTERVIEWNODROPCURSOR'); RegisterClasses([TdxMasterViewStyle, TdxMasterViewLevel]); RegColumnList := TList.Create; dxMasterViewRegisterColumn(TdxMasterViewColumn); dxMVGroupByBoxText := LoadStr(dxsMVGroupByBoxText); WndProcHandlers := TList.Create; finalization WndProcHandlers.Free; dxMasterViewUnregisterColumn(TdxMasterViewColumn); with RegColumnList do begin while Count > 0 do begin Dispose(PRegColumnRecord(Last)); Remove(Last); end; Free; end; end.