git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.DevExpressVCL@38 05c56307-c608-d34a-929d-697000501d7a
8051 lines
253 KiB
ObjectPascal
8051 lines
253 KiB
ObjectPascal
|
|
{*******************************************************************}
|
|
{ }
|
|
{ Developer Express Visual Component Library }
|
|
{ ExpressQuantumGrid Suite }
|
|
{ }
|
|
{ Copyright (c) 1998-2009 Developer Express Inc. }
|
|
{ ALL RIGHTS RESERVED }
|
|
{ }
|
|
{ The entire contents of this file is protected by U.S. and }
|
|
{ International Copyright Laws. Unauthorized reproduction, }
|
|
{ reverse-engineering, and distribution of all or any portion of }
|
|
{ the code contained in this file is strictly prohibited and may }
|
|
{ result in severe civil and criminal penalties and will be }
|
|
{ prosecuted to the maximum extent possible under the law. }
|
|
{ }
|
|
{ RESTRICTIONS }
|
|
{ }
|
|
{ THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES }
|
|
{ (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE }
|
|
{ SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS }
|
|
{ LICENSED TO DISTRIBUTE THE EXPRESSGRID 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 dxDBGrid;
|
|
|
|
interface
|
|
|
|
{$I dxTLVer.inc}
|
|
|
|
uses
|
|
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
|
|
Db, dxTL, Mask, dxTLStr, dxCntner, dxEditor, dxDBCtrl,
|
|
dxExEdtr, dxUtils, dxFilter{$IFDEF DELPHI6}, Variants{$ENDIF};
|
|
|
|
const
|
|
{for AGroupPanel}
|
|
agpHOfs = 8;
|
|
agpHWidht = 100;
|
|
agpHDeltaX = 4;
|
|
agpHDeltaY = 0;
|
|
|
|
{Paint}
|
|
dxclGroupPanel = clBtnShadow;
|
|
dxclGroupPanelText = clBtnFace;
|
|
dxclGroupPanelLine = clBtnText;
|
|
|
|
type
|
|
{ TCustomdxDBGrid }
|
|
TdxDBGridOption = (egoColumnSizing, egoColumnMoving, egoEditing, egoTabs, egoTabThrough,
|
|
egoRowSelect, egoMultiSelect, egoCanDelete, egoConfirmDelete, egoCanNavigation,
|
|
egoCanAppend, egoCancelOnExit, egoImmediateEditor, egoCanInsert, egoLoadAllRecords,
|
|
egoPreview, egoDrawEndEllipsis, egoStoreToRegistry, egoAutoWidth, egoShowHourGlass,
|
|
egoCaseInsensitive, egoDblClick, egoIndicator, egoExtMultiSelect, egoResetColumnFocus,
|
|
egoExtCustomizing, egoHideFocusRect, egoStoreToIniFile, egoUseLocate, egoSmartReload,
|
|
egoSmartRefresh, egoSeekDetail);
|
|
TdxDBGridOptions = set of TdxDBGridOption;
|
|
|
|
TdxDBGridOptionEx = (egoInvertSelect, egoUseBitmap, egoBandHeaderWidth, egoAutoCalcPreviewLines,
|
|
egoBandSizing, egoBandMoving, egoHorzThrough, egoVertThrough, egoCellMultiSelect,
|
|
egoEnterThrough, egoEnterShowEditor, egoFullSizing, egoDragScroll, egoDragExpand,
|
|
egoDragCollapse, egoRowAutoHeight, egoBandButtonClicking, egoBandPanelSizing,
|
|
egoHeaderButtonClicking, egoHeaderPanelSizing, egoRowSizing, egoAutoSort,
|
|
egoNotHideColumn, egoMultiSort, egoShowButtonAlways, egoAutoHeaderPanelHeight,
|
|
egoKeepColumnWidth, egoSyncSelection, egoCollapsedReload, egoAnsiSort,
|
|
egoMouseScroll, egoAutoSearch);
|
|
TdxDBGridOptionsEx = set of TdxDBGridOptionEx;
|
|
|
|
// New Options (HotTrack, SimpleButton)
|
|
TdxDBGridOptionBehavior = (edgoAnsiSort, edgoAutoCopySelectedToClipboard,
|
|
edgoAutoSearch, edgoAutoSort,
|
|
edgoBandButtonClicking, edgoCaseInsensitive, edgoCellMultiSelect,
|
|
edgoCollapsedReload, edgoDblClick, edgoDragCollapse, edgoDragExpand, edgoDragScroll,
|
|
edgoEditing, edgoEnterShowEditor, edgoEnterThrough, edgoExtMultiSelect,
|
|
edgoHeaderButtonClicking, edgoHorzThrough, edgoImmediateEditor, edgoMouseScroll,
|
|
edgoMultiSelect, edgoMultiSort, edgoSeekDetail, edgoShowHourGlass, edgoStoreToIniFile,
|
|
edgoStoreToRegistry, edgoTabs, edgoTabThrough, edgoVertThrough);
|
|
|
|
TdxDBGridOptionCustomize = (edgoBandMoving, edgoBandPanelSizing, edgoBandSizing,
|
|
edgoColumnMoving, edgoColumnSizing, edgoExtCustomizing, edgoFullSizing,
|
|
edgoHeaderPanelSizing, edgoKeepColumnWidth, edgoNotHideColumn, edgoRowSizing);
|
|
|
|
TdxDBGridOptionDB = (edgoCanAppend, edgoCancelOnExit, edgoCanDelete, edgoCanInsert,
|
|
edgoCanNavigation, edgoConfirmDelete, edgoLoadAllRecords, edgoPartialLoad,
|
|
edgoResetColumnFocus, edgoSmartRefresh, edgoSmartReload, edgoSyncSelection,
|
|
edgoUseBookmarks, edgoUseLocate);
|
|
|
|
TdxDBGridOptionView = (edgoAutoCalcPreviewLines, edgoAutoHeaderPanelHeight,
|
|
edgoAutoWidth, edgoBandHeaderWidth, edgoDrawEndEllipsis, edgoHideFocusRect,
|
|
edgoHotTrack, edgoIndicator, edgoInvertSelect, edgoPreview, edgoRowAutoHeight,
|
|
edgoRowSelect, edgoShowButtonAlways, edgoUseBitmap);
|
|
|
|
TdxDBGridOptionsBehavior = set of TdxDBGridOptionBehavior;
|
|
TdxDBGridOptionsCustomize = set of TdxDBGridOptionCustomize;
|
|
TdxDBGridOptionsDB = set of TdxDBGridOptionDB;
|
|
TdxDBGridOptionsView = set of TdxDBGridOptionView;
|
|
|
|
// Add FilterOptions(CaseIns)
|
|
const
|
|
dxDBGridDefaultOptionsBehavior = [edgoAutoSort,
|
|
edgoDragScroll, edgoEditing, edgoEnterShowEditor,
|
|
edgoImmediateEditor, edgoTabThrough, edgoVertThrough];
|
|
dxDBGridDefaultOptionsCustomize = [edgoBandMoving, edgoBandSizing, edgoColumnMoving,
|
|
edgoColumnSizing];
|
|
dxDBGridDefaultOptionsDB = [edgoCancelOnExit, edgoCanDelete, edgoCanInsert,
|
|
edgoCanNavigation, edgoConfirmDelete, edgoUseBookmarks];
|
|
dxDBGridDefaultOptionsView = [edgoBandHeaderWidth, edgoUseBitmap];
|
|
|
|
type
|
|
TCustomdxDBGrid = class;
|
|
TdxDBGridFilterPopupListBox = class;
|
|
TdxDBGridFilter = class;
|
|
TdxDBGridFilterClass = class of TdxDBGridFilter;
|
|
|
|
{ TdxDBGridColumn }
|
|
TdxDBGridColumn = class(TdxDBTreeListColumn)
|
|
public
|
|
property SummaryGroup;
|
|
published
|
|
property DisableFilter;
|
|
property GroupIndex;
|
|
property SummaryType;
|
|
property SummaryField;
|
|
property SummaryFormat;
|
|
property SortBySummary;
|
|
property OnSummary;
|
|
property SummaryGroupName;
|
|
|
|
property SummaryFooterType;
|
|
property SummaryFooterField;
|
|
property SummaryFooterFormat;
|
|
property OnSummaryFooter;
|
|
property OnDrawSummaryFooter;
|
|
end;
|
|
|
|
{ TdxGridDataLink }
|
|
TdxGridDataLink = class(TdxDBTreeListControlDataLink)
|
|
private
|
|
function GetDBGrid: TCustomdxDBGrid;
|
|
protected
|
|
// override TDataLink
|
|
procedure FocusControl(Field: TFieldRef); override;
|
|
procedure LayoutChanged; override;
|
|
property DBGrid: TCustomDxDBGrid read GetDBGrid;
|
|
end;
|
|
|
|
{ TdxDBGridNode }
|
|
TSummaryValue = record
|
|
Value: Extended;
|
|
AssignedValue: Boolean;
|
|
end;
|
|
|
|
PSummaryList =^TSummaryList;
|
|
TSummaryList = array[0..(MaxInt div 2) div SizeOf(TSummaryValue)] of TSummaryValue;
|
|
|
|
TdxDBGridNode = class(TdxDBTreeListControlNode)
|
|
private
|
|
FGroupId: Variant; {group id}
|
|
FSummary: Extended; {summary value}
|
|
FAssignedSummary: Boolean; {summary value no asigned if False}
|
|
FRecNo: Integer; {RecNo -> MoveBy}
|
|
FSummaryList: PSummaryList; {multi summary values}
|
|
function GetSummaryValue: Extended;
|
|
function GetMultiSummaryValue(Index: Integer): Extended;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure Delete; override;
|
|
property GroupId: Variant read FGroupId;
|
|
property RecNo: Integer read FRecNo;
|
|
property SummaryValue: Extended read GetSummaryValue;
|
|
property SummaryValues[Index: Integer]: Extended read GetMultiSummaryValue;
|
|
end;
|
|
|
|
TdxDBTreeListColumnClick = procedure(Sender: TObject; Column: TdxDBTreeListColumn) of object;
|
|
TdxDBTreeListColumnSorting = procedure(Sender: TObject; Column: TdxDBTreeListColumn; var Allow: Boolean) of object;
|
|
TShowColumnEvent = procedure(Sender: TObject; Column: TdxDBTreeListColumn) of object;
|
|
THideColumnEvent = procedure(Sender: TObject; Column: TdxDBTreeListColumn) of object;
|
|
|
|
TAddGroupColumnEvent = procedure(Sender: TObject; Column: TdxDBTreeListColumn; var Allow: Boolean) of object;
|
|
TEndDragGroupColumn = procedure(Sender: TObject; Column: TdxDBTreeListColumn;
|
|
NewGroupIndex: Integer; var Allow: Boolean) of object;
|
|
TdxDBGridBackgroundDrawEvent = procedure(Sender: TObject; ACanvas: TCanvas; ARect: TRect) of object;
|
|
|
|
TdxCustomSummaryTextEvent = procedure(Sender: TObject; Node: TdxTreeListNode; var Text: string) of object;
|
|
TdxSummaryNodeEvent = procedure(Sender: TObject; Node: TdxTreeListNode; DataSet: TDataSet; var Value: Extended) of object;
|
|
|
|
TdxDBGridFilterChangedEvent = procedure(Sender: TObject; ADataSet: TDataSet; const AFilterText: string) of object;
|
|
TdxDBGridEnumFilterValuesEvent = procedure(Sender: TObject; AColumn: TdxDBTreeListColumn;
|
|
var AValue: Variant; var ADisplayText: string; var ARepeatEnumeration: Boolean) of object;
|
|
|
|
TdxChangeNodeInfo = record
|
|
Id: Variant;
|
|
Level: Integer;
|
|
Selected: Boolean;
|
|
end;
|
|
|
|
TdxDBGridReloadMode = (rlUnknown, rlGridMode, rlAllRecords);
|
|
TdxDBGridLoadMode = (lmCurrent, lmNext, lmPrior, lmAllRecords);
|
|
TdxDBGridResyncMode = (rmNone, rmTop, rmBottom);
|
|
|
|
TCustomdxDBGrid = class(TCustomdxDBTreeListControl)
|
|
private
|
|
// DataSet, Navigation, Sync
|
|
DataChangedBusy: Boolean;
|
|
DataChangedLocate, DataChangedLocateResync: Boolean;
|
|
FDataLink: TdxGridDataLink;
|
|
FDefaultFields: Boolean;
|
|
FGridBOF, FGridEOF: Boolean;
|
|
FInUpdateActive: Boolean;
|
|
FLocateByNode: Boolean;
|
|
FLockPrepareNode: Integer; // Prepare Node - synchronizing with DataSet
|
|
FOldActiveRecord: Integer; // Prepare Node - synchronizing with DataSet
|
|
FOldTopVisibleId: Variant;
|
|
FOldTopVisibleLevel: Integer;
|
|
FPartialLoadBufferCount: Integer;
|
|
FPrevId: Variant;
|
|
FPrevNodeInfo: TdxChangeNodeInfo;
|
|
FTopVisibleUpdate: Integer;
|
|
FUpdateBufferCount: Boolean;
|
|
FUpdatingNode: TdxDBGridNode;
|
|
// Grouping
|
|
FDragGroupColumn: TdxDBTreeListColumn;
|
|
FFlagChangedGroupColumn: Boolean;
|
|
FGroupColumnDragFlag: Boolean;
|
|
FGroupColumnPointDragging: TPoint;
|
|
FGroupColumns: TList;
|
|
FGroupFields: TStrings;
|
|
FLockGrouping: Integer;
|
|
FPrevReloadMode: TdxDBGridReloadMode;
|
|
FRefreshGroupColumns: Boolean;
|
|
FRefreshGroupList: Boolean;
|
|
// Summary
|
|
FSummaryAbsoluteList: TList;
|
|
FSummaryGroups: TdxDBGridSummaryGroups;
|
|
FSummarySeparator: string;
|
|
FTotalSummaryCount: Integer;
|
|
// Selection
|
|
FBkmSelectionAnchor: TBookmarkStr;
|
|
FSaveBkmList: TStringList;
|
|
FSaveIdList: TList;
|
|
FSelectAllFlag: Boolean;
|
|
// Style
|
|
FGroupPanelColor: TColor;
|
|
FGroupPanelFontColor: TColor;
|
|
FGroupPanelVisible: Boolean;
|
|
FOldFocusedColumn: Integer;
|
|
FOptions: TdxDBGridOptions;
|
|
FOptionsEx: TdxDBGridOptionsEx;
|
|
FOptionsBehavior: TdxDBGridOptionsBehavior;
|
|
FOptionsCustomize: TdxDBGridOptionsCustomize;
|
|
FOptionsDB: TdxDBGridOptionsDB;
|
|
FOptionsView: TdxDBGridOptionsView;
|
|
// Filter
|
|
FFiltering: Boolean;
|
|
FFilter: TdxDBGridFilter;
|
|
FFilterPopupListBox: TdxDBGridFilterPopupListBox;
|
|
FFilterStream: TStream;
|
|
// Events
|
|
FOnAddGroupColumn: TAddGroupColumnEvent;
|
|
FOnBackgroundDrawEvent: TdxDBGridBackgroundDrawEvent;
|
|
FOnClearNodeData: TNotifyEvent;
|
|
FOnColumnSorting: TdxDBTreeListColumnSorting;
|
|
FOnEndDragGroupColumn: TEndDragGroupColumn;
|
|
FOnGetCustomSummaryText: TdxCustomSummaryTextEvent;
|
|
FOnReloadGroupList: TNotifyEvent;
|
|
FOnSummaryNode: TdxSummaryNodeEvent;
|
|
// obsolete
|
|
FOnColumnClick: TdxDBTreeListColumnClick;
|
|
FOnCustomDraw: TCustomDrawEvent;
|
|
FOnHideColumnEvent: THideColumnEvent;
|
|
FOnShowColumnEvent: TShowColumnEvent;
|
|
// Filter
|
|
FOnFilterChanged: TdxDBGridFilterChangedEvent;
|
|
FOnEnumFilterValues: TdxDBGridEnumFilterValuesEvent;
|
|
|
|
function CalcGroupPanelHeight(ColCount: Integer): Integer;
|
|
function ChangedGroupFields: Boolean;
|
|
procedure CheckChangeNodeEx;
|
|
procedure ClearBookmarks;
|
|
procedure ClearIdList;
|
|
procedure ClearGroupFields;
|
|
function FindIdVariant(const Id: Variant; var Index: Integer): Boolean;
|
|
function GetFilter: TdxDBGridFilter;
|
|
function GetFooterPanelVisible: Boolean;
|
|
function GetGroupPanelVisible: Boolean;
|
|
function GetOptions: TdxDBGridOptions;
|
|
function GetOptionsEx: TdxDBGridOptionsEx;
|
|
function GetOptionsBehavior: TdxDBGridOptionsBehavior;
|
|
function GetOptionsCustomize: TdxDBGridOptionsCustomize;
|
|
function GetOptionsDB: TdxDBGridOptionsDB;
|
|
function GetOptionsView: TdxDBGridOptionsView;
|
|
function GetPartialLoad: Boolean;
|
|
function GetUseBookmarks: Boolean;
|
|
procedure InsertNode(FAppend: Boolean);
|
|
procedure LoadChangeNodeInfo(var NodeInfo: TdxChangeNodeInfo);
|
|
function RestoreBookmark(ANode: TdxTreeListNode; const ABookmark: TBookmarkStr; ABackward: Boolean): Boolean;
|
|
procedure RestoreSelected(ANode: TdxTreeListNode);
|
|
procedure SaveBookmarks;
|
|
procedure SaveGroupFields;
|
|
procedure ScrollGroupPanel;
|
|
procedure SetDataSource(Value: TDataSource);
|
|
procedure SetDefaultFields(Value: Boolean);
|
|
procedure SetFilter(Value: TdxDBGridFilter);
|
|
procedure SetFooterPanelVisible(Value: Boolean);
|
|
procedure SetGroupPanelColor(Value: TColor);
|
|
procedure SetGroupPanelFontColor(Value: TColor);
|
|
procedure SetGroupPanelVisible(Value : Boolean);
|
|
procedure SetOptions(Value: TdxDBGridOptions);
|
|
procedure SetOptionsEx(Value: TdxDBGridOptionsEx);
|
|
// New Options
|
|
procedure SetOptionsBehavior(Value: TdxDBGridOptionsBehavior);
|
|
procedure SetOptionsCustomize(Value: TdxDBGridOptionsCustomize);
|
|
procedure SetOptionsDB(Value: TdxDBGridOptionsDB);
|
|
procedure SetOptionsView(Value: TdxDBGridOptionsView);
|
|
procedure SetPartialLoad(Value: Boolean);
|
|
procedure SetPartialLoadBufferCount(Value: Integer);
|
|
procedure SetSummaryGroups(Value: TdxDBGridSummaryGroups);
|
|
procedure SetSummarySeparator(Value: string);
|
|
procedure SetUseBookmarks(Value: Boolean);
|
|
// dataset
|
|
function InternalGridMode: Boolean;
|
|
procedure UpdateActive;
|
|
// messages
|
|
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
|
|
procedure CMDesignHitTest(var Msg: TCMDesignHitTest); message CM_DESIGNHITTEST;
|
|
protected
|
|
FlagSmartReload: Boolean;
|
|
FlagFullRefresh: Boolean;
|
|
|
|
// override TComponent
|
|
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 WndProc(var Message: TMessage); override;
|
|
// Size
|
|
function GetGroupHeaderRect(Index: Integer): TRect;
|
|
function GetGroupPanelHeight: Integer; override;
|
|
function GetIndentWidth: Integer; override;
|
|
function GetScrollVertGridRect: TRect; override;
|
|
function GetVisibleRowCount: Integer; override;
|
|
procedure GetVScrollInfo(var Min, Max, Pos : Integer; var Page, Mask : UINT); override;
|
|
procedure UpdateRowCount; override;
|
|
procedure UpdateScrollBars; override;
|
|
// Drag & Drop & Customizing
|
|
procedure CalcArrowsPos(var P: TPoint; PPosInfo: Pointer; IsBand: Boolean; DownIndex, DragIndex: Integer); override;
|
|
procedure DoEndDragGroupColumn(Column: TdxDBTreeListColumn; NewGroupIndex: Integer; var Accept: Boolean); virtual;
|
|
procedure EndDragHeader(Flag: Boolean); override;
|
|
function GetColumnArrowsPos(Pos: TPoint): Integer;
|
|
procedure GetDragImageCursor(P: TPoint; var ADragCursor: TCursor); override;
|
|
function GetIsCustomizing: Boolean; override;
|
|
function IsHeaderCustomizing: Boolean; override;
|
|
procedure HideHeader(AbsoluteIndex: Integer); override;
|
|
function IsLastNode(ANode: TdxTreeListNode): Boolean; override;
|
|
function IsTopNode(ANode: TdxTreeListNode): Boolean; override;
|
|
procedure ShowColumnHeader(BandIndex, RowIndex, ColIndex, AbsoluteIndex: Integer); override;
|
|
// Paint
|
|
function AssignedDrawCellEvent(ANode: TdxTreeListNode; AbsoluteIndex: Integer): Boolean; override;
|
|
procedure DrawGroupPanel(ACanvas: TCanvas; ARect: TRect; HeaderBrush, PanelBrush: HBRUSH); override;
|
|
procedure DoDrawCell(ACanvas: TCanvas; var ARect: TRect; ANode: TdxTreeListNode; AIndex: Integer; ASelected, AFocused: Boolean;
|
|
ANewItemRow: Boolean; ALeftEdge, ARightEdge: Boolean; ABrush: HBRUSH;
|
|
var AText: string; var AColor: TColor; AFont: TFont; var AAlignment: TAlignment; var ADone: Boolean); override;
|
|
procedure DoHeaderClick(AbsoluteIndex: Integer); override;
|
|
procedure DoHeaderDropDownButtonClick(AbsoluteIndex: Integer); override;
|
|
procedure DoStatusCloseButtonClick; override;
|
|
function GetGridColor(ABrushColor: TColor): TColor; override;
|
|
procedure PrepareNode(ANode: TdxTreeListNode); override;
|
|
procedure UnPrepareNode(ANode: TdxTreeListNode); override;
|
|
// Editor
|
|
function CanEditShow: Boolean; override;
|
|
procedure DoBeforeEditing(Node: TdxTreeListNode; var AllowEditing: Boolean); override;
|
|
// Selected
|
|
function CanFullMultiSelect: Boolean; override;
|
|
function CanNodeSelected(ANode: TdxTreeListNode): Boolean; override;
|
|
function CompareSelectionAnchor(ANode: TdxTreeListNode): Integer; override;
|
|
procedure DoSelectedCountChange; override;
|
|
procedure InvalidateSelection; override;
|
|
function IsNodeSelected(ANode: TdxTreeListNode): Boolean; override;
|
|
procedure NodeSelected(ANode: TdxTreeListNode; ASelected: Boolean); override;
|
|
procedure SelectNodes(N1, N2: TdxTreeListNode); override;
|
|
procedure SelectRecords(ADirectionUp: Boolean; ACount: Integer);
|
|
procedure SetSelectionAnchor(ANode: TdxTreeListNode); override;
|
|
// Sorting
|
|
procedure AddSortedColumn(Column: TdxTreeListColumn); override;
|
|
function CanColumnSorting(Column: TdxTreeListColumn): Boolean; override;
|
|
procedure CheckSorted;
|
|
procedure DoSortColumn(StartIndex, ColIndex: Integer; FlagDesc: Boolean); override;
|
|
function IsAutoFilter: Boolean; override;
|
|
function IsAutoSort: Boolean; override;
|
|
function IsFilterStatusVisible: Boolean; override;
|
|
function IsMultiSort: Boolean; override;
|
|
function IsMultiSortColumn(AbsoluteIndex: Integer): Boolean; override;
|
|
function IsSummaryColumn(ColIndex: Integer): Boolean;
|
|
function IsUseLocate: Boolean; virtual;
|
|
procedure SetColumnSorted(Column: TdxTreeListColumn); override;
|
|
// Grouping
|
|
procedure ChangedGroupColumn(Column: TdxDBTreeListColumn); override;
|
|
function FindGroupNode(StartNode: TdxDBGridNode; Value: Variant;
|
|
var Node: TdxDBGridNode; FlagDesc: Boolean; StartIndex: Integer) : Boolean;
|
|
function GetGroupColumns(Index: Integer ): TdxDBGridColumn;
|
|
function GetGroupColumnsCount: Integer;
|
|
function IsSmartReload: Boolean;
|
|
function IsSmartRefresh: Boolean;
|
|
procedure MoveNodesToRoot;
|
|
// Summary
|
|
procedure AssignSummaryFields; override;
|
|
procedure AssignSummaryFooterFields; override;
|
|
procedure CalcSummary(SmartFlag: Boolean); // Count , Average
|
|
procedure DoClearNodeData; virtual;
|
|
function FindSummaryGroup(Column: TdxDBTreeListColumn): TdxDBGridSummaryGroup;
|
|
function GetCustomSummaryText(Node: TdxTreeListNode): string;
|
|
function GetSummaryText(ANode: TdxTreeListNode; AColumn: TdxDBTreeListColumn): string;
|
|
function GetSummaryValue(ANode: TdxTreeListNode): Extended; override;
|
|
procedure LoadSummaryFooterValues(ADetailNode: TDxDBGridNode; List: TList);
|
|
procedure LoadSummaryValues(ANode, ADetailNode: TDxDBGridNode; List: TList);
|
|
procedure MakeSummaryFieldList(List: TList);
|
|
procedure MakeSummaryFooterFieldList(List: TList);
|
|
procedure ReCalcSummary(ADetailNode: TdxDBGridNode);
|
|
procedure RefreshSummaryItems(const PrevName, NewName: string); override;
|
|
procedure SortingBySummary;
|
|
// Style
|
|
function CanDblClick: Boolean; override;
|
|
procedure EndCustomLayout; override;
|
|
function IsSmartRecalcRowHeight: Boolean; override;
|
|
function IsVScrollBarDisableHide: Boolean; override;
|
|
// based override
|
|
procedure ClearListNodes; override;
|
|
function CreateNode: TdxTreeListNode; override;
|
|
procedure DoMouseWHeelScroll(AScrollUp: Boolean; AScrollLines: Integer); override;
|
|
function GetCellAlignment(Node: TdxTreeListNode; AbsoluteIndex: Integer): TAlignment; override;
|
|
function GetDataLink: TdxDBTreeListControlDataLink; override;
|
|
function GetDataSource: TDataSource; override;
|
|
function GetFooterCellText(Node: TdxTreeListNode; AbsoluteIndex, FooterIndex: Integer): string; override;
|
|
function GetNodeString(Node: TdxTreeListNode; Column: Integer{AbsoluteIndex}): string; override;
|
|
function GetNodeValue(Node : TdxTreeListNode; Column : Integer{AbsoluteIndex}) : Variant; override;
|
|
function GetNodeVariant(Node: TdxTreeListNode; Column: Integer{AbsoluteIndex}): Variant; override;
|
|
function GetPreviewText(Node: TdxTreeListNode): string; override;
|
|
function IsCancelOnExit: Boolean; override;
|
|
function IsCanInsert: Boolean; override;
|
|
function IsCanNavigation: Boolean; override;
|
|
function IsEasySelect: Boolean; override;
|
|
function IsExistRowFooterCell(Node: TdxTreeListNode; AbsoluteIndex, FooterIndex: Integer): Boolean; override;
|
|
function IsKeyFieldEmpty: Boolean; override;
|
|
function IsLevelFooter(Level: Integer): Boolean; override;
|
|
function IsLoadedAll: Boolean; override;
|
|
function IsRowGroup(Node: TdxTreeListNode): Boolean; override;
|
|
function IsUseBookmarks: Boolean; override;
|
|
procedure MakeListNodes; override;
|
|
procedure RemoveColumn(Column: TdxTreeListColumn); override;
|
|
procedure ResetAutoHeaderPanelRowCountOption; override;
|
|
procedure SetDataChangedBusy(Value: Boolean); override;
|
|
procedure SetFocusedNode(Node: TdxTreeListNode; Column: Integer; MakeVisibleFlag: Boolean); override;
|
|
procedure SetFocusedNumber(AIndex: Integer); override;
|
|
procedure SetTopVisibleNode(Node: TdxTreeListNode); override;
|
|
// based
|
|
procedure CorrectIdGroupNodes;
|
|
function GetDefaultFields: Boolean; override;
|
|
function GetMinBufferCount: Integer;
|
|
procedure GetNextNodes(Mode: TdxDBGridLoadMode; ResyncMode: TdxDBGridResyncMode; SelectMode: Boolean);
|
|
procedure FindNodeById;
|
|
procedure LoadGroupList(FNodes: TList); virtual;
|
|
function LocateByNode(OldNode, Node: TdxDBgridNode; const Value: Variant; UseLocate: Boolean) : Boolean;
|
|
procedure RefreshGroupList;
|
|
procedure RefreshNodeValues(var Node: TdxDBTreeListControlNode); override;
|
|
procedure ReLoadGroupList;
|
|
procedure RemoveDuplicateBookmarks(AClearNodes: Boolean);
|
|
procedure SetGroupIndex(AColumn: TdxDBTreeListColumn; AIndex: Integer); override;
|
|
procedure SmartRefreshNode;
|
|
// DataSet
|
|
procedure DataChanged; override;
|
|
function IsDataSetBusy: Boolean; override;
|
|
procedure LinkActive(Value: Boolean); override;
|
|
procedure RecordChanged(Field: TField); override;
|
|
procedure Scroll(Distance: Integer); override;
|
|
// Save/Load
|
|
procedure BeginReadSettings(ARegIniWrapper: TdxRegIniWrapper); override;
|
|
procedure EndReadSettings(ARegIniWrapper: TdxRegIniWrapper); override;
|
|
procedure ReadColumn(ARegIniWrapper: TdxRegIniWrapper; const APathCol: string; AColumn: TdxTreeListColumn); override;
|
|
procedure ReadSettings(ARegIniWrapper: TdxRegIniWrapper; const APath: string); override;
|
|
procedure WriteColumn(ARegIniWrapper: TdxRegIniWrapper; const APathCol: string; AColumn: TdxTreeListColumn); override;
|
|
procedure WriteSettings(ARegIniWrapper: TdxRegIniWrapper; const APath: string); override;
|
|
// Navigation
|
|
function GetNodeByNavigation(ANode: TdxTreeListNode; ANavigationMode: TdxTreeListNavigationMode;
|
|
AGotoHidden: Boolean): TdxTreeListNode; override;
|
|
// Filter
|
|
procedure CancelDragSizing; override;
|
|
function CheckFilterNode(ANode: TdxDBGridNode): Boolean; virtual;
|
|
procedure ClearFilter; virtual;
|
|
procedure DestroyFilter; virtual;
|
|
function GetHeaderDropDownButtonState(AbsoluteIndex: Integer): TdxHeaderDropDownButtonState; override;
|
|
function GetFilterClass: TdxDBGridFilterClass; virtual;
|
|
function IsAutoFilterValuesLoad: Boolean; virtual;
|
|
function NodeFilterTestVisible(ATestNode: TdxDBGridNode): Boolean; virtual;
|
|
function NodeRefreshFilter(ATestNode: TdxDBGridNode; ARecalc: Boolean): Boolean; virtual;
|
|
procedure PrepareFilter; virtual;
|
|
procedure RefreshFilter; virtual;
|
|
procedure UpdateDataSetFilter; virtual;
|
|
function GetStatusButtonVisible: Boolean; override;
|
|
function GetStatusText: string; override;
|
|
function GetStatusCloseButtonHint: string; override;
|
|
|
|
procedure SetFilterMode;
|
|
property Filter: TdxDBGridFilter read GetFilter write SetFilter;
|
|
|
|
property GroupPanelColor: TColor read FGroupPanelColor write SetGroupPanelColor default dxclGroupPanel;
|
|
property GroupPanelFontColor: TColor read FGroupPanelFontColor write SetGroupPanelFontColor default dxclGroupPanelText;
|
|
property GroupPanelHeight: Integer read GetGroupPanelHeight;
|
|
property HideFocusRect default False;
|
|
property HideSelectionColor default clHighlight;
|
|
property HideSelectionTextColor default clHighlightText;
|
|
property Options: TdxDBGridOptions read GetOptions write SetOptions stored False;
|
|
property OptionsEx: TdxDBGridOptionsEx read GetOptionsEx write SetOptionsEx stored False;
|
|
// New
|
|
property OptionsBehavior: TdxDBGridOptionsBehavior read GetOptionsBehavior
|
|
write SetOptionsBehavior default dxDBGridDefaultOptionsBehavior;
|
|
property OptionsCustomize: TdxDBGridOptionsCustomize read GetOptionsCustomize
|
|
write SetOptionsCustomize default dxDBGridDefaultOptionsCustomize;
|
|
property OptionsDB: TdxDBGridOptionsDB read GetOptionsDB
|
|
write SetOptionsDB default dxDBGridDefaultOptionsDB;
|
|
property OptionsView: TdxDBGridOptionsView read GetOptionsView
|
|
write SetOptionsView default dxDBGridDefaultOptionsView;
|
|
property PaintStyle default psOutlook;
|
|
property ShowGrid default True;
|
|
property OnAddGroupColumn: TAddGroupColumnEvent read FOnAddGroupColumn write FOnAddGroupColumn;
|
|
property OnEndDragGroupColumn: TEndDragGroupColumn read FOnEndDragGroupColumn write FOnEndDragGroupColumn;
|
|
property OnReloadGroupList: TNotifyEvent read FOnReloadGroupList write FOnReloadGroupList;
|
|
// obsolete
|
|
property OnColumnSorting: TdxDBTreeListColumnSorting read FOnColumnSorting write FOnColumnSorting;
|
|
// Filter
|
|
property OnFilterChanged: TdxDBGridFilterChangedEvent read FOnFilterChanged write FOnFilterChanged;
|
|
property OnEnumFilterValues: TdxDBGridEnumFilterValuesEvent read FOnEnumFilterValues write FOnEnumFilterValues;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
function AddGroupColumn(Column: TdxDBTreeListColumn): Boolean;
|
|
procedure AssignColumns(ATreeListControl: TCustomdxTreeListControl); override;
|
|
procedure BeginGrouping;
|
|
procedure BeginSelection; override;
|
|
function CanAddGroupColumn(Column: TdxDBTreeListColumn): Boolean; virtual;
|
|
procedure ClearGroupColumns; override;
|
|
procedure DeleteGroupColumn(Index: Integer);
|
|
procedure DeleteSelection; override;
|
|
procedure EndGrouping;
|
|
procedure EndSelection; override;
|
|
function FindNodeByKeyValue(const Value: Variant): TdxDBGridNode;
|
|
procedure FullRefresh; override;
|
|
class function GetDefaultColumnClass: TdxDBTreeListColumnClass; override;
|
|
class function GetDefaultFieldColumnClass(AField: TField): TdxDBTreeListColumnClass; override;
|
|
function GetGroupColumnAt(X, Y: Integer): TdxDBTreeListColumn;
|
|
function GetNodeFooterColumnAt(X, Y: Integer): TdxDBTreeListColumn;
|
|
function GetSummaryGroupByName(const AName: string): TdxDBGridSummaryGroup;
|
|
function GetSummaryItemAt(X, Y: Integer; var ASummaryGroup: TdxDBGridSummaryGroup;
|
|
var AColumn: TdxDBTreeListColumn; AutoCreate: Boolean): TdxDBGridSummaryItem;
|
|
function InsertGroupColumn(AIndex: Integer; Column: TdxDBTreeListColumn): Boolean;
|
|
function IsBOF: Boolean; override;
|
|
function IsEOF: Boolean; override;
|
|
function IsFilterMode: Boolean;
|
|
function IsGridMode: Boolean;
|
|
function IsLoadAllRecords: Boolean;
|
|
function IsPartialLoad: Boolean;
|
|
function PointInFooterPanel(P: TPoint): Boolean; // obsolete - use GetHitInfo and GetHitTestInfoAt
|
|
function PointInGroupPanel(P: TPoint): Boolean; // obsolete - use GetHitInfo and GetHitTestInfoAt
|
|
procedure RebuildGroupIndexes;
|
|
procedure RefreshGroupColumns;
|
|
procedure RefreshSorting; override;
|
|
procedure ResetFullRefresh;
|
|
procedure SaveAllToStrings(List: TStrings); override;
|
|
procedure SaveSelectedToStrings(List: TStrings); override;
|
|
procedure SelectAll;
|
|
procedure SetFlagSmartReload;
|
|
|
|
property DataLink: TdxGridDataLink read FDataLink;
|
|
property DataSetChangedBusy: Boolean read DataChangedBusy;
|
|
property DataSource: TDataSource read GetDataSource write SetDataSource;
|
|
property GroupColumnCount: Integer read GetGroupColumnsCount;
|
|
property GroupColumns[Index: Integer]: TdxDBGridColumn read GetGroupColumns;
|
|
property LockRefresh;
|
|
|
|
property OnBackgroundDrawEvent: TdxDBGridBackgroundDrawEvent read FOnBackgroundDrawEvent write FOnBackgroundDrawEvent;
|
|
property OnGetCustomSummaryText: TdxCustomSummaryTextEvent read FOnGetCustomSummaryText write FOnGetCustomSummaryText;
|
|
property OnSummaryNode: TdxSummaryNodeEvent read FOnSummaryNode write FOnSummaryNode;
|
|
property OnClearNodeData: TNotifyEvent read FOnClearNodeData write FOnClearNodeData;
|
|
// obsolete
|
|
property OnCustomDraw: TCustomDrawEvent read FOnCustomDraw write FOnCustomDraw;
|
|
property OnColumnClick: TdxDBTreeListColumnClick read FOnColumnClick write FOnColumnClick;
|
|
property OnShowColumn: TShowColumnEvent read FOnShowColumnEvent write FOnShowColumnEvent;
|
|
property OnHideColumn: THideColumnEvent read FOnHideColumnEvent write FOnHideColumnEvent;
|
|
published
|
|
property DefaultFields: Boolean read GetDefaultFields write SetDefaultFields default False;
|
|
property KeyField;
|
|
property PartialLoad: Boolean read GetPartialLoad write SetPartialLoad stored False;
|
|
property PartialLoadBufferCount: Integer read FPartialLoadBufferCount write SetPartialLoadBufferCount default 100;
|
|
property ShowGroupPanel: Boolean read GetGroupPanelVisible write SetGroupPanelVisible default False;
|
|
property ShowSummaryFooter: Boolean read GetFooterPanelVisible write SetFooterPanelVisible default False;
|
|
property SummaryGroups: TdxDBGridSummaryGroups read FSummaryGroups write SetSummaryGroups;
|
|
property SummarySeparator: string read FSummarySeparator write SetSummarySeparator;
|
|
property UseBookmarks: Boolean read GetUseBookmarks write SetUseBookmarks stored False; // Option
|
|
end;
|
|
|
|
{ TdxDBGridFilterPopupListBox }
|
|
TdxDBGridFilterValues = class(TStringList)
|
|
private
|
|
FAnsiSort: Boolean;
|
|
FCaseInsensitive: Boolean;
|
|
FMaxCount: Integer;
|
|
public
|
|
procedure AddValue(AText: string; AValue: Variant; ACustomLoad: Boolean);
|
|
procedure Clear; override;
|
|
function FindValue(const AValue: Variant; var AIndex: Integer): Boolean;
|
|
destructor Destroy; override;
|
|
property AnsiSort: Boolean read FAnsiSort write FAnsiSort;
|
|
property CaseInsensitive: Boolean read FCaseInsensitive write FCaseInsensitive;
|
|
property MaxCount: Integer read FMaxCount write FMaxCount;
|
|
end;
|
|
|
|
TdxDBGridFilterPopupListBox = class(TCustomdxPopupPickListBox)
|
|
private
|
|
FColumn: TdxDBTreeListColumn;
|
|
FFilterValues: TdxDBGridFilterValues;
|
|
function GetGrid: TCustomdxDBGrid;
|
|
function GetListVisible: Boolean;
|
|
procedure SetListVisible(Value: Boolean);
|
|
protected
|
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
|
|
procedure ClosePopup(Accept: Boolean);
|
|
property Column: TdxDBTreeListColumn read FColumn write FColumn;
|
|
property FilterValues: TdxDBGridFilterValues read FFilterValues;
|
|
property Grid: TCustomdxDBGrid read GetGrid;
|
|
property ListVisible: Boolean read GetListVisible write SetListVisible;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TdxDBGridFilter }
|
|
TdxDBGridFilterColumnCriteria = (fcNone, fcBlanks, fcNonBlanks, fcValue, fcCustom);
|
|
TdxDBGridFilterStatus = (fsAuto, fsNone, fsAlways);
|
|
|
|
TdxDBGridFilter = class(TPersistent)
|
|
private
|
|
FActive: Boolean;
|
|
FAutoDataSetFilter: Boolean;
|
|
FCaseInsensitive: Boolean;
|
|
FCriteria: TdxCriteria;
|
|
FDBGrid: TCustomdxDBGrid;
|
|
FDropDownCount: Integer;
|
|
FDropDownWidth: Integer;
|
|
FFilterStatus: TdxDBGridFilterStatus;
|
|
FMaxDropDownCount: Integer;
|
|
procedure SetActive(Value: Boolean);
|
|
procedure SetAutoDataSetFilter(Value: Boolean);
|
|
procedure SetCaseInsensitive(Value: Boolean);
|
|
procedure SetFilterStatus(Value: TdxDBGridFilterStatus);
|
|
protected
|
|
procedure DefineProperties(Filer: TFiler); override;
|
|
procedure Changed;
|
|
function CheckFilterNode(ANode: TdxDBGridNode): Boolean;
|
|
function GetFilterCaption: string; virtual;
|
|
function GetFilterText: string; virtual;
|
|
procedure Prepare;
|
|
procedure SetFilterText(const Value: string); virtual;
|
|
property Criteria: TdxCriteria read FCriteria;
|
|
property DBGrid: TCustomdxDBGrid read FDBGrid;
|
|
public
|
|
constructor Create(ADBGrid: TCustomdxDBGrid);
|
|
destructor Destroy; override;
|
|
procedure Add(AColumn: TdxDBTreeListColumn; const AValue: Variant; const ADisplayValue: string); virtual;
|
|
procedure AddNull(AColumn: TdxDBTreeListColumn; IsNot: Boolean); virtual;
|
|
procedure Clear; virtual;
|
|
function GetFilterColumnCriteria(AColumn: TdxDBTreeListColumn; var AValue: Variant): TdxDBGridFilterColumnCriteria;
|
|
function IsColumnFilterExist(AColumn: TdxDBTreeListColumn): Boolean; virtual;
|
|
function IsEmpty: Boolean; virtual;
|
|
procedure Remove(AColumn: TdxDBTreeListColumn); virtual;
|
|
procedure RestoreDefaults;
|
|
procedure ShowCustomDialog(AColumn: TdxDBTreeListColumn); virtual;
|
|
property FilterCaption: string read GetFilterCaption;
|
|
property FilterText: string read GetFilterText write SetFilterText;
|
|
published
|
|
property Active: Boolean read FActive write SetActive default False;
|
|
property AutoDataSetFilter: Boolean read FAutoDataSetFilter write SetAutoDataSetFilter default False;
|
|
property CaseInsensitive: Boolean read FCaseInsensitive write SetCaseInsensitive default False;
|
|
property DropDownCount: Integer read FDropDownCount write FDropDownCount default 12;
|
|
property DropDownWidth: Integer read FDropDownWidth write FDropDownWidth default 0;
|
|
property FilterStatus: TdxDBGridFilterStatus read FFilterStatus write SetFilterStatus default fsAuto;
|
|
property MaxDropDownCount: Integer read FMaxDropDownCount write FMaxDropDownCount default 1000;
|
|
end;
|
|
|
|
{ TdxDBGrid }
|
|
TdxDBGrid = class(TCustomdxDBGrid)
|
|
public
|
|
procedure SaveToHTML(const AFileName: string; ASaveAll: Boolean);
|
|
procedure SaveToXLS(const AFileName: string; ASaveAll: Boolean);
|
|
procedure SaveToText(const AFileName: string; ASaveAll: Boolean;
|
|
const ASeparator, ABeginString, AEndString: string);
|
|
procedure SaveToXML(const AFileName: string; ASaveAll: Boolean);
|
|
|
|
property DragNode;
|
|
property EditingText;
|
|
property FocusedField;
|
|
property GroupPanelHeight;
|
|
property HeaderHeight;
|
|
property HotTrackInfo;
|
|
property SelectedCount;
|
|
property SelectedNodes;
|
|
property SelectedRows;
|
|
property TopIndex;
|
|
published
|
|
// standard
|
|
property Align;
|
|
property BorderStyle;
|
|
property Color;
|
|
property Ctl3D;
|
|
property DragCursor;
|
|
property DragMode;
|
|
property Enabled;
|
|
property Font;
|
|
property ParentColor;
|
|
property ParentCtl3D;
|
|
property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property ShowHint;
|
|
property TabOrder;
|
|
property TabStop;
|
|
property Visible;
|
|
|
|
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;
|
|
|
|
// original
|
|
property ArrowsColor;
|
|
property AutoExpandOnSearch;
|
|
property AutoSearchColor;
|
|
property AutoSearchTextColor;
|
|
property BandColor;
|
|
property BandFont;
|
|
property CustomizingRowCount;
|
|
property DataSource;
|
|
property DefaultRowHeight;
|
|
property DblClkExpanding;
|
|
property Filter;
|
|
property FixedBandLineColor;
|
|
property FixedBandLineWidth;
|
|
property GridLineColor;
|
|
property GrIndicatorWidth;
|
|
property GroupPanelColor;
|
|
property GroupPanelFontColor;
|
|
property GroupNodeColor;
|
|
property GroupNodeTextColor;
|
|
property HeaderColor;
|
|
property HeaderFont;
|
|
property HideFocusRect;
|
|
property HideSelection default False;
|
|
property HideSelectionColor;
|
|
property HideSelectionTextColor;
|
|
property HighlightColor;
|
|
property HighlightTextColor;
|
|
property IndentDesc;
|
|
property IniFileName;
|
|
property IniSectionName;
|
|
property LookAndFeel;
|
|
property MaxRowLineCount;
|
|
property Options;
|
|
property OptionsEx;
|
|
property OptionsBehavior;
|
|
property OptionsCustomize;
|
|
property OptionsDB;
|
|
property OptionsView;
|
|
property PreviewFieldName;
|
|
property PreviewFont;
|
|
property PreviewLines;
|
|
property PreviewMaxLength;
|
|
property RegistryPath;
|
|
property RowFooterColor;
|
|
property RowFooterTextColor;
|
|
property RowSeparatorLineWidth;
|
|
property ScrollBars;
|
|
property ShowBands;
|
|
property ShowGrid;
|
|
property ShowHeader;
|
|
property ShowHiddenInCustomizeBox;
|
|
property ShowNewItemRow;
|
|
property ShowPreviewGrid;
|
|
property ShowRowFooter;
|
|
property SimpleCustomizeBox;
|
|
property WaitForExpandNodeTime;
|
|
|
|
property OnAddGroupColumn;
|
|
property OnBackgroundDrawEvent;
|
|
property OnBandButtonClick;
|
|
property OnBandClick;
|
|
property OnBeforeCalcSummary;
|
|
property OnBeginDragNode;
|
|
property OnCalcRowLineHeight;
|
|
property OnCalcSummary;
|
|
property OnCanBandDragging;
|
|
property OnCanHeaderDragging;
|
|
property OnCanNodeSelected;
|
|
property OnChangeColumn;
|
|
property OnChangedColumnsWidth;
|
|
property OnChangeLeftCoord;
|
|
property OnChangeNode;
|
|
property OnChangeNodeEx;
|
|
property OnChangeTopVisibleNode;
|
|
property OnClearNodeData;
|
|
property OnCollapsed;
|
|
property OnCollapsing;
|
|
property OnColumnSorting;
|
|
property OnCompare;
|
|
property OnCustomDrawBand;
|
|
property OnCustomDrawCell;
|
|
property OnCustomDrawColumnHeader;
|
|
property OnCustomDrawFooter;
|
|
property OnCustomDrawFooterNode;
|
|
property OnCustomDrawPreview;
|
|
property OnCustomDrawPreviewCell;
|
|
property OnDragEndBand;
|
|
property OnDragEndHeader;
|
|
property OnDragOverBand;
|
|
property OnDragOverHeader;
|
|
property OnDeletion;
|
|
property OnEditChange;
|
|
property OnEdited;
|
|
property OnEditing;
|
|
property OnEditValidate;
|
|
property OnEndColumnsCustomizing;
|
|
property OnEndDragGroupColumn;
|
|
property OnExpanded;
|
|
property OnExpanding;
|
|
property OnGetEditColor;
|
|
property OnGetLevelColor;
|
|
property OnGetNodeDragText;
|
|
property OnGetPreviewText;
|
|
property OnHeaderButtonClick;
|
|
property OnHeaderMoved;
|
|
property OnHideBand;
|
|
property OnHideHeader;
|
|
property OnHotTrackNode;
|
|
property OnIsExistFooterCell;
|
|
property OnRefreshNodeData;
|
|
property OnReloadGroupList;
|
|
property OnSelectedCountChange;
|
|
property OnShowBand;
|
|
property OnShowHeader;
|
|
property OnStartBandDragging;
|
|
property OnStartHeaderDragging;
|
|
|
|
// obsolete
|
|
property OnColumnMoved;
|
|
property OnCustomDraw;
|
|
property OnColumnClick;
|
|
property OnShowColumn;
|
|
property OnHideColumn;
|
|
|
|
{$IFDEF DELPHI4}
|
|
property Anchors;
|
|
property Constraints;
|
|
{$ENDIF}
|
|
{$IFDEF DELPHI5}
|
|
property OnContextPopup;
|
|
{$ENDIF}
|
|
|
|
// Filter
|
|
property OnFilterChanged;
|
|
property OnEnumFilterValues;
|
|
end;
|
|
|
|
{TdxDBGridMaskColumn}
|
|
TdxDBGridMaskColumn = class(TdxDBTreeListMaskColumn)
|
|
public
|
|
property SummaryGroup;
|
|
published
|
|
property DisableFilter;
|
|
property GroupIndex;
|
|
property SummaryType;
|
|
property SummaryField;
|
|
property SummaryFormat;
|
|
property SortBySummary;
|
|
property OnSummary;
|
|
property SummaryGroupName;
|
|
|
|
property SummaryFooterType;
|
|
property SummaryFooterField;
|
|
property SummaryFooterFormat;
|
|
property OnSummaryFooter;
|
|
property OnDrawSummaryFooter;
|
|
end;
|
|
|
|
const
|
|
DefaultDBGridColumnType: array[ftUnknown..ftTypedBinary] of TdxDBTreeListColumnClassInfo = (
|
|
(ColumnClass: nil; Version: 0), { ftUnknown }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftString }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftSmallint }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftInteger }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftWord }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftBoolean }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftFloat }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftCurrency }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftBCD }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftDate }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftTime }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftDateTime }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftBytes }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftVarBytes }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftAutoInc }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftBlob }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftMemo }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftGraphic }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftFmtMemo }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftParadoxOle }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0), { ftDBaseOle }
|
|
(ColumnClass: TdxDBGridColumn; Version: 0)); { ftTypedBinary }
|
|
DefaultDBGridLookupColumnType: TdxDBTreeListColumnClassInfo = (ColumnClass: TdxDBGridColumn; Version: 0);
|
|
|
|
procedure InitDefaultDBGridColumnClasses(Proc: TInitColumnClassProc);
|
|
procedure ResetDefaultDBGridColumnClasses;
|
|
|
|
procedure LoadDBGridFilterFromStream(AStream: TStream; AFilter: TdxDBGridFilter);
|
|
procedure LoadDBGridFilterFromFile(const FileName: string; AFilter: TdxDBGridFilter);
|
|
procedure SaveDBGridFilterToStream(AStream: TStream; AFilter: TdxDBGridFilter);
|
|
procedure SaveDBGridFilterToFile(const FileName: string; AFilter: TdxDBGridFilter);
|
|
|
|
var
|
|
sdxPanelText: string; // 'Drag a column header here to group by that column';
|
|
|
|
implementation
|
|
|
|
uses Menus, Clipbrd, Consts, Registry, IniFiles
|
|
{$IFNDEF DELPHI3}, DBTables{$ENDIF}, dxGrExpt{filter}, dxGrFltr, dxGrFCmn{filter};
|
|
|
|
const
|
|
MaxMapSize = (MaxInt div 2) div SizeOf(Integer); { 250 million }
|
|
|
|
type
|
|
TIntArray = array[0..MaxMapSize] of Integer;
|
|
PIntArray = ^TIntArray;
|
|
|
|
// CalcSummary routines
|
|
function GetSummaryCount(ANode: TdxTreeListNode): Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := 0;
|
|
if ANode.Count > 0 then
|
|
if ANode[0].Count = 0 then
|
|
Result := ANode.Count
|
|
else
|
|
for I := 0 to ANode.Count - 1 do
|
|
Result := Result + GetSummaryCount(ANode[I]);
|
|
end;
|
|
|
|
procedure CalcSummaryCount(ANode: TdxTreeListNode; ALevel: Integer);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if (ANode.Level = ALevel) then
|
|
TdxDBGridNode(ANode).FSummary := GetSummaryCount(ANode)
|
|
else
|
|
for I := 0 to ANode.Count - 1 do
|
|
CalcSummaryCount(ANode[I], ALevel);
|
|
end;
|
|
|
|
procedure CalcAverageCount(ANode: TdxTreeListNode; ALevel: Integer);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if (ANode.Level = ALevel) then
|
|
TdxDBGridNode(ANode).FSummary := TdxDBGridNode(ANode).FSummary/GetSummaryCount(ANode)
|
|
else
|
|
for I := 0 to ANode.Count - 1 do
|
|
CalcAverageCount(ANode[I], ALevel);
|
|
end;
|
|
|
|
procedure CalcMultiSummaryCount(ANode: TdxTreeListNode; ALevel, AIndex: Integer);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if (ANode.Level = ALevel) then
|
|
TdxDBGridNode(ANode).FSummaryList^[AIndex].Value := GetSummaryCount(ANode)
|
|
else
|
|
for I := 0 to ANode.Count - 1 do
|
|
CalcMultiSummaryCount(ANode[I], ALevel, AIndex);
|
|
end;
|
|
|
|
procedure CalcMultiAverageCount(ANode: TdxTreeListNode; ALevel, AIndex: Integer);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if (ANode.Level = ALevel) then
|
|
TdxDBGridNode(ANode).FSummaryList^[AIndex].Value :=
|
|
TdxDBGridNode(ANode).FSummaryList^[AIndex].Value / GetSummaryCount(ANode)
|
|
else
|
|
for I := 0 to ANode.Count - 1 do
|
|
CalcMultiAverageCount(ANode[I], ALevel, AIndex);
|
|
end;
|
|
|
|
// Filter routines
|
|
|
|
type
|
|
TFilterWrapper = class(TComponent)
|
|
private
|
|
FFilter: TdxDBGridFilter;
|
|
published
|
|
property Filter: TdxDBGridFilter read FFilter write FFilter;
|
|
end;
|
|
|
|
procedure LoadDBGridFilterFromStream(AStream: TStream; AFilter: TdxDBGridFilter);
|
|
var
|
|
Wrapper: TFilterWrapper;
|
|
begin
|
|
Wrapper := TFilterWrapper.Create(nil);
|
|
try
|
|
Wrapper.Filter := AFilter;
|
|
AStream.ReadComponent(Wrapper);
|
|
finally
|
|
Wrapper.Free;
|
|
end;
|
|
AFilter.Changed;
|
|
end;
|
|
|
|
procedure LoadDBGridFilterFromFile(const FileName: string; AFilter: TdxDBGridFilter);
|
|
var
|
|
AStream: TStream;
|
|
begin
|
|
AStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
LoadDBGridFilterFromStream(AStream, AFilter);
|
|
finally
|
|
AStream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure SaveDBGridFilterToStream(AStream: TStream; AFilter: TdxDBGridFilter);
|
|
var
|
|
Wrapper: TFilterWrapper;
|
|
begin
|
|
Wrapper := TFilterWrapper.Create(nil);
|
|
try
|
|
Wrapper.Filter := AFilter;
|
|
AStream.WriteComponent(Wrapper);
|
|
finally
|
|
Wrapper.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure SaveDBGridFilterToFile(const FileName: string; AFilter: TdxDBGridFilter);
|
|
var
|
|
AStream: TStream;
|
|
begin
|
|
AStream := TFileStream.Create(FileName, fmCreate);
|
|
try
|
|
SaveDBGridFilterToStream(AStream, AFilter);
|
|
finally
|
|
AStream.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TdxGridDataLink }
|
|
|
|
procedure TdxGridDataLink.FocusControl(Field: TFieldRef);
|
|
var
|
|
Node: TdxTreeListNode;
|
|
begin
|
|
if Assigned(Field) and Assigned(Field^) then
|
|
begin
|
|
if not (egoCanNavigation in DBGrid.Options) then Exit;
|
|
if not DBGrid.IsGridMode then
|
|
begin
|
|
Node := DBGrid.FocusedNode;
|
|
if Node <> nil then
|
|
begin
|
|
while Node.Count > 0 do
|
|
Node := Node[0];
|
|
Node.Focused := True;
|
|
Node.MakeVisible;
|
|
end;
|
|
end;
|
|
if (egoRowSelect in DBGrid.Options) then Exit;
|
|
if DBGrid.IsNewItemRowVisible then DBGrid.NewItemRowActive := True; // new item row
|
|
DBGrid.FocusedField := Field^;
|
|
if (DBGrid.FocusedField = Field^) and DBGrid.AcquireFocus then
|
|
begin
|
|
Field^ := nil;
|
|
DBGrid.ShowEditor;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxGridDataLink.LayoutChanged;
|
|
var
|
|
FlagGrp: Boolean;
|
|
begin
|
|
FlagGrp := not DBGrid.IsGridMode;
|
|
if FlagGrp then DBGrid.BeginGrouping;
|
|
try
|
|
DBGrid.LayoutChanged;
|
|
finally
|
|
DBGrid.FlagFullRefresh := True;
|
|
try
|
|
if FlagGrp then DBGrid.EndGrouping;
|
|
finally
|
|
DBGrid.FlagFullRefresh := False;
|
|
end;
|
|
end;
|
|
if not (FlagGrp and (DBGrid.FLockGrouping = 0)) then
|
|
inherited LayoutChanged;
|
|
end;
|
|
|
|
function TdxGridDataLink.GetDBGrid: TCustomDxDBGrid;
|
|
begin
|
|
Result := DBTreeListControl as TCustomDxDBGrid;
|
|
end;
|
|
|
|
{TdxDBGridNode}
|
|
destructor TdxDBGridNode.Destroy;
|
|
begin
|
|
// free summary list
|
|
if FSummaryList <> nil then
|
|
begin
|
|
FreeMem(FSummaryList);
|
|
FSummaryList := nil;
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TdxDBGridNode.Delete;
|
|
var
|
|
ADBGrid: TCustomDxDBGrid;
|
|
begin
|
|
ADBGrid := TCustomDxDBGrid(Owner);
|
|
if not ADBGrid.DataLink.Active then Exit;
|
|
if ADBGrid.IsGridMode then
|
|
begin
|
|
if not Focused then Exit;
|
|
end
|
|
else
|
|
begin
|
|
if HasChildren or not (egoCanNavigation in ADBGrid.Options) then Exit;
|
|
if ADBGrid.IsUseLocate then
|
|
if not ADBGrid.LocateByNode(TdxDBgridNode(ADBGrid.FocusedNode), TdxDBgridNode(Self),
|
|
TdxDBGridNode(Self).Id, True) then Exit
|
|
else
|
|
else
|
|
if not ADBGrid.LocateByNode(TdxDBgridNode(ADBGrid.FocusedNode), TdxDBgridNode(Self), Null, False) then Exit;
|
|
end;
|
|
ADBGrid.FUpdatingNode := Self;
|
|
ADBGrid.FPrevId := Self.Id;
|
|
try
|
|
ADBGrid.DataLink.DataSet.Delete;
|
|
except
|
|
ADBGrid.FUpdatingNode := nil;
|
|
ADBGrid.FPrevId := Null;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
function TdxDBGridNode.GetSummaryValue: Extended;
|
|
begin
|
|
Result := TCustomdxDBGrid(Owner).GetSummaryValue(Self);
|
|
end;
|
|
|
|
function TdxDBGridNode.GetMultiSummaryValue(Index: Integer): Extended;
|
|
begin
|
|
if FSummaryList <> nil then
|
|
Result := FSummaryList^[Index].Value
|
|
else Result := 0;
|
|
end;
|
|
|
|
{TCustomDxDBGrid}
|
|
constructor TCustomdxDBGrid.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FDataLink := TdxGridDataLink.Create(Self);
|
|
FGroupColumns := TList.Create;
|
|
// New Options
|
|
FOptionsBehavior := dxDBGridDefaultOptionsBehavior;
|
|
FOptionsCustomize := dxDBGridDefaultOptionsCustomize;
|
|
FOptionsDB := dxDBGridDefaultOptionsDB;
|
|
FOptionsView := dxDBGridDefaultOptionsView;
|
|
FOptions := [egoEditing, egoColumnSizing, egoColumnMoving,
|
|
egoCanDelete, egoConfirmDelete, egoCanNavigation, egoCancelOnExit,
|
|
egoImmediateEditor, egoCanInsert, egoTabThrough];
|
|
FOptionsEx := [egoUseBitmap, egoBandHeaderWidth, egoBandSizing, egoBandMoving, egoAutoSort,
|
|
egoEnterShowEditor, egoVertThrough, egoDragScroll];
|
|
FOldFocusedColumn := -1;
|
|
inherited HideFocusRect := False;
|
|
inherited HideSelection := False;
|
|
inherited PaintStyle := psOutlook;
|
|
inherited ShowGrid := True;
|
|
inherited Options := [aoEditing, aoColumnSizing, aoColumnMoving, aoImmediateEditor, aoTabThrough];
|
|
inherited OptionsEx := [aoUseBitmap, aoBandHeaderWidth, aoBandSizing, aoBandMoving,
|
|
aoEnterShowEditor, aoVertThrough, aoDragScroll];
|
|
inherited HideSelectionColor := inherited HighlightColor;
|
|
inherited HideSelectionTextColor := inherited HighlightTextColor;
|
|
FFlagChangedGroupColumn := False;
|
|
FGroupFields := TStringList.Create;
|
|
{add self to grid list}
|
|
RegisterDBTreeListControl(Self);
|
|
FGroupPanelColor := dxclGroupPanel;
|
|
FGroupPanelFontColor := dxclGroupPanelText;
|
|
{new}
|
|
FSaveBkmList := TStringList.Create;;
|
|
FSummaryGroups := TdxDBGridSummaryGroups.Create(Self, TdxDBGridSummaryGroup);
|
|
FSummarySeparator := ', ';
|
|
FSummaryAbsoluteList := TList.Create;
|
|
FPartialLoadBufferCount := 100;
|
|
end;
|
|
|
|
destructor TCustomdxDBGrid.Destroy;
|
|
begin
|
|
try
|
|
DoSaveLayout;
|
|
finally
|
|
BeforeDestroy;
|
|
DestroyFilter;
|
|
ClearListNodes;
|
|
FSummaryAbsoluteList.Free;
|
|
FSummaryAbsoluteList := nil;
|
|
FSaveBkmList.Free;
|
|
FSaveBkmList := nil;
|
|
if FSaveIdList <> nil then
|
|
begin
|
|
ClearIdList;
|
|
FSaveIdList.Free;
|
|
FSaveIdList := nil;
|
|
end;
|
|
FSummaryGroups.Free;
|
|
FSummaryGroups := nil;
|
|
FGroupFields.Free;
|
|
FGroupFields := nil;
|
|
ClearSelection;
|
|
FGroupColumns.Free;
|
|
FGroupColumns := nil;
|
|
FDataLink.Free;
|
|
FDataLink := nil;
|
|
FFilterStream.Free;
|
|
FFilterStream := nil;
|
|
{remove self from grid list}
|
|
UnRegisterDBTreeListControl(Self);
|
|
inherited Destroy;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.AddGroupColumn(Column: TdxDBTreeListColumn): Boolean;
|
|
begin
|
|
Result := InsertGroupColumn(FGroupColumns.Count, Column);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.AssignColumns(ATreeListControl: TCustomdxTreeListControl);
|
|
var
|
|
I: Integer;
|
|
ADBGrid: TCustomDxDBGrid;
|
|
Column: TdxDBGridColumn;
|
|
begin
|
|
BeginUpdate;
|
|
BeginGrouping;
|
|
try
|
|
ClearGroupColumns;
|
|
inherited AssignColumns(ATreeListControl);
|
|
if ATreeListControl is TCustomdxDBGrid then
|
|
begin
|
|
ADBGrid := TCustomdxDBGrid(ATreeListControl);
|
|
for I := 0 to ADBGrid.GroupColumnCount - 1 do
|
|
begin
|
|
Column := TdxDBGridColumn(ColumnByName(ADBGrid.GroupColumns[i].Name));
|
|
if Column <> nil then Column.GroupIndex := I;
|
|
end;
|
|
end;
|
|
finally
|
|
EndGrouping;
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.BeginGrouping;
|
|
begin
|
|
if FLockGrouping = 0 then SaveGroupFields;
|
|
Inc(FLockGrouping);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.BeginSelection;
|
|
begin
|
|
if (LockSelection = 0) and Assigned(OnChangeNodeEx) then
|
|
LoadChangeNodeInfo(FPrevNodeInfo);
|
|
inherited BeginSelection;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.CanAddGroupColumn(Column: TdxDBTreeListColumn): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
// check Default Fields
|
|
if DefaultFields then Exit;
|
|
if not FRefreshGroupColumns then
|
|
if (Column = nil) or Column.DisableGrouping or
|
|
(Column.Field is TBlobField ) or ((GetVisibleHeaderCount = 1) and Column.Visible) then Exit;
|
|
for i := 0 to FGroupColumns.Count - 1 do
|
|
if (GroupColumns[i] = Column) or
|
|
((GroupColumns[i].Field = Column.Field)
|
|
and (Column.Field <> nil)) then Exit;
|
|
Result := True;
|
|
if Assigned(FOnAddGroupColumn) then FOnAddGroupColumn(Self, Column, Result);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ClearGroupColumns;
|
|
begin
|
|
BeginSorting;
|
|
try
|
|
BeginUpdate;
|
|
try
|
|
BeginGrouping;
|
|
try
|
|
while GroupColumnCount > 0 do DeleteGroupColumn(0);
|
|
finally
|
|
EndGrouping;
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
finally
|
|
EndSorting;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DeleteGroupColumn(Index : Integer);
|
|
begin
|
|
if GroupColumnCount = 0 then exit;
|
|
BeginUpdate;
|
|
try
|
|
GroupColumns[Index].Visible := True;
|
|
GroupColumns[Index].Sorted := csNone;
|
|
TdxDBGridColumn(GroupColumns[Index]).FGroupIndex := -1;
|
|
FGroupColumns.Delete(Index);
|
|
RebuildGroupIndexes;
|
|
FlagSmartReload := True;
|
|
if FLockGrouping = 0 then ReLoadGroupList;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DeleteSelection;
|
|
var
|
|
I: Integer;
|
|
Current: TBookMarkStr;
|
|
begin
|
|
inherited DeleteSelection;
|
|
if Datalink.Active then
|
|
with Datalink.Datasource.Dataset do
|
|
begin
|
|
HideEditor;
|
|
ResetFullRefresh;
|
|
DisableControls;
|
|
Current := Bookmark;
|
|
try
|
|
if State in dsEditModes then
|
|
begin
|
|
Cancel;
|
|
Current := Bookmark;
|
|
end;
|
|
// sort bookmark (egoExtMultiSelect)
|
|
for I := BkmList.Count-1 downto 0 do
|
|
begin
|
|
if not IsGridMode and (BkmList.Objects[I] <> nil) and
|
|
TdxTreeListNode(BkmList.Objects[I]).HasChildren then Continue;
|
|
try
|
|
if IsUseBookmarks then
|
|
begin
|
|
if CompareBkm(BkmList[I], Current) = 0 then
|
|
Current := ''; // Check Current
|
|
Bookmark := BkmList[I];
|
|
Delete;
|
|
end
|
|
else
|
|
begin
|
|
if (BkmList.Objects[I] <> nil) and
|
|
(VarType(TdxDBGridNode(BkmList.Objects[I]).Id) <> varEmpty) and
|
|
Locate(KeyField, TdxDBGridNode(BkmList.Objects[I]).Id, []) then
|
|
begin
|
|
if CompareBkm(Bookmark, Current) = 0 then
|
|
Current := ''; // Check Current
|
|
Delete;
|
|
end;
|
|
end;
|
|
BkmList.Delete(I);
|
|
except
|
|
if not (egoExtMultiSelect in Options) then raise;
|
|
// on E: EDBEngineError do ;
|
|
end;
|
|
end;
|
|
finally
|
|
BkmList.Clear;
|
|
try
|
|
if (Current <> '') and BookmarkValid(TBookmark(Current)) then
|
|
Bookmark := Current;
|
|
except
|
|
end;
|
|
EnableControls;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.EndGrouping;
|
|
begin
|
|
Dec(FLockGrouping);
|
|
if FLockGrouping < 0 then FLockGrouping := 0;
|
|
if FLockGrouping = 0 then
|
|
begin
|
|
if FPrevReloadMode = rlGridMode then FlagSmartReload := False;
|
|
if not ChangedGroupFields then RefreshGroupList
|
|
else ReLoadGroupList;
|
|
FPrevReloadMode := rlUnknown;
|
|
end;
|
|
ClearGroupFields;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.EndSelection;
|
|
begin
|
|
inherited EndSelection;
|
|
if (LockSelection = 0) and Assigned(OnChangeNodeEx) then
|
|
CheckChangeNodeEx;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.FindNodeByKeyValue(const Value: Variant): TdxDBGridNode;
|
|
|
|
function Find(ANode: TdxTreeListNode; var Node: TdxDBGridNode): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
if ANode.Count > 0 then
|
|
for I := 0 to ANode.Count - 1 do
|
|
if Find(ANode[I], Node) then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end
|
|
else
|
|
else
|
|
if TdxDBGridNode(ANode).Id = Value then
|
|
begin
|
|
Node := TdxDBGridNode(ANode);
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := nil;
|
|
if not IsGridMode then
|
|
for I := 0 to Count - 1 do
|
|
if Find(Items[I], Result) then Break;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.FullRefresh;
|
|
begin
|
|
if IsGridMode then
|
|
LayoutChanged
|
|
else
|
|
begin
|
|
ResetFullRefresh;
|
|
RefreshGroupColumns;
|
|
end;
|
|
end;
|
|
|
|
class function TCustomdxDBGrid.GetDefaultColumnClass: TdxDBTreeListColumnClass;
|
|
begin
|
|
Result := TdxDBGridColumn;
|
|
end;
|
|
|
|
class function TCustomdxDBGrid.GetDefaultFieldColumnClass(AField: TField): TdxDBTreeListColumnClass;
|
|
begin
|
|
if AField.FieldKind = fkLookup then
|
|
Result := DefaultDBGridLookupColumnType.ColumnClass
|
|
else
|
|
if AField.FieldKind = fkData then
|
|
if AField.DataType in [ftUnknown..ftTypedBinary] then
|
|
Result := DefaultDBGridColumnType[AField.DataType].ColumnClass
|
|
else Result := TdxDBGridColumn
|
|
else Result := TdxDBGridColumn;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetGroupColumnAt(X, Y: Integer): TdxDBTreeListColumn;
|
|
var
|
|
DrawInfo: TdxGridDrawInfo;
|
|
I: Integer;
|
|
begin
|
|
Result := nil;
|
|
CalcRectInfo(DrawInfo);
|
|
if PtInRect(DrawInfo.GroupPanelRect, Point(X, Y)) then
|
|
for I := 0 to GroupColumnCount - 1 do
|
|
if PtInRect(GetGroupHeaderRect(I), Point(X, Y)) then
|
|
begin
|
|
Result := GroupColumns[I];
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetNodeFooterColumnAt(X, Y: Integer): TdxDBTreeListColumn;
|
|
begin
|
|
Result := TdxDBTreeListColumn(inherited GetNodeFooterColumnAt(X, Y));
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetSummaryGroupByName(const AName: string): TdxDBGridSummaryGroup;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := nil;
|
|
for I := 0 to SummaryGroups.Count - 1 do
|
|
if AnsiCompareText(SummaryGroups[I].Name, AName) = 0 then
|
|
begin
|
|
Result := SummaryGroups[I];
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetSummaryItemAt(X, Y: Integer; var ASummaryGroup: TdxDBGridSummaryGroup;
|
|
var AColumn: TdxDBTreeListColumn; AutoCreate: Boolean): TdxDBGridSummaryItem;
|
|
var
|
|
AHitInfo: TdxTreeListHitInfo;
|
|
I, ALevel: Integer;
|
|
ASummaryItem: TdxDBGridSummaryItem;
|
|
begin
|
|
Result := nil;
|
|
ASummaryGroup := nil;
|
|
AColumn := nil;
|
|
AHitInfo := GetHitInfo(Point(X, Y));
|
|
if (AHitInfo.hitType = htSummaryNodeFooter) and (AHitInfo.Node <> nil) and
|
|
(AHitInfo.Column <> -1) and (AHitInfo.FooterRow <> -1) then
|
|
begin
|
|
AColumn := Columns[AHitInfo.Column];
|
|
ALevel := GetNodeFooterLevel(AHitInfo.Node, AHitInfo.FooterRow);
|
|
ASummaryGroup := GroupColumns[ALevel].FSummaryGroup;
|
|
if Assigned(ASummaryGroup) then
|
|
begin
|
|
for I := 0 to ASummaryGroup.SummaryItems.Count - 1 do
|
|
begin
|
|
ASummaryItem := ASummaryGroup.SummaryItems[I];
|
|
if ASummaryItem.ColumnName <> '' then
|
|
begin
|
|
if ColumnByName(ASummaryItem.ColumnName) = AColumn then
|
|
begin
|
|
Result := ASummaryItem;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
if AutoCreate and (Result = nil) then
|
|
begin
|
|
Result := ASummaryGroup.SummaryItems.Add;
|
|
Result.ColumnName := AColumn.Name;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.InsertGroupColumn(AIndex : Integer; Column:TdxDBTreeListColumn):Boolean;
|
|
begin
|
|
Result := False;
|
|
if not CanAddGroupColumn(Column) then Exit;
|
|
if AIndex < 0 then AIndex := 0;
|
|
if AIndex > FGroupColumns.Count then AIndex := FGroupColumns.Count;
|
|
if not IsGridMode and not IsPartialLoad then FlagSmartReload := True;
|
|
FGroupColumns.Insert(AIndex, Column);
|
|
RebuildGroupIndexes;
|
|
BeginUpdate;
|
|
try
|
|
if TdxDBGridColumn(Column).FSorted = csNone then
|
|
TdxDBGridColumn(Column).FSorted := csUp;
|
|
RemoveSortedColumn(Column);
|
|
if not (egoNotHideColumn in OptionsEx) then
|
|
Column.Visible := False;
|
|
if FLockGrouping = 0 then ReLoadGroupList;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsAutoFilter: Boolean;
|
|
begin
|
|
Result := Filter.Active;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsBOF: Boolean;
|
|
var
|
|
ADataSet: TDataSet;
|
|
begin
|
|
if IsLoadedAll then
|
|
Result := inherited IsBOF
|
|
else
|
|
begin
|
|
if IsPartialLoad then
|
|
Result := FGridBOF and inherited IsBOF
|
|
else
|
|
if Assigned(DataSource) and Assigned(DataSource.DataSet) then
|
|
begin
|
|
ADataSet := DataSource.DataSet;
|
|
Result := ADataSet.BOF;
|
|
end
|
|
else
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsEOF: Boolean;
|
|
var
|
|
ADataSet: TDataSet;
|
|
begin
|
|
if IsLoadedAll then
|
|
Result := inherited IsEOF
|
|
else
|
|
begin
|
|
if IsPartialLoad then
|
|
Result := FGridEOF and inherited IsEOF
|
|
else
|
|
if Assigned(DataSource) and Assigned(DataSource.DataSet) then
|
|
begin
|
|
ADataSet := DataSource.DataSet;
|
|
Result := ADataSet.EOF;
|
|
end
|
|
else
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsFilterMode: Boolean;
|
|
begin
|
|
Result := IsLoadAllRecords and FFiltering;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsGridMode: Boolean;
|
|
begin
|
|
Result := InternalGridMode and not IsPartialLoad;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsLoadAllRecords: Boolean;
|
|
begin
|
|
Result := not IsGridMode and not IsPartialLoad;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsPartialLoad: Boolean;
|
|
begin
|
|
Result := InternalGridMode and PartialLoad;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.PointInFooterPanel(P: TPoint): Boolean;
|
|
begin
|
|
Result := GetHitTestInfoAt(P.X, P.Y) = htSummaryFooter;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.PointInGroupPanel(P: TPoint): Boolean;
|
|
begin
|
|
Result := GetHitTestInfoAt(P.X, P.Y) = htGroupPanel;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RebuildGroupIndexes;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to GroupColumnCount-1 do
|
|
TdxDBGridColumn(GroupColumns[i]).FGroupIndex := i;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RefreshGroupColumns; // after Loaded
|
|
var
|
|
List: TStringList;
|
|
I: Integer;
|
|
begin
|
|
if (egoSmartRefresh in Options) and not FlagFullRefresh and
|
|
not IsGridMode then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
FlagSmartReload := True;
|
|
ReCalcSummary(nil);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
Exit;
|
|
end;
|
|
FRefreshGroupColumns := True;
|
|
List := TStringList.Create;
|
|
List.Sorted := True;
|
|
for i := 0 to ColumnCount - 1 do
|
|
if TdxDBGridColumn(Columns[i]).GroupIndex <> -1 then
|
|
List.AddObject(
|
|
Format('%11d', [TdxDBGridColumn(Columns[i]).GroupIndex]),
|
|
Columns[i]);
|
|
try
|
|
if (List.Count > 0) then
|
|
begin
|
|
BeginGrouping;
|
|
try
|
|
for i:= 0 to List.Count - 1 do
|
|
TdxDBGridColumn(List.Objects[i]).GroupIndex :=
|
|
TdxDBGridColumn(List.Objects[i]).GroupIndex;
|
|
FlagSmartReload := False;
|
|
finally
|
|
EndGrouping;
|
|
end;
|
|
end
|
|
else
|
|
if not IsGridMode then ReloadGroupList;
|
|
finally
|
|
List.Free;
|
|
FRefreshGroupColumns := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RefreshSorting;
|
|
var
|
|
Column: TdxDBTreeListColumn;
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
Column := SortedColumn;
|
|
if (Column <> nil) and (Column.Sorted <> csNone) then
|
|
DoSortColumn(GroupColumnCount, Column.Index, Column.Sorted = csDown);
|
|
// TODO Refresh Sort Group Nodes
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ResetFullRefresh;
|
|
begin
|
|
FlagFullRefresh := True;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SaveAllToStrings(List : TStrings);
|
|
var
|
|
Current : TBookmarkStr;
|
|
begin
|
|
if not IsGridMode then
|
|
inherited SaveAllToStrings(List)
|
|
else
|
|
begin
|
|
{Header}
|
|
List.Add(GetHeaderTabText);
|
|
{Records}
|
|
if DataLink.Active then
|
|
with Datalink.Dataset do
|
|
begin
|
|
DisableControls;
|
|
Current := Bookmark;
|
|
{save top pos}
|
|
Datalink.ActiveRecord := 0;
|
|
try
|
|
First;
|
|
while not Eof do
|
|
begin
|
|
List.Add(GetNodeTabText(Items[Datalink.ActiveRecord]));
|
|
Next;
|
|
end;
|
|
finally
|
|
Bookmark := Current;
|
|
EnableControls;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SaveSelectedToStrings(List: TStrings);
|
|
var
|
|
Current: TBookmarkStr;
|
|
I: Integer;
|
|
begin
|
|
if not IsGridMode or (not (egoMultiSelect in Options)) then
|
|
inherited SaveSelectedToStrings(List)
|
|
else
|
|
begin
|
|
List.Add(GetHeaderTabText);
|
|
if DataLink.Active then
|
|
with Datalink.Dataset do
|
|
begin
|
|
DisableControls;
|
|
Current := Bookmark;
|
|
{save top pos}
|
|
Datalink.ActiveRecord := 0;
|
|
try
|
|
for i := 0 to SelectedCount - 1 do
|
|
begin
|
|
Bookmark := SelectedRows[i];
|
|
List.Add(GetNodeTabText(Items[Datalink.ActiveRecord]));
|
|
end;
|
|
finally
|
|
Bookmark := Current;
|
|
EnableControls;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SelectAll;
|
|
var
|
|
bm, Current: TBookmarkStr;
|
|
N1, N2: TdxTreeListNode;
|
|
begin
|
|
if not (egoMultiSelect in Options) or
|
|
not DataLink.Active or (Count = 0) then Exit;
|
|
BeginSelection;
|
|
try
|
|
if IsGridMode then
|
|
begin
|
|
with Datalink.Dataset do
|
|
begin
|
|
DisableControls;
|
|
bm := BookMark;
|
|
try
|
|
BkmList.Clear;
|
|
First;
|
|
while not EOF do
|
|
begin
|
|
Current := Bookmark;
|
|
BkmList.Add(Current);
|
|
Next;
|
|
end;
|
|
finally
|
|
BookMark := bm;
|
|
EnableControls;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
if IsPartialLoad then
|
|
begin
|
|
if IsUseBookmarks or not (FGridBOF and FGridEOF) then
|
|
GetNextNodes(lmAllRecords, rmNone, IsUseBookmarks{selected -> True});
|
|
if not IsUseBookmarks then
|
|
begin
|
|
BkmList.Clear;
|
|
if Count > 0 then SelectNodes(Items[0], Items[Count - 1]);
|
|
end;
|
|
end
|
|
else { Load All Records}
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
ClearSelection;
|
|
FullExpand;
|
|
if IsUseBookmarks then
|
|
begin
|
|
FSelectAllFlag := True;
|
|
FRefreshGroupList := True;
|
|
try
|
|
RefreshGroupList;
|
|
if not FLocateByNode then FindNodeById;
|
|
finally
|
|
FRefreshGroupList := False;
|
|
FSelectAllFlag := False;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
{ Start Node }
|
|
N1 := Items[0];
|
|
if not IsExtMultiSelect then
|
|
while N1.Count > 0 do N1 := N1[0];
|
|
{ End Node }
|
|
N2 := Items[Count - 1];
|
|
while N2.Count > 0 do N2 := N2[N2.Count - 1];
|
|
SelectNodes(N1, N2);
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
finally
|
|
EndSelection;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetFlagSmartReload;
|
|
begin
|
|
FlagSmartReload := True;
|
|
end;
|
|
|
|
// protected TCustomDxDBGrid
|
|
procedure TCustomdxDBGrid.KeyDown(var Key: Word; Shift: TShiftState);
|
|
|
|
function DeletePrompt: Boolean;
|
|
var
|
|
Title, Msg: string;
|
|
begin
|
|
Title := sdxGrTitleConfirm{LoadStr(dxSTitleWarning)};
|
|
Msg := sdxGrDeleteRecordQuestion{LoadStr(dxSDeleteRecordQuestion)};
|
|
if SelectedCount > 0{1} then
|
|
Msg := sdxDeleteMultipleRecordsQuestion{LoadStr(dxSDeleteMultipleRecordsQuestion)};
|
|
Result := not (egoConfirmDelete in Options) or
|
|
(MessageBox(Self.Handle, PChar(Msg), PChar(Title), MB_ICONQUESTION or MB_OKCANCEL) <> IDCANCEL);
|
|
end;
|
|
|
|
function IsNeedPost(AUp: Boolean): Boolean;
|
|
begin
|
|
Result := not (IsVertThrough and DoMoveFocusedColumn(AUp, True));
|
|
end;
|
|
|
|
var
|
|
KeyDownEvent: TKeyEvent;
|
|
D: Integer;
|
|
begin
|
|
KeyDownEvent := OnKeyDown;
|
|
OnKeyDown := nil;
|
|
BeginSelection;
|
|
FInKeyDown := True;
|
|
try
|
|
if Assigned(KeyDownEvent) then KeyDownEvent(Self, Key, Shift);
|
|
// check SelectionAnchor
|
|
if IsPartialLoad and (ssShift in Shift) and (SelectionAnchor = nil) then
|
|
begin
|
|
if FDatalink.Active then
|
|
SelectionAnchor := FocusedNode;
|
|
end;
|
|
|
|
if not (FSearching and ((Key in [VK_DELETE, VK_BACK, VK_ESCAPE]) or
|
|
((Key = VK_RETURN) and (Shift*[ssCtrl, ssShift] <> [])))) then
|
|
if FDatalink.Active and CanTreeListAcceptKey(Key, Shift) then
|
|
with FDatalink.DataSet do
|
|
begin
|
|
if ssCtrl in Shift then
|
|
if not NewItemRowActive and (Key = VK_DELETE) and (egoCanDelete in Options) and (egoCanNavigation in Options)
|
|
and not ReadOnly and (FocusedNode <> Nil) and not FocusedNode.HasChildren then
|
|
begin
|
|
if not {$IFDEF DELPHI3}IsEmpty {$else}(FDataLink.RecordCount = 0){$ENDIF}and CanModify and DeletePrompt then
|
|
if SelectedCount > 0{1} then DeleteSelection
|
|
else TdxDBGridNode(FocusedNode).Delete;
|
|
Exit;
|
|
end;
|
|
if (Key = VK_ESCAPE) {and (Self.State <> tsEditing)} then
|
|
FDatalink.Reset
|
|
else
|
|
if IsGridMode then
|
|
begin
|
|
if not (ssShift in Shift) and (Key in [VK_HOME, VK_END]) and
|
|
((egoRowSelect in Options) or (ssCtrl in Shift)) then
|
|
begin
|
|
case Key of
|
|
VK_HOME: First;
|
|
VK_END: Last;
|
|
end;
|
|
if not (ssCtrl in Shift) then
|
|
begin
|
|
ClearSelection;
|
|
FocusedNode.Selected := True;
|
|
end;
|
|
Exit;
|
|
end
|
|
else
|
|
if Key in [VK_INSERT, VK_DOWN, VK_UP] then
|
|
begin
|
|
if CanModify and (egoEditing in Options)
|
|
and not (egoRowSelect in Options) then
|
|
case Key of
|
|
VK_INSERT : if not (ssCtrl in Shift) and (egoCanInsert in Options) and (egoCanNavigation in Options) then
|
|
begin
|
|
ClearSelection;
|
|
Insert;
|
|
end;
|
|
VK_DOWN :begin
|
|
{ if (State = dsInsert) and
|
|
not Modified and
|
|
not FDatalink.FModified and EOF then Exit;}
|
|
if (State = dsInsert) and not Modified and not FDatalink.IsModified then
|
|
begin
|
|
if not EOF then Cancel;
|
|
Exit;
|
|
end;
|
|
inherited KeyDown(Key, Shift);
|
|
if (egoCanAppend in Options) and
|
|
(egoCanInsert in Options) and EOF and
|
|
not ((State = dsInsert) and not Modified and
|
|
not FDatalink.IsModified) then
|
|
begin
|
|
if not FSelecting and (SelectedCount = 1) then ClearSelection;
|
|
Append;
|
|
end;
|
|
Exit;
|
|
end;
|
|
VK_UP : begin
|
|
if (State = dsInsert) and not Modified and EOF and
|
|
not FDatalink.IsModified then Cancel
|
|
else inherited KeyDown(Key, Shift);
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
if (egoMultiSelect in Options) and (ssShift in Shift) and
|
|
(Key in [VK_HOME, VK_END, VK_PRIOR, VK_NEXT]) then
|
|
begin
|
|
case Key of
|
|
VK_HOME: SelectRecords(True, 0);
|
|
VK_END: SelectRecords(False, 0);
|
|
VK_PRIOR: begin
|
|
if FocusedNode <> TopVisibleNode then
|
|
D := FocusedNode.Index - TopVisibleNode.Index
|
|
else D := RowCount - 1;
|
|
if D < 1 then D := 1;
|
|
SelectRecords(True, D);
|
|
end;
|
|
VK_NEXT: begin
|
|
if FocusedNode <> LastNode then
|
|
D := LastNode.Index - FocusedNode.Index
|
|
else D := RowCount - 1;
|
|
if D < 1 then D := 1;
|
|
SelectRecords(False, D);
|
|
end;
|
|
end;
|
|
Exit;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if not (egoRowSelect in Options) and not NewItemRowActive and
|
|
(FocusedNode <> Nil) and (FocusedNode.HasChildren) and
|
|
(Key in [VK_LEFT, VK_RIGHT]) then
|
|
case Key of
|
|
VK_LEFT : if FocusedNode.Expanded then
|
|
FocusedNode.Expanded := False;
|
|
VK_RIGHT : if not FocusedNode.Expanded then
|
|
FocusedNode.Expanded := True;
|
|
end;
|
|
if not (egoRowSelect in Options) and CanModify then
|
|
if ((Key = VK_INSERT) and not (ssCtrl in Shift)) and
|
|
(egoCanInsert in Options) and (egoCanNavigation in Options) then
|
|
begin
|
|
if (Self.State = tsEditing) then CloseEditor;
|
|
ClearSelection;
|
|
Insert;
|
|
ShowEditor;
|
|
end;
|
|
case Key of
|
|
VK_UP: if (State in dsEditModes) then
|
|
begin
|
|
if (State = dsInsert) and not Modified and EOF and
|
|
not FDatalink.IsModified then
|
|
begin
|
|
Cancel;
|
|
FocusedNumber := (GetAbsoluteCount-1);
|
|
Exit;
|
|
end
|
|
else
|
|
if (FocusedNumber = 0) and IsNeedPost(True{Up}) then Post;
|
|
end;
|
|
VK_DOWN: if FocusedNumber = (GetAbsoluteCount-1) then
|
|
begin
|
|
if (State = dsInsert) and
|
|
not Modified and
|
|
not FDatalink.IsModified then Exit;
|
|
if (egoCanAppend in Options) and CanModify and
|
|
(egoCanInsert in Options) and (egoCanNavigation in Options) and
|
|
not ((State = dsInsert) and not Modified and
|
|
not FDatalink.IsModified) then
|
|
begin
|
|
if not FSelecting and (SelectedCount = 1) then ClearSelection;
|
|
Append;
|
|
end
|
|
else
|
|
if (State in dsEditModes) and IsNeedPost(False{Down}) then Post;
|
|
end;
|
|
end;
|
|
end;
|
|
if (egoRowSelect in Options) and (Key in [VK_LEFT, VK_RIGHT]) and
|
|
(FocusedNode <> Nil) and not FocusedNode.HasChildren then
|
|
begin
|
|
case Key of
|
|
VK_LEFT : PostMessage(self.Handle, WM_HScroll, SB_LINEUP, 0);
|
|
VK_RIGHT : PostMessage(self.Handle, WM_HScroll, SB_LINEDOWN, 0);
|
|
end;
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
inherited KeyDown(Key, Shift);
|
|
finally
|
|
OnKeyDown := KeyDownEvent;
|
|
EndSelection;
|
|
FInKeyDown := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.Loaded;
|
|
var
|
|
PrevLoadedLayout: Boolean;
|
|
begin
|
|
if IsCustomStoring then Exit;
|
|
FLoaded := True;
|
|
BeginUpdate;
|
|
try
|
|
PrevLoadedLayout := LoadedLayout;
|
|
LoadedLayout := True;
|
|
try
|
|
inherited Loaded;
|
|
finally
|
|
LoadedLayout := PrevLoadedLayout;
|
|
end;
|
|
FlagFullRefresh := True;
|
|
try
|
|
RefreshGroupColumns;
|
|
finally
|
|
FlagFullRefresh := False;
|
|
end;
|
|
CheckSortedColumns;
|
|
{RefreshDefWidth before LoadFromReg }
|
|
RefreshDefaultColumnsWidths;
|
|
// Load From Ini/Regsirty
|
|
DoRestoreLayout;
|
|
// if GroupColumnCount = 0 then CheckSorted;
|
|
// calc best panel size
|
|
if IsAutoHeaderPanelRowCount then HeaderPanelBestFit;
|
|
finally
|
|
EndUpdate;
|
|
FLoaded := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
I: Integer;
|
|
HitInfo: TdxTreeListHitInfo;
|
|
begin
|
|
HitInfo := GetHitInfo(Point(X, Y));
|
|
if HitInfo.hitType = htGroupPanel then
|
|
begin
|
|
if GroupColumnCount > 0 then
|
|
begin
|
|
FDragGroupColumn := nil;
|
|
for I := 0 to GroupColumnCount - 1 do
|
|
if PtInRect(GetGroupHeaderRect(I), Point(X, Y)) then
|
|
begin
|
|
if CanHeaderDragging(GroupColumns[I].Index) then
|
|
FDragGroupColumn := GroupColumns[I];
|
|
Break;
|
|
end;
|
|
if (Button = mbLeft) and (FDragGroupColumn <> nil) then
|
|
begin
|
|
FGroupColumnPointDragging.X := X;
|
|
FGroupColumnPointDragging.Y := Y;
|
|
FGroupColumnDragFlag := True;
|
|
if ssDouble in Shift then Shift := Shift - [ssDouble];
|
|
end;
|
|
end;
|
|
end;
|
|
if (Button = mbLeft) and (ssDouble in Shift) and
|
|
(State = tsNormal) and (HitInfo.hitType in (NodeHitTests + [htIndicator])) and
|
|
(HitInfo.Node <> nil) and (not HitInfo.Node.HasChildren) then
|
|
begin
|
|
FreeClickTimer;
|
|
{if CanDblClick then }DblClick;
|
|
Exit;
|
|
end;
|
|
inherited MouseDown(Button, Shift, X, Y);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
if FGroupColumnDragFlag and (FDragGroupColumn <> nil) then
|
|
if ((X < FGroupColumnPointDragging.X - 5) or (X > FGroupColumnPointDragging.X + 5)
|
|
or (Y < FGroupColumnPointDragging.Y - 5) or (Y > FGroupColumnPointDragging.Y + 5)) then
|
|
begin
|
|
FDragAbsoluteHeaderIndex := FDragGroupColumn.Index;
|
|
StartDragHeader(FDragAbsoluteHeaderIndex);
|
|
FGroupColumnDragFlag := False;
|
|
end;
|
|
inherited MouseMove(Shift, X, Y);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
if (FDragGroupColumn <> nil) and (State <> tsColumnDragging) and
|
|
(Button = mbLeft) then
|
|
begin
|
|
SetState(tsNormal);
|
|
if IsAutoSort then DoColumnSort(FDragGroupColumn);
|
|
end;
|
|
|
|
if (State = tsColumnDragging) then
|
|
EndDragHeader(True);
|
|
|
|
FGroupColumnDragFlag := False;
|
|
FDragGroupColumn := nil;
|
|
|
|
inherited MouseUp(Button, Shift, X, Y);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.Notification(AComponent: TComponent; // TODO remove SmrField!
|
|
Operation: TOperation);
|
|
var
|
|
I: Integer;
|
|
NeedLayout, IsKeyField: Boolean;
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if (Operation = opRemove) then
|
|
begin
|
|
if (FDataLink <> nil) then
|
|
if (AComponent = DataSource) then
|
|
DataSource := nil
|
|
else
|
|
if (AComponent is TField) then
|
|
begin
|
|
NeedLayout := False;
|
|
for I := 0 to ColumnCount-1 do
|
|
with TdxDBGridColumn(Columns[I]) do
|
|
if (Field = AComponent) or (FPreviewField = AComponent) or
|
|
((FKeyField = AComponent) and not IsGridMode and FDataLink.Active) then
|
|
begin
|
|
NeedLayout := True;
|
|
FField := nil;
|
|
end;
|
|
// check KeyField (OnChangeNodeEx)
|
|
IsKeyField := FKeyField = AComponent;
|
|
// reset if needed
|
|
if NeedLayout and Assigned(FDatalink.Dataset) and
|
|
(not FDatalink.Dataset.ControlsDisabled) then
|
|
begin
|
|
if IsKeyField then FKeyField := nil;
|
|
BeginUpdate;
|
|
try
|
|
for I := 0 to ColumnCount-1 do
|
|
with Columns[I] do
|
|
if Field = AComponent then
|
|
FieldName := FieldName;
|
|
if IsKeyField and (not IsGridMode) and FDataLink.Active then
|
|
LinkActive(FDataLink.DataSet.Active);
|
|
if (FPreviewField = AComponent) then FPreviewField := Nil;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end
|
|
else
|
|
if IsKeyField then FKeyField := nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.WndProc(var Message: TMessage);
|
|
var
|
|
P: TPoint;
|
|
begin
|
|
if Assigned(FFilterPopupListBox) and FFilterPopupListBox.ListVisible then
|
|
begin
|
|
case Message.Msg of
|
|
WM_KEYDOWN, WM_SYSKEYDOWN, WM_CHAR:
|
|
with Message do
|
|
SendMessage(FFilterPopupListBox.Handle, Msg, WParam, LParam);
|
|
WM_MOUSEFIRST .. WM_MOUSELAST:
|
|
begin
|
|
P := SmallPointToPoint(TWMMouse(Message).Pos);
|
|
MapWindowPoints(Handle, FFilterPopupListBox.Handle, P, 1);
|
|
if PtInRect(FFilterPopupListBox.ClientRect, P) then
|
|
begin
|
|
with Message do
|
|
SendMessage(FFilterPopupListBox.Handle, Msg, WParam, MakeLParam(P.X, P.Y))
|
|
end
|
|
else
|
|
if (Message.Msg = WM_LBUTTONDOWN) or (Message.Msg = WM_LBUTTONUP) or
|
|
(Message.Msg = WM_MOUSEMOVE) then
|
|
inherited;
|
|
end;
|
|
WM_KILLFOCUS, WM_CANCELMODE, CM_CANCELMODE:
|
|
begin
|
|
with TCMCancelMode(Message) do
|
|
if (Sender <> FFilterPopupListBox) then
|
|
FFilterPopupListBox.ClosePopup(False); // TODO global method
|
|
end;
|
|
else
|
|
inherited;
|
|
end;
|
|
end
|
|
else
|
|
inherited;
|
|
end;
|
|
|
|
// Size
|
|
function TCustomdxDBGrid.GetGroupHeaderRect(Index: Integer): TRect;
|
|
begin
|
|
Result.Left := agpHOfs + Index*(agpHWidht + agpHDeltaX);
|
|
Result.Top := agpHOfs + Index*((HeaderRowHeight div 2) + agpHDeltaY);
|
|
Result.Right := Result.Left + agpHWidht;
|
|
Result.Bottom := Result.Top + HeaderRowHeight;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetGroupPanelHeight: Integer;
|
|
begin
|
|
Result := 0;
|
|
if FGroupPanelVisible then
|
|
Result := CalcGroupPanelHeight(GroupColumnCount);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetIndentWidth: Integer;
|
|
begin
|
|
Result := GroupColumnCount * Indent;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetScrollVertGridRect: TRect;
|
|
var
|
|
RNode: TRect;
|
|
begin
|
|
Result := inherited GetScrollVertGridRect;
|
|
if IsGridMode and not (IsRowAutoHeight or (IsShowPreview and IsAutoCalcPreviewLines)) then
|
|
begin
|
|
if Count > 0 then
|
|
begin
|
|
RNode := GetRectNode(Items[Count - 1]);
|
|
if Result.Bottom > RNode.Bottom then Result.Bottom := RNode.Bottom;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetVisibleRowCount: Integer;
|
|
begin
|
|
if IsGridMode then Result := RowCount
|
|
else Result := inherited GetVisibleRowCount;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.GetVScrollInfo(var Min, Max, Pos : Integer; var Page, Mask : UINT);
|
|
begin
|
|
if IsGridMode then
|
|
begin
|
|
Min := 0;
|
|
Page := 0;
|
|
Max := 4;
|
|
// Pos := 0;
|
|
|
|
if FDatalink.Active and FDatalink.DataSet.Active and
|
|
HandleAllocated then
|
|
with FDatalink.DataSet do
|
|
begin
|
|
{$IFDEF DELPHI3}
|
|
if IsSequenced then
|
|
begin
|
|
Min := 1;
|
|
Page := Self.RowCount;
|
|
Max := RecordCount + Integer(Page) -1;
|
|
if FDatalink.DataSet.State in [dsInactive, dsBrowse, dsEdit] then
|
|
try
|
|
if FDatalink.DataSet.RecNo <> -1 then
|
|
Pos := FDatalink.DataSet.RecNo;
|
|
except
|
|
end;
|
|
end
|
|
else
|
|
{$ENDIF}
|
|
begin
|
|
Min := 0;
|
|
Page := 0;
|
|
Max := 4;
|
|
if BOF then Pos := 0
|
|
else if EOF then Pos := 4
|
|
else Pos := 2;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
inherited GetVScrollInfo(Min, Max, Pos, Page, Mask);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.UpdateRowCount;
|
|
var
|
|
DrawInfo: TdxGridDrawInfo;
|
|
H, RealH, RHeight: Integer;
|
|
begin
|
|
inherited UpdateRowCount;
|
|
if IsGridMode and FDataLink.Active then
|
|
begin
|
|
CalcRectInfo(DrawInfo);
|
|
H := DrawInfo.CellsRect.Bottom - DrawInfo.CellsRect.Top;
|
|
if not (IsRowAutoHeight or (IsShowPreview and IsAutoCalcPreviewLines)) then
|
|
begin
|
|
RHeight := GetRowHeight(FocusedNode, DefaultRowHeight, True{ReCalc});
|
|
FRowCount := (H div RHeight);
|
|
FVisibleRowCount := (H div RHeight) + Byte((H mod RHeight) <> 0);
|
|
end
|
|
else
|
|
begin
|
|
RealH := CalcRowCount(TopVisibleNode, H, FRowCount, FVisibleRowCount);
|
|
if (RealH < H) then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
while True do
|
|
begin
|
|
with FDataLink do
|
|
begin
|
|
BufferCount := Self.Count + 1; // NodeCount
|
|
if RecordCount < BufferCount then Break;
|
|
Add;
|
|
end;
|
|
RealH := CalcRowCount(TopVisibleNode, H, FRowCount, FVisibleRowCount);
|
|
if (RealH >= H) or (FVisibleRowCount > FRowCount) then
|
|
begin
|
|
if (FRowCount > 0) and (FVisibleRowCount > FRowCount) then
|
|
begin
|
|
Self.Items[Self.Count - 1].Free;
|
|
FDataLink.BufferCount := Self.Count;
|
|
end;
|
|
Break;
|
|
end;
|
|
end;
|
|
finally
|
|
CancelUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
if FVisibleRowCount < 1 then FVisibleRowCount := 1;
|
|
if FRowCount < 1 then FRowCount := 1;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.UpdateScrollBars;
|
|
|
|
procedure SetNodeCount(Value: Integer);
|
|
var
|
|
OldNodeCount, i: Integer;
|
|
begin
|
|
OldNodeCount := GetAbsoluteCount;
|
|
if Value = OldNodeCount then Exit;
|
|
if OldNodeCount < Value then
|
|
for i:= 0 to (Value - OldNodeCount)-1 do Add
|
|
else
|
|
for i:= 0 to (OldNodeCount-Value)-1 do Items[Count-1].Free;
|
|
Invalidate;
|
|
end;
|
|
|
|
begin
|
|
inherited UpdateScrollBars;
|
|
if FUpdateBufferCount then Exit;
|
|
FUpdateBufferCount := True;
|
|
if IsGridMode then
|
|
with FDataLink do
|
|
begin
|
|
if not Active or (RecordCount = 0) or not HandleAllocated then
|
|
SetNodeCount(1)
|
|
else
|
|
begin
|
|
if FDataLink.BufferCount <> RowCount then
|
|
FDataLink.BufferCount := RowCount;
|
|
if FocusedNumber <> FDatalink.ActiveRecord then CloseEditor;
|
|
SetNodeCount(RecordCount);
|
|
end;
|
|
UpdateActive;
|
|
end
|
|
else
|
|
if FDataLink.Active then
|
|
FDataLink.BufferCount := 1;
|
|
FUpdateBufferCount := False;
|
|
end;
|
|
|
|
// Drag & Drop & Customizing
|
|
procedure TCustomdxDBGrid.CalcArrowsPos(var P: TPoint; PPosInfo: Pointer; IsBand: Boolean;
|
|
DownIndex, DragIndex: Integer);
|
|
var
|
|
ArrowDelta: Integer;
|
|
Pos: TPoint;
|
|
Col: Integer;
|
|
R: TRect;
|
|
begin
|
|
Pos := ScreenToClient(P);
|
|
if not IsBand and ShowGroupPanel and PointInGroupPanel(POS) then
|
|
begin
|
|
SetInfo(PPosInfo, -1, -1, -1, -1);
|
|
P := Point(-100, -100);
|
|
ArrowDelta := arWidth div 2;
|
|
Col := GetColumnArrowsPos(Pos);
|
|
if (DragIndex <> -1) and (TdxDBGridColumn(Columns[DragIndex]).GroupIndex = -1) and
|
|
not CanAddGroupColumn(Columns[DragIndex]) then Exit;
|
|
R := GetGroupHeaderRect(Col);
|
|
Pos.X := R.Left - ArrowDelta;
|
|
Pos.Y := R.Top - arHeight;
|
|
P := ClientToScreen(Pos);
|
|
end
|
|
else
|
|
inherited CalcArrowsPos(P, PPosInfo, IsBand, DownIndex, DragIndex);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoEndDragGroupColumn(Column: TdxDBTreeListColumn; NewGroupIndex: Integer; var Accept: Boolean);
|
|
begin
|
|
if Assigned(FOnEndDragGroupColumn) then
|
|
FOnEndDragGroupColumn(Self, Column, NewGroupIndex, Accept);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.EndDragHeader(Flag: Boolean);
|
|
var
|
|
P, P1 : TPoint;
|
|
Column, Index: Integer;
|
|
Col: TdxDBGridColumn{TdxDBTreeListColumn};
|
|
PrevVisivle, Accept: Boolean;
|
|
begin
|
|
try
|
|
if Flag then
|
|
begin
|
|
GetCursorPos(P);
|
|
P1 := P;
|
|
P := ScreenToClient(P);
|
|
if ShowGroupPanel and not PointInCustomizingForm(P1) and
|
|
PointInGroupPanel(P) then
|
|
begin
|
|
if FDragAbsoluteHeaderIndex <> -1 then
|
|
begin
|
|
Col := TdxDBGridColumn(Columns[FDragAbsoluteHeaderIndex]);
|
|
inherited EndDragHeader(False);
|
|
Index := GetColumnArrowsPos(P);
|
|
if Col <> nil then
|
|
begin
|
|
if (Col.GroupIndex <> -1) and (Index >= GroupColumnCount) then
|
|
Index := GroupColumnCount - 1;
|
|
Accept := True;
|
|
DoEndDragGroupColumn(Col, Index, Accept);
|
|
if Accept then
|
|
begin
|
|
Col.GroupIndex := Index;
|
|
if not Col.Visible and (Col.GroupIndex = -1) then // from customize form
|
|
begin
|
|
if Assigned(OnShowHeader) then OnShowHeader(Self, Col, -1, -1, -1);
|
|
end;
|
|
end;
|
|
end;
|
|
Exit;
|
|
end;
|
|
end
|
|
else if FDragGroupColumn <> nil then
|
|
begin
|
|
Column := FGroupColumns.IndexOf(FDragGroupColumn);
|
|
if Column <> -1 then
|
|
begin
|
|
Col := GroupColumns[Column];
|
|
PrevVisivle := Col.Visible;
|
|
inherited EndDragHeader(Flag);
|
|
DeleteGroupColumn(Column);
|
|
// check sorted
|
|
CheckSorted;
|
|
if not PrevVisivle and PointInCustomizingForm(P1) and not Col.DisableCustomizing and
|
|
not ((GetVisibleHeaderCount = 1) and (VisibleColumns[0] = Col)) then Col.Visible := False;
|
|
if not Col.Visible then
|
|
begin
|
|
if Assigned(OnHideHeader) then OnHideHeader(Self, Col);
|
|
end;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
inherited EndDragHeader(Flag);
|
|
finally
|
|
FDragGroupColumn := nil;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetColumnArrowsPos(Pos: TPoint): Integer;
|
|
var
|
|
R: TRect;
|
|
I: Integer;
|
|
begin
|
|
Result := 0;
|
|
for I := 0 to GroupColumnCount - 1 do
|
|
begin
|
|
R := GetGroupHeaderRect(I);
|
|
if Pos.X < ((R.Right + R.Left) div 2) then
|
|
begin
|
|
Result := I;
|
|
Break;
|
|
end;
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.GetDragImageCursor(P: TPoint; var ADragCursor: TCursor);
|
|
var
|
|
Column: TdxDBGridColumn{TdxDBTreeListColumn};
|
|
begin
|
|
inherited GetDragImageCursor(p, ADragCursor);
|
|
if ShowGroupPanel and not PointInCustomizingForm(ClientToScreen(P)) then
|
|
begin
|
|
if PointInGroupPanel(P) then
|
|
begin
|
|
ADragCursor := Cursor;
|
|
if (FDragAbsoluteHeaderIndex <> -1) then
|
|
begin
|
|
Column := TdxDBGridColumn(Columns[FDragAbsoluteHeaderIndex]);
|
|
if (Column.GroupIndex = -1) and not CanAddGroupColumn(Column) and
|
|
((GroupColumnCount >= 0) and (FDragGroupColumn = nil)) then
|
|
ADragCursor := crdxTreeListDeleteCursor;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetIsCustomizing: Boolean;
|
|
begin
|
|
Result := inherited GetIsCustomizing;
|
|
// if FDragGroupColumn <> Nil then Result := True;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsHeaderCustomizing: Boolean;
|
|
begin
|
|
Result := inherited IsHeaderCustomizing;
|
|
if FDragGroupColumn <> nil then
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.HideHeader(AbsoluteIndex: Integer);
|
|
begin
|
|
inherited HideHeader(AbsoluteIndex);
|
|
if Assigned(FOnHideColumnEvent) then FOnHideColumnEvent(Self, Columns[AbsoluteIndex]);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsLastNode(ANode: TdxTreeListNode) : Boolean;
|
|
begin
|
|
Result := inherited IsLastNode(ANode);
|
|
if IsGridMode and Datalink.Active then
|
|
Result := Datalink.DataSet.EOF
|
|
else
|
|
if IsPartialLoad then Result := Result and FGridEOF;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsTopNode(ANode: TdxTreeListNode) : Boolean;
|
|
begin
|
|
Result := inherited IsTopNode(ANode);
|
|
if IsGridMode and Datalink.Active then
|
|
Result := Datalink.DataSet.BOF
|
|
else
|
|
if IsPartialLoad then Result := Result and FGridBOF;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ShowColumnHeader(BandIndex, RowIndex, ColIndex, AbsoluteIndex: Integer);
|
|
begin
|
|
inherited ShowColumnHeader(BandIndex, RowIndex, ColIndex, AbsoluteIndex);
|
|
if Assigned(FOnShowColumnEvent) then OnShowColumn(Self, Columns[AbsoluteIndex]);
|
|
end;
|
|
|
|
// Paint
|
|
function TCustomdxDBGrid.AssignedDrawCellEvent(ANode: TdxTreeListNode; AbsoluteIndex: Integer): Boolean;
|
|
begin
|
|
Result := inherited AssignedDrawCellEvent(ANode, AbsoluteIndex) or
|
|
Assigned(FOnCustomDraw);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DrawGroupPanel(ACanvas: TCanvas; ARect: TRect; HeaderBrush, PanelBrush: HBRUSH);
|
|
|
|
procedure DrawLines;
|
|
var
|
|
Points: PIntArray;
|
|
Strokes: PIntArray;
|
|
MaxLines, I, J: Integer;
|
|
R1, R2: TRect;
|
|
begin
|
|
if GroupColumnCount = 0 then Exit;
|
|
MaxLines := (GroupColumnCount - 1);
|
|
if MaxLines > 0 then
|
|
begin
|
|
Points := AllocMem(MaxLines * SizeOf(TPoint) * 3);
|
|
Strokes := AllocMem(MaxLines * SizeOf(Integer));
|
|
for I := 0 to MaxLines - 1 do Strokes^[I] := 3;
|
|
for I := 0 to MaxLines - 1 do
|
|
begin
|
|
R1 := GetGroupHeaderRect(I);
|
|
R2 := GetGroupHeaderRect(I + 1);
|
|
J := I * 6;
|
|
Points^[J] := (R1.Left + R1.Right) div 2;
|
|
Points^[J + 1] := R1.Bottom - 1;
|
|
Points^[J + 2] := Points^[J];
|
|
Points^[J + 3] := Points^[J + 1] + 3;
|
|
Points^[J + 4] := R2.Left + 1;
|
|
Points^[J + 5] := Points^[J + 1] + 3;
|
|
end;
|
|
ACanvas.Pen.Color := dxclGroupPanelLine{clBlack};
|
|
PolyPolyLine(ACanvas.Handle, Points^, Strokes^, MaxLines);
|
|
FreeMem(Strokes);
|
|
FreeMem(Points);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
FText: string;
|
|
I: Integer;
|
|
begin
|
|
with ACanvas do
|
|
begin
|
|
// Custom Draw
|
|
if Assigned(FOnBackgroundDrawEvent) then
|
|
FOnBackgroundDrawEvent(Self, ACanvas, ARect)
|
|
else
|
|
begin
|
|
if (LookAndFeel = lfUltraFlat) and (FGroupPanelColor = clBtnShadow){Default} then
|
|
Windows.FillRect(ACanvas.Handle, ARect, PanelBrush)
|
|
else
|
|
begin
|
|
Brush.Color := FGroupPanelColor;
|
|
FillRect(ARect);
|
|
end;
|
|
if GroupColumnCount = 0 then
|
|
begin
|
|
Font.Assign(Self.HeaderFont);
|
|
Font.Color := FGroupPanelFontColor;
|
|
FText := ' ' + sdxPanelText;
|
|
SetBkMode(Handle, TRANSPARENT);
|
|
DrawText(Handle, PChar(FText), Length(FText), ARect,
|
|
DT_LEFT or DT_EXPANDTABS or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER);
|
|
end;
|
|
end;
|
|
DrawLines;
|
|
Font.Assign(Self.HeaderFont);
|
|
for I := 0 to GroupColumnCount - 1 do
|
|
begin
|
|
ARect := GetGroupHeaderRect(I);
|
|
if RectVisible(Handle, ARect) then
|
|
begin
|
|
DrawBandEx(Handle, ARect, ARect, HeaderBrush, DrawBitmap,
|
|
GroupColumns[I].Caption, False{ADown}, False{AMultiLine},
|
|
taLeftJustify, GroupColumns[I].Sorted, GetHeaderGlyph(GroupColumns[I].Index), hbNormal,
|
|
LookAndFeel, []{GetHeaderDropDownButtonState(GroupColumns[I].Index) TODO });
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoDrawCell(ACanvas: TCanvas; var ARect: TRect; ANode: TdxTreeListNode; AIndex: Integer; ASelected, AFocused: Boolean;
|
|
ANewItemRow: Boolean; ALeftEdge, ARightEdge: Boolean; ABrush: HBRUSH;
|
|
var AText: string; var AColor: TColor; AFont: TFont; var AAlignment: TAlignment; var ADone: Boolean);
|
|
var
|
|
Column: TdxDBTreeListColumn;
|
|
begin
|
|
if (ANode <> nil) and ANode.HasChildren then
|
|
AIndex := GroupColumns[ANode.Level].Index;
|
|
// obsolete
|
|
Column := Columns[AIndex];
|
|
if Assigned(FOnCustomDraw) then FOnCustomDraw(Self, ACanvas, ARect, ANode, Column, AText,
|
|
AFont, AColor, ASelected, AFocused, ADone);
|
|
if ADone then Exit;
|
|
inherited DoDrawCell(ACanvas, ARect, ANode, AIndex, ASelected, AFocused,
|
|
ANewItemRow, ALeftEdge, ARightEdge, ABrush, AText, AColor, AFont, AAlignment, ADone);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoHeaderClick(AbsoluteIndex: Integer);
|
|
begin
|
|
if Assigned(FOnColumnClick) then FOnColumnClick(Self, Columns[AbsoluteIndex]);
|
|
inherited DoHeaderClick(AbsoluteIndex);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoHeaderDropDownButtonClick(AbsoluteIndex: Integer);
|
|
var
|
|
P: TPoint;
|
|
R: TRect;
|
|
|
|
function GetHeaderRect: TRect;
|
|
var
|
|
DrawInfo: TdxGridDrawInfo;
|
|
I: Integer;
|
|
begin
|
|
SetRectEmpty(Result);
|
|
CalcDrawInfo(DrawInfo);
|
|
try
|
|
with DrawInfo do
|
|
for I := 0 to HeaderCount - 1 do
|
|
if HeadersInfo^[I].AbsoluteIndex = AbsoluteIndex then
|
|
begin
|
|
Result := HeadersInfo^[I].HeaderRect;
|
|
Break;
|
|
end;
|
|
finally
|
|
FreeDrawInfo(DrawInfo);
|
|
end;
|
|
end;
|
|
|
|
procedure LoadItems;
|
|
var
|
|
I, J: Integer;
|
|
FlagNullExist: Boolean;
|
|
|
|
procedure AddValue(const V: Variant; const S: string);
|
|
begin
|
|
if VarIsEmpty(V) or VarIsNull(V) or ((VarType(V) = varString) and (V = '')) then
|
|
FlagNullExist := True
|
|
else
|
|
FFilterPopupListBox.FilterValues.AddValue(S, V, not IsAutoFilterValuesLoad);
|
|
end;
|
|
|
|
procedure LoadValueFromNode(Node: TdxTreeListNode);
|
|
var
|
|
I: Integer;
|
|
S: string;
|
|
V: Variant;
|
|
begin
|
|
if Node.Count > 0 then
|
|
for I := 0 to Node.Count - 1 do
|
|
LoadValueFromNode(Node[I])
|
|
else
|
|
begin
|
|
TdxDBGridColumn(FFilterPopupListBox.Column).GetFilterValues(Node, V, S);
|
|
AddValue(V, S);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
V: Variant;
|
|
S: string;
|
|
AColumnCriteria: TdxDBGridFilterColumnCriteria;
|
|
ARepeatEnumeration: Boolean;
|
|
begin
|
|
// Load Filter Values
|
|
FFilterPopupListBox.FilterValues.Clear;
|
|
FFilterPopupListBox.FilterValues.AnsiSort := IsAnsiSort;
|
|
FFilterPopupListBox.FilterValues.CaseInsensitive := Filter.CaseInsensitive;
|
|
FFilterPopupListBox.FilterValues.MaxCount := Filter.MaxDropDownCount;
|
|
FlagNullExist := False;
|
|
if IsAutoFilterValuesLoad then
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
LoadValueFromNode(Items[I]);
|
|
for I := 0 to FHiddenList.Count - 1 do
|
|
LoadValueFromNode(TdxTreeListNode(FHiddenList[I]));
|
|
end
|
|
else // Custom Event
|
|
begin
|
|
if Assigned(FOnEnumFilterValues) then
|
|
begin
|
|
repeat
|
|
ARepeatEnumeration := True;
|
|
S := '';
|
|
V := Null;
|
|
FOnEnumFilterValues(Self, FFilterPopupListBox.Column, V, S, ARepeatEnumeration);
|
|
AddValue(V, S);
|
|
until not ARepeatEnumeration;
|
|
end;
|
|
end;
|
|
// ListBox Items
|
|
FFilterPopupListBox.Items.Clear;
|
|
FFilterPopupListBox.Items.Assign(FFilterPopupListBox.FilterValues);
|
|
FFilterPopupListBox.Items.Insert(0, dxSFilterBoxAllCaption);
|
|
FFilterPopupListBox.Items.Insert(1, dxSFilterBoxCustomCaption);
|
|
J := 2;
|
|
if FlagNullExist then
|
|
begin
|
|
FFilterPopupListBox.Items.Insert(2, dxSFilterBoxBlanksCaption);
|
|
FFilterPopupListBox.Items.Insert(3, dxSFilterBoxNonBlanksCaption);
|
|
Inc(J, 2);
|
|
end;
|
|
// Set Index
|
|
with FFilterPopupListBox do
|
|
begin
|
|
AColumnCriteria := Grid.Filter.GetFilterColumnCriteria(FFilterPopupListBox.Column, V);
|
|
case AColumnCriteria of
|
|
fcBlanks:
|
|
if FlagNullExist then ItemIndex := 2;
|
|
fcNonBlanks:
|
|
if FlagNullExist then ItemIndex := 3;
|
|
fcValue:
|
|
begin
|
|
for I := 0 to FilterValues.Count - 1 do
|
|
if PVariant(FilterValues.Objects[I])^ = V then
|
|
begin
|
|
ItemIndex := J + I;
|
|
Break;
|
|
end;
|
|
end;
|
|
fcCustom:
|
|
ItemIndex := 1;
|
|
else
|
|
ItemIndex := 0; // All
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure PrepareFilterPopupListBox;
|
|
var
|
|
I, W, TW: Integer;
|
|
begin
|
|
with FFilterPopupListBox do
|
|
begin
|
|
Visible := False;
|
|
Parent := Self;
|
|
Column := Self.Columns[AbsoluteIndex];
|
|
IntegralHeight := True;
|
|
Color := Self.Color;
|
|
Font := Self.Font;
|
|
if LookAndFeel = lfUltraFlat then
|
|
PopupBorderStyle := pbSingle
|
|
else
|
|
PopupBorderStyle := pbFlat;
|
|
LoadItems;
|
|
// check width, height
|
|
if (Grid.Filter.DropDownCount > 0) and (Items.Count >= Grid.Filter.DropDownCount) then
|
|
ClientHeight := Grid.Filter.DropDownCount * RealItemHeight
|
|
else
|
|
ClientHeight := Items.Count * RealItemHeight;
|
|
// calc width
|
|
Width := R.Right - R.Left;
|
|
if Filter.DropDownWidth <> 0 then
|
|
W := Filter.DropDownWidth
|
|
else
|
|
begin
|
|
W := ClientWidth;
|
|
for I := 0 to Items.Count - 1 do
|
|
begin
|
|
TW := Canvas.TextWidth(Items[I]);
|
|
if TW > W then W := TW;
|
|
end;
|
|
end;
|
|
ClientWidth := W + 4;
|
|
if Width < W then Width := W;
|
|
end;
|
|
end;
|
|
|
|
procedure ShowFilterPopupListBox;
|
|
begin
|
|
P := ClientToScreen(Point(R.Left, R.Bottom));
|
|
with FFilterPopupListBox do
|
|
begin
|
|
// Calc Position
|
|
CheckScreenPosition(P, Width, Height, R.Bottom - R.Top);
|
|
SetBounds(P.X, P.Y, Width, Height);
|
|
SetWindowPos(Handle, HWND_TOP, Left, Top, 0, 0,
|
|
SWP_NOSIZE or SWP_NOACTIVATE or SWP_SHOWWINDOW);
|
|
ListVisible := True;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
R := GetHeaderRect;
|
|
if not IsRectEmpty(R) then
|
|
begin
|
|
if FFilterPopupListBox = nil then
|
|
FFilterPopupListBox := TdxDBGridFilterPopupListBox.Create(Self);
|
|
PrepareFilterPopupListBox;
|
|
ShowFilterPopupListBox;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoStatusCloseButtonClick;
|
|
begin
|
|
Filter.Clear;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetGridColor(ABrushColor : TColor) : TColor;
|
|
begin
|
|
Result := inherited GetGridColor(ABrushColor);
|
|
if GroupColumnCount = 0 {IsGridMode} then
|
|
begin
|
|
Result := dxclTreeLineHighColor;
|
|
if (ColorToRGB(ABrushColor) = dxclTreeLineHighColor) or
|
|
(GetSysColor(COLOR_BTNFACE) = 0) {HighContrastBlack} or
|
|
(GetSysColor(COLOR_BTNFACE) = $00FFFFFF) {HighContrastWhite} then
|
|
Result := dxclTreeLineShadowColor;
|
|
end;
|
|
if GridLineColor <> clNone then
|
|
Result := GridLineColor;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.PrepareNode(ANode: TdxTreeListNode);
|
|
begin
|
|
if FLockPrepareNode = 0 then
|
|
if IsGridMode and FDataLink.Active then
|
|
begin
|
|
FOldActiveRecord := FDataLink.ActiveRecord;
|
|
FDataLink.ActiveRecord := ANode.Index;
|
|
end;
|
|
Inc(FLockPrepareNode);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.UnPrepareNode(ANode: TdxTreeListNode);
|
|
begin
|
|
Dec(FLockPrepareNode);
|
|
if FLockPrepareNode = 0 then
|
|
if IsGridMode and FDataLink.Active then
|
|
FDataLink.ActiveRecord := FOldActiveRecord;
|
|
end;
|
|
|
|
// Editor
|
|
function TCustomdxDBGrid.CanEditShow: Boolean;
|
|
begin
|
|
Result := inherited CanEditShow and not FocusedNode.HasChildren;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoBeforeEditing(Node : TdxTreeListNode; var AllowEditing: Boolean);
|
|
begin
|
|
if Node.HasChildren then
|
|
AllowEditing := False
|
|
else
|
|
inherited DoBeforeEditing(Node, AllowEditing);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.CanFullMultiSelect: Boolean;
|
|
begin
|
|
Result := not IsGridMode;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.CanNodeSelected(ANode: TdxTreeListNode): Boolean;
|
|
var
|
|
Node: TdxTreeListNode;
|
|
begin
|
|
Result := True;
|
|
if Assigned(OnCanNodeSelected) then OnCanNodeSelected(Self, ANode, Result);
|
|
if Result and not IsGridMode and not IsExtMultiSelect then
|
|
begin
|
|
Node := GetFirstSelectedNode;
|
|
if Node <> nil then
|
|
Result := not Node.HasChildren and not ANode.HasChildren;
|
|
end;
|
|
{ if Result then
|
|
if not IsGridMode and not (egoExtMultiSelect in Options) then
|
|
if GetSelectedCount > 0 then
|
|
Result := not ANode.HasChildren and
|
|
not (SelectedNodes[0].HasChildren);}
|
|
end;
|
|
|
|
function TCustomdxDBGrid.CompareSelectionAnchor(ANode: TdxTreeListNode) : Integer;
|
|
begin
|
|
if IsGridMode and (DataLink.Active) then
|
|
Result := CompareBkm(Datalink.DataSource.DataSet.Bookmark, FBkmSelectionAnchor)
|
|
else Result := inherited CompareSelectionAnchor(ANode);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoSelectedCountChange;
|
|
begin
|
|
inherited DoSelectedCountChange;
|
|
if not (csDestroying in ComponentState) and (LockSelection = 0) and
|
|
Assigned(OnChangeNodeEx) then
|
|
begin
|
|
CheckChangeNodeEx;
|
|
LoadChangeNodeInfo(FPrevNodeInfo);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.InvalidateSelection;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if IsGridMode then
|
|
begin
|
|
if SelectedCount = 0 then
|
|
if FocusedNode <> nil then
|
|
InvalidateRect(GetRectNode(FocusedNode))
|
|
else
|
|
else
|
|
for i := 0 to Count - 1 do
|
|
if IsNodeSelected(Items[i]) then
|
|
InvalidateRect(GetRectNode(Items[i]));
|
|
end
|
|
else inherited InvalidateSelection;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsNodeSelected(ANode:TdxTreeListNode):Boolean;
|
|
var
|
|
OldActive, Index : Integer;
|
|
begin
|
|
Result := False;
|
|
if GetSelectedCount = 0 then Exit;
|
|
if not IsGridMode then
|
|
begin
|
|
// Result := BkmList.IndexOfObject(ANode) <> -1;
|
|
if IsUseBookmarks then
|
|
Result := BkmList.IndexOfObject(ANode) <> -1
|
|
else Result := FindSelectedNode(ANode, Index); {Sorted List}
|
|
end
|
|
else
|
|
if DataLink.Active then
|
|
begin
|
|
if ANode.Index = -1 then Exit;
|
|
OldActive := DataLink.ActiveRecord;
|
|
try
|
|
DataLink.ActiveRecord := ANode.Index;
|
|
Result := FindBkm(Datalink.Datasource.Dataset.Bookmark, Index);
|
|
finally
|
|
DataLink.ActiveRecord := OldActive;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.NodeSelected(ANode:TdxTreeListNode; ASelected : Boolean);
|
|
var
|
|
Index: Integer;
|
|
Current: TBookmarkStr;
|
|
OldActive : Integer;
|
|
begin
|
|
if not (egoMultiSelect in Options) or not DataLink.Active then Exit;
|
|
|
|
if IsUseBookmarks then
|
|
begin
|
|
if not ((egoExtMultiSelect in Options) and (GroupColumnCount > 0)) then
|
|
begin
|
|
if not ANode.Focused and ASelected and not IsGridMode then Exit;
|
|
|
|
if not ANode.Focused then
|
|
begin
|
|
OldActive := 0;
|
|
try
|
|
if IsGridMode then
|
|
begin
|
|
OldActive := DataLink.ActiveRecord;
|
|
DataLink.ActiveRecord := ANode.Index;
|
|
end;
|
|
Current := Datalink.Datasource.Dataset.Bookmark;
|
|
finally
|
|
if IsGridMode then
|
|
DataLink.ActiveRecord := OldActive;
|
|
end;
|
|
end
|
|
else
|
|
Current := Datalink.Datasource.Dataset.Bookmark;
|
|
|
|
if ASelected and not CanNodeSelected(ANode) then Exit;
|
|
{***}
|
|
if not IsGridMode and not ASelected then
|
|
begin
|
|
Index := BkmList.IndexOfObject(ANode);
|
|
if (Index = -1) then Exit;
|
|
end
|
|
else
|
|
{***}
|
|
if (Length(Current) = 0) or (FindBkm(Current, Index) = ASelected) then Exit;
|
|
end
|
|
else
|
|
begin
|
|
if ASelected and not CanNodeSelected(ANode) then Exit;
|
|
Current := Datalink.Datasource.Dataset.Bookmark;
|
|
Index := BkmList.IndexOfObject(ANode);
|
|
if (Index <> -1) = ASelected then Exit;
|
|
if ASelected then FindBkm(Current, Index);
|
|
if Index = -1 then Index := 0;
|
|
end;
|
|
|
|
if ASelected then
|
|
begin
|
|
BkmList.Insert(Index, Current);
|
|
if not IsGridMode then BkmList.Objects[Index] := ANode
|
|
end
|
|
else
|
|
if Index <> -1 then BkmList.Delete(Index);
|
|
end
|
|
else
|
|
begin
|
|
if ASelected then
|
|
begin
|
|
if not CanNodeSelected(ANode) then Exit;
|
|
if not FindSelectedNode(ANode, Index) then
|
|
BkmList.InsertObject(Index, '', ANode);
|
|
end
|
|
else
|
|
if FindSelectedNode(ANode, Index) then
|
|
BkmList.Delete(Index);
|
|
end;
|
|
UpdateNode(ANode, False {no Below});
|
|
inherited NodeSelected(ANode, ASelected);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SelectNodes(N1, N2: TdxTreeListNode);
|
|
begin
|
|
if (Datalink.Dataset.State in dsEditModes) or not IsMultiSelect then Exit;
|
|
if IsPartialLoad and IsUseBookmarks then
|
|
begin
|
|
BeginSelection;
|
|
try
|
|
SaveBookmarks;
|
|
try
|
|
GetNextNodes(lmCurrent, rmNone, True)
|
|
finally
|
|
ClearBookmarks;
|
|
end;
|
|
finally
|
|
EndSelection;
|
|
end;
|
|
end
|
|
else
|
|
inherited SelectNodes(N1, N2);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SelectRecords(ADirectionUp: Boolean; ACount: Integer);
|
|
var
|
|
bm, Current: TBookmarkStr;
|
|
I: Integer;
|
|
FlagFound: Boolean;
|
|
begin
|
|
if not (egoMultiSelect in Options) or
|
|
not DataLink.Active or (Count = 0) or
|
|
not IsGridMode then Exit;
|
|
BeginSelection;
|
|
try
|
|
with Datalink.Dataset do
|
|
begin
|
|
DisableControls;
|
|
bm := Bookmark;
|
|
if Length(FBkmSelectionAnchor) = 0 then
|
|
FBkmSelectionAnchor := BookMark;
|
|
try
|
|
BkmList.Clear;
|
|
if ACount = 0 then
|
|
begin
|
|
BookMark := FBkmSelectionAnchor;
|
|
repeat
|
|
Current := Bookmark;
|
|
if ADirectionUp then
|
|
begin
|
|
BkmList.Insert(0, Current);
|
|
Prior;
|
|
end
|
|
else
|
|
begin
|
|
BkmList.Add(Current);
|
|
Next;
|
|
end;
|
|
until (ADirectionUp and Bof) or (not ADirectionUp and Eof);
|
|
end
|
|
else
|
|
begin
|
|
I := 0;
|
|
if ADirectionUp and (CompareBkm(FBkmSelectionAnchor, bm) < 0) then
|
|
begin
|
|
while (CompareBkm(FBkmSelectionAnchor, Bookmark) <> 0) and (I < ACount) do
|
|
begin
|
|
Prior;
|
|
Inc(I);
|
|
end;
|
|
if I = ACount then
|
|
begin
|
|
bm := Bookmark;
|
|
Bookmark := FBkmSelectionAnchor;
|
|
repeat
|
|
Current := Bookmark;
|
|
BkmList.Add(Current);
|
|
if (CompareBkm(Current, bm) = 0) or Eof then Break;
|
|
Next;
|
|
until False;
|
|
end
|
|
else
|
|
begin
|
|
repeat
|
|
Current := Bookmark;
|
|
BkmList.Insert(0, Current);
|
|
Inc(I);
|
|
if (I > ACount) or Bof then Break;
|
|
Prior;
|
|
until False;
|
|
end;
|
|
end
|
|
else
|
|
if not ADirectionUp and (CompareBkm(bm, FBkmSelectionAnchor) < 0) then
|
|
begin
|
|
while (CompareBkm(FBkmSelectionAnchor, Bookmark) <> 0) and (I < ACount) do
|
|
begin
|
|
Next;
|
|
Inc(I);
|
|
end;
|
|
if I = ACount then
|
|
begin
|
|
bm := Bookmark;
|
|
Bookmark := FBkmSelectionAnchor;
|
|
repeat
|
|
Current := Bookmark;
|
|
BkmList.Insert(0, Current);
|
|
if (CompareBkm(Current, bm) = 0) or Bof then Break;
|
|
Prior;
|
|
until False;
|
|
end
|
|
else
|
|
begin
|
|
repeat
|
|
Current := Bookmark;
|
|
BkmList.Add(Current);
|
|
Inc(I);
|
|
if (I > ACount) or Eof then Break;
|
|
Next;
|
|
until False;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
FlagFound := False;
|
|
Bookmark := FBkmSelectionAnchor;
|
|
repeat
|
|
Current := Bookmark;
|
|
if ADirectionUp then
|
|
BkmList.Insert(0, Current)
|
|
else BkmList.Add(Current);
|
|
if (CompareBkm(Current, bm) = 0) then FlagFound := True;
|
|
if FlagFound then Inc(I);
|
|
if (I > ACount) then Break;
|
|
if ADirectionUp then
|
|
Prior
|
|
else Next;
|
|
until ((ADirectionUp and Bof) or (not ADirectionUp and Eof));
|
|
end;
|
|
end;
|
|
FSelecting := True;
|
|
finally
|
|
// check empty DataSet
|
|
if (BkmList.Count = 1) and (Length(BkmList[0]) = 0) then BkmList.Clear;
|
|
EnableControls;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
finally
|
|
EndSelection;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetSelectionAnchor(ANode : TdxTreeListNode);
|
|
var
|
|
OldIndex : Integer;
|
|
begin
|
|
if IsGridMode and (DataLink.Active) then
|
|
begin
|
|
if (ANode <> nil) then
|
|
begin
|
|
OldIndex := DataLink.ActiveRecord;
|
|
try
|
|
DataLink.ActiveRecord := ANode.Index;
|
|
FBkmSelectionAnchor := Datalink.DataSource.DataSet.Bookmark;
|
|
inherited SetSelectionAnchor(Nil);
|
|
finally
|
|
DataLink.ActiveRecord := OldIndex;
|
|
end;
|
|
end
|
|
else FBkmSelectionAnchor := '';
|
|
end
|
|
else
|
|
begin
|
|
if IsPartialLoad and DataLink.Active then
|
|
begin
|
|
FBkmSelectionAnchor := Datalink.DataSource.DataSet.Bookmark;
|
|
// if ANode = nil then ANode := FocusedNode;
|
|
end;
|
|
inherited SetSelectionAnchor(ANode);
|
|
end;
|
|
end;
|
|
|
|
// Sorting
|
|
procedure TCustomdxDBGrid.AddSortedColumn(Column: TdxTreeListColumn);
|
|
begin
|
|
if (Column <> nil) and (Column.Sorted <> csNone) and
|
|
(TdxDBGridColumn(Column).GroupIndex = -1) then
|
|
inherited AddSortedColumn(Column);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.CanColumnSorting(Column: TdxTreeListColumn): Boolean;
|
|
begin
|
|
Result := True;
|
|
if Assigned(FOnColumnSorting) then
|
|
FOnColumnSorting(Self, Column as TdxDBTreeListColumn, Result);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.CheckSorted;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if IsGridMode or IsPartialLoad then
|
|
begin
|
|
for I := 0 to ColumnCount - 1 do
|
|
if (TdxDBGridColumn(Columns[I]).GroupIndex = -1) and (Columns[I].Sorted <> csNone) then
|
|
begin
|
|
TdxDBGridColumn(Columns[I]).FSorted := csNone;
|
|
RemoveSortedColumn(Columns[I]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoSortColumn(StartIndex, ColIndex : Integer; FlagDesc : Boolean);
|
|
begin
|
|
inherited DoSortColumn(StartIndex, ColIndex, FlagDesc);
|
|
{correct group nodes id}
|
|
CorrectIdGroupNodes;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsAutoSort: Boolean;
|
|
begin
|
|
Result := (egoAutoSort in OptionsEx) and not (IsGridMode or IsPartialLoad);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsFilterStatusVisible: Boolean;
|
|
begin
|
|
Result := (Filter.FilterStatus <> fsNone) and
|
|
((Filter.FilterStatus = fsAlways) or (FFiltering and not Filter.IsEmpty));
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsMultiSort: Boolean;
|
|
begin
|
|
Result := (egoMultiSort in OptionsEx) and not IsGridMode;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsMultiSortColumn(AbsoluteIndex: Integer): Boolean;
|
|
begin
|
|
Result := inherited IsMultiSortColumn(AbsoluteIndex) and
|
|
((TdxDBGridColumn(Columns[AbsoluteIndex]).GroupIndex = -1) or
|
|
(TdxDBGridColumn(Columns[AbsoluteIndex]).SortedOrder <> -1));
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsSummaryColumn(ColIndex : Integer) : Boolean;
|
|
begin
|
|
Result := (TdxDBGridColumn(Columns[ColIndex]).GroupIndex <> -1) and
|
|
TdxDBGridColumn(Columns[ColIndex]).SortBySummary;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsUseLocate: Boolean;
|
|
begin
|
|
Result := (egoUseLocate in Options) or (egoSmartRefresh in Options) or IsFilterMode;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetColumnSorted(Column : TdxTreeListColumn);
|
|
|
|
function ExistGroupColumn(const AFieldName: string): Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := True;
|
|
for i := 0 to GroupColumnCount - 1 do
|
|
if AnsiCompareText(GroupColumns[i].FieldName, AFieldName) = 0 then Exit;
|
|
Result := False;
|
|
end;
|
|
|
|
var
|
|
StartIndex: Integer;
|
|
Col: TdxDBGridColumn{TdxDBTreeListColumn};
|
|
begin
|
|
Col := TdxDBGridColumn(Column);
|
|
if {IsGridMode or IsPartialLoad or }(Col = nil) or (csLoading in ComponentState) or (Col.Sorted = csNone) or
|
|
{(Col.Field = nil) or }((Col.GroupIndex = -1) and ExistGroupColumn(Col.FieldName)) then Exit;
|
|
if IsMultiSort and (SortedColumn <> nil) then; // check SortedColumns[]
|
|
inherited SetColumnSorted(Column);
|
|
// if not LockSorting then
|
|
if IsAutoSort and not LockSorting then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
// sort nodes
|
|
StartIndex := Col.GroupIndex;
|
|
if StartIndex = -1 then StartIndex := GroupColumnCount;
|
|
DoSortColumn(StartIndex, Col.Index, Col.Sorted = csDown);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Grouping
|
|
procedure TCustomdxDBGrid.ChangedGroupColumn(Column: TdxDBTreeListColumn);
|
|
begin
|
|
if FFlagChangedGroupColumn or FLoaded then Exit;
|
|
FFlagChangedGroupColumn := True;
|
|
try
|
|
if (Column <> nil) and
|
|
((Column.FieldName = '') or
|
|
((Column.Field <> nil) and (Column.Field is TBlobField) and
|
|
not TdxDBGridColumn(Column).IsBlobColumn) or
|
|
((FDataLink.DataSet <> nil) and
|
|
(FDataLink.DataSet.FindField(Column.FieldName) = nil))) then
|
|
TdxDBGridColumn(Column).GroupIndex := -1
|
|
else
|
|
begin
|
|
BeginGrouping;
|
|
EndGrouping;
|
|
end;
|
|
finally
|
|
FFlagChangedGroupColumn := False;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.FindGroupNode(StartNode: TdxDBGridNode; Value : Variant; var Node : TdxDBGridNode;
|
|
FlagDesc : Boolean; StartIndex: Integer) : Boolean; {return ANode = Nil - add node, else - insert before}
|
|
|
|
{
|
|
function CompareValues (V1, V2 : Variant) : Integer;
|
|
begin
|
|
try
|
|
if V1 = V2 then Result := 0
|
|
else if V1 < V2 then Result := -1
|
|
else Result := 1;
|
|
except
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
}
|
|
|
|
function CompareValues(V1, V2: Variant): Integer;
|
|
begin
|
|
try
|
|
{
|
|
if (VarType(V1) = varString) and IsCaseInsensitive then
|
|
begin
|
|
V1 := AnsiUpperCase(V1);
|
|
V2 := AnsiUpperCase(V2);
|
|
end;
|
|
if V1 = V2 then
|
|
Result := 0
|
|
else
|
|
if (VarType(V1) = varString) and IsAnsiSort then
|
|
Result := AnsiCompareStr(V1, V2)
|
|
else
|
|
if V1 < V2 then Result := -1
|
|
else Result := 1;
|
|
}
|
|
if (VarType(V1) = varString) and (VarType(V2) = varString) and
|
|
IsCaseInsensitive then
|
|
begin
|
|
V1 := AnsiUpperCase(V1);
|
|
V2 := AnsiUpperCase(V2);
|
|
end;
|
|
if V1 = V2 then
|
|
Result := 0
|
|
else
|
|
if VarType(V1) = varNull then
|
|
Result := -1
|
|
else
|
|
if VarType(V2) = varNull then
|
|
Result := 1
|
|
else
|
|
begin
|
|
if (VarType(V1) = varString) and IsAnsiSort then
|
|
Result := AnsiCompareStr(V1, V2)
|
|
else
|
|
if V1 < V2 then Result := -1
|
|
else Result := 1;
|
|
end;
|
|
except
|
|
on EVariantError do
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
C, I, L, H, CountItems : Integer;
|
|
begin
|
|
Result := False;
|
|
Node := Nil;
|
|
if StartNode = Nil then Exit;
|
|
L := StartIndex{0};
|
|
if StartNode.Parent <> Nil then
|
|
CountItems := StartNode.Parent.Count - 1
|
|
else CountItems := Count - 1;
|
|
H := CountItems;
|
|
{ if SortBySummary begin}
|
|
if IsSummaryColumn(GroupColumns[StartNode.Level].Index) then
|
|
begin
|
|
for I := StartIndex{0} to CountItems do
|
|
begin
|
|
if StartNode.Parent <> Nil then
|
|
Node := TdxDBGridNode(StartNode.Parent[i])
|
|
else Node := TdxDBGridNode(Items[i]);
|
|
C := CompareValues(Node.GroupId, Value);
|
|
if C = 0 then
|
|
begin
|
|
Result := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
Exit;
|
|
end;
|
|
{end}
|
|
if FlagDesc then begin
|
|
L := H;
|
|
H := StartIndex{0};
|
|
end;
|
|
while ((L <= H) and (not FlagDesc)) or
|
|
((H <= L) and (H >=0) and (L >=0) and (FlagDesc)) Do
|
|
begin
|
|
I := (L + H) shr 1;
|
|
if StartNode.Parent <> Nil then
|
|
Node := TdxDBGridNode(StartNode.Parent[i])
|
|
else Node := TdxDBGridNode(Items[i]);
|
|
C := CompareValues(Node.GroupId, Value);
|
|
if C = 0 then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end
|
|
else
|
|
if not FlagDesc then
|
|
begin
|
|
if (C < 0) then L := I + 1
|
|
else H := I - 1;
|
|
end
|
|
else
|
|
begin
|
|
if (C > 0) then H := I + 1
|
|
else L := I - 1;
|
|
end;
|
|
end;
|
|
if FlagDesc then L := H;
|
|
if L > CountItems then Node := Nil
|
|
else
|
|
if StartNode.Parent <> Nil then
|
|
Node := TdxDBGridNode(StartNode.Parent[L])
|
|
else Node := TdxDBGridNode(Items[L]);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetGroupColumns(Index: Integer ): TdxDBGridColumn;
|
|
begin
|
|
Result := nil;
|
|
if FGroupColumns <> nil then
|
|
Result := FGroupColumns[Index];
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetGroupColumnsCount: Integer;
|
|
begin
|
|
Result := FGroupColumns.Count;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsSmartReload: Boolean;
|
|
begin
|
|
Result := (egoSmartReload in Options) and FlagSmartReload and not IsGridMode;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsSmartRefresh: Boolean;
|
|
begin
|
|
Result := (egoSmartRefresh in Options) and not FlagFullRefresh and
|
|
not (FDataLink.DataSet.BOF or FDataLink.DataSet.EOF) and
|
|
not ((Count = 1) and (GroupColumnCount > 0) and (not Items[0].HasChildren){insert new record in empty grid})
|
|
and not IsPartialLoad;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.MoveNodesToRoot;
|
|
|
|
procedure MoveToRoot(Node: TdxDBGridNode);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Node[0].Count = 0 then
|
|
Node.MoveChildrenToRoot
|
|
else
|
|
for i := 0 to Node.Count - 1 do
|
|
MoveToRoot(TdxDBGridNode(Node[i]));
|
|
end;
|
|
|
|
var
|
|
I: Integer;
|
|
PrevFlag: Boolean;
|
|
begin
|
|
ClearListNodes;
|
|
PrevFlag := FClearListNodesFlag;
|
|
FClearListNodesFlag := True;
|
|
try
|
|
for I := 0 to Count - 1 do
|
|
begin
|
|
MoveToRoot(TdxDBGridNode(Items[0]));
|
|
Items[0].Free;
|
|
end;
|
|
finally
|
|
FClearListNodesFlag := PrevFlag;
|
|
end;
|
|
end;
|
|
|
|
// Summary
|
|
procedure TCustomdxDBGrid.AssignSummaryFields;
|
|
|
|
procedure ClearSummaryList(Node: TdxTreeListNode);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if Node.Count > 0 then
|
|
begin
|
|
FreeMem(TdxDBGridNode(Node).FSummaryList);
|
|
TdxDBGridNode(Node).FSummaryList := nil;
|
|
for I := 0 to Node.Count - 1 do
|
|
ClearSummaryList(Node[I]);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
I, J: Integer;
|
|
SummaryItem: TdxDBGridSummaryItem;
|
|
S: string;
|
|
Column: TdxDBTreeListColumn;
|
|
begin
|
|
if Assigned(DataLink.DataSet) then
|
|
begin
|
|
for I := 0 to GroupColumnCount - 1 do
|
|
begin
|
|
GroupColumns[I].FSmrField := DataLink.DataSet.FindField(GroupColumns[I].SummaryField);
|
|
GroupColumns[I].FSummaryGroup := FindSummaryGroup(GroupColumns[I]);
|
|
end;
|
|
// summary groups
|
|
if SummaryGroups.Count > 0 then
|
|
begin
|
|
// clear nodes
|
|
for I := 0 to Self.Count - 1 do
|
|
ClearSummaryList(Items[I]);
|
|
// assign fields
|
|
for I := 0 to SummaryGroups.Count - 1 do
|
|
if SummaryGroups[I].SummaryItems <> nil then
|
|
for J := 0 to SummaryGroups[I].SummaryItems.Count - 1 do
|
|
begin
|
|
SummaryItem := SummaryGroups[I].SummaryItems[J];
|
|
S := SummaryItem.SummaryField;
|
|
if (S = '') and (SummaryItem.ColumnName <> '') then
|
|
begin
|
|
Column := ColumnByName(SummaryItem.ColumnName);
|
|
if Column <> nil then
|
|
S := Column.FieldName;
|
|
end;
|
|
SummaryItem.SmrField := DataLink.DataSet.FindField(S);
|
|
end;
|
|
end;
|
|
end;
|
|
FTotalSummaryCount := 0;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.AssignSummaryFooterFields;
|
|
begin
|
|
inherited AssignSummaryFooterFields;
|
|
FTotalSummaryCount := 0;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.CalcSummary(SmartFlag: Boolean); // Count , Average
|
|
var
|
|
I, J, K: Integer;
|
|
Col: TdxDBGridColumn;
|
|
begin
|
|
// group summary
|
|
for i := 0 to GroupColumnCount - 1 do
|
|
begin
|
|
// Simple Summary
|
|
case GroupColumns[i].SummaryType of
|
|
cstCount : for j := 0 to Count - 1 do CalcSummaryCount(Items[j], i);
|
|
cstAvg : for j := 0 to Count - 1 do CalcAverageCount(Items[j], i);
|
|
end;
|
|
// Summary Group
|
|
if GroupColumns[i].FSummaryGroup <> nil then
|
|
begin
|
|
if GroupColumns[i].FSummaryGroup.SummaryItems <> nil then
|
|
for K := 0 to GroupColumns[i].FSummaryGroup.SummaryItems.Count - 1 do
|
|
begin
|
|
case GroupColumns[i].FSummaryGroup.SummaryItems[K].SummaryType of
|
|
cstCount: for J := 0 to Count - 1 do CalcMultiSummaryCount(Items[J], I, K);
|
|
cstAvg: for J := 0 to Count - 1 do CalcMultiAverageCount(Items[J], I, K);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
// footer summary
|
|
for i := 0 to ColumnCount - 1 do
|
|
begin
|
|
Col := TdxDBGridColumn(Columns[i]);
|
|
case Col.SummaryFooterType of
|
|
cstCount : Col.FSummaryFooterValue := FTotalSummaryCount;
|
|
cstAvg : if (FTotalSummaryCount > 0) and not SmartFlag then
|
|
Col.FSummaryFooterValue := Col.FSummaryFooterValue / FTotalSummaryCount;
|
|
end;
|
|
end;
|
|
if Assigned(OnCalcSummary) then OnCalcSummary(Self);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoClearNodeData;
|
|
begin
|
|
if Assigned(FOnClearNodeData) then FOnClearNodeData(Self);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.FindSummaryGroup(Column: TdxDBTreeListColumn): TdxDBGridSummaryGroup;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := nil;
|
|
if (TdxDBGridColumn(Column).SummaryGroupName = '') and (TdxDBGridColumn(Column).SummaryType = cstNone) then
|
|
Result := SummaryGroups.GetDefaultGroup
|
|
else
|
|
for I := 0 to SummaryGroups.Count - 1 do
|
|
if AnsiCompareText(SummaryGroups[I].Name, TdxDBGridColumn(Column).SummaryGroupName) = 0 then
|
|
begin
|
|
Result := SummaryGroups[I];
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetCustomSummaryText(Node: TdxTreeListNode): String;
|
|
begin
|
|
Result := '';
|
|
if Assigned(FOnGetCustomSummaryText) then FOnGetCustomSummaryText(Self, Node, Result);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetSummaryText(ANode: TdxTreeListNode; AColumn: TdxDBTreeListColumn): string;
|
|
var
|
|
V: Extended;
|
|
DataType: TFieldType;
|
|
I, C: Integer;
|
|
IsExistGroupItem: Boolean;
|
|
begin
|
|
Result := '';
|
|
with TdxDBGridColumn(AColumn) do
|
|
begin
|
|
// Simple Summary
|
|
if SummaryType <> cstNone then
|
|
begin
|
|
V := Self.GetSummaryValue(ANode);
|
|
if (SummaryType <> cstCount) and Assigned(FSmrField) and
|
|
(FSmrField.DataType in [ftDate, ftTime, ftDateTime]) then DataType := FSmrField.DataType
|
|
else DataType := ftUnknown;
|
|
Result := Result + GetSummaryString(SummaryType, SummaryFormat, V, DataType, False) + ' ';
|
|
end;
|
|
// Summary Groups
|
|
if (FSummaryGroup <> nil) and (FSummaryGroup.SummaryItems <> nil) then
|
|
begin
|
|
C := FSummaryGroup.SummaryItems.Count;
|
|
IsExistGroupItem := False;
|
|
if C > 0 then
|
|
begin
|
|
Result := Result + FSummaryGroup.BeginSummaryText;
|
|
for I := 0 to C - 1 do
|
|
begin
|
|
with FSummaryGroup.SummaryItems[I] do
|
|
if (ColumnName = '') and (TdxDBGridNode(ANode).FSummaryList <> nil) then
|
|
begin
|
|
if IsExistGroupItem and (SummaryType <> cstNone) then
|
|
Result := Result + SummarySeparator;
|
|
V := TdxDBGridNode(ANode).FSummaryList^[I].Value;
|
|
if (SummaryType <> cstCount) and Assigned(SmrField) and
|
|
(SmrField.DataType in [ftDate, ftTime, ftDateTime]) then
|
|
DataType := SmrField.DataType
|
|
else
|
|
DataType := ftUnknown;
|
|
Result := Result + GetSummaryString(SummaryType, SummaryFormat, V, DataType, False);
|
|
if SummaryType <> cstNone then
|
|
IsExistGroupItem := True;
|
|
// if I < (C - 1) then
|
|
// Result := Result + SummarySeparator;
|
|
end;
|
|
end;
|
|
Result := Result + FSummaryGroup.EndSummaryText;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetSummaryValue(ANode: TdxTreeListNode): Extended;
|
|
begin
|
|
Result := VarAsType(TdxDBGridNode(ANode).FSummary, varDouble);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.LoadSummaryFooterValues(ADetailNode : TDxDBGridNode; List: TList);
|
|
var
|
|
SType : TdxSummaryType;
|
|
Col, ColField: TdxDBGridColumn{TdxDBTreeListColumn};
|
|
V : Extended;
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to ColumnCount - 1 do
|
|
begin
|
|
Col := TdxDBGridColumn(Columns[i]);
|
|
SType := Col.SummaryFooterType;
|
|
if (SType <> cstNone) then
|
|
begin
|
|
if ADetailNode = nil then
|
|
if Assigned(Col.FSmrFooterField) and (SType <> cstCount) then
|
|
V := Col.FSmrFooterField.AsFloat
|
|
else V := 0
|
|
else
|
|
begin
|
|
V := 0;
|
|
if (List <> nil) and (i < List.Count) then
|
|
ColField := List[i]
|
|
else ColField := TdxDBGridColumn(FindColumnByFieldName(Col.FSummaryFooterField));
|
|
if (ColField <> nil) and (SType <> cstCount) then
|
|
V := GetNodeVarData(ADetailNode, ColField)
|
|
else V := 0;
|
|
end;
|
|
if Assigned(Col.OnSummaryFooter) and Assigned(Datalink.DataSet) then
|
|
Col.OnSummaryFooter(Col{Self}, Datalink.DataSet, V);
|
|
with Col do
|
|
if not FAssignedSummaryFooter then
|
|
begin
|
|
FSummaryFooterValue := V;
|
|
FAssignedSummaryFooter := True;
|
|
end
|
|
else
|
|
case SType of
|
|
cstMin : if V < FSummaryFooterValue then FSummaryFooterValue := V;
|
|
cstMax : if V > FSummaryFooterValue then FSummaryFooterValue := V;
|
|
cstSum, cstAvg : FSummaryFooterValue := FSummaryFooterValue + V;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.LoadSummaryValues(ANode, ADetailNode: TDxDBGridNode; List: TList);
|
|
var
|
|
SType: TdxSummaryType;
|
|
Col, ColField: TdxDBGridColumn{TdxDBTreeListColumn};
|
|
V: Extended;
|
|
I, J: Integer;
|
|
// summary group
|
|
PSummaryValues: PSummaryList;
|
|
s:string;
|
|
Column:TdxDBTreeListColumn;
|
|
begin
|
|
repeat
|
|
J := ANode.Level;
|
|
Col := GroupColumns[J];
|
|
SType := Col.SummaryType;
|
|
// Simple Summary
|
|
if (SType <> cstNone) or Assigned(Self.FOnSummaryNode) then
|
|
begin
|
|
if ADetailNode = nil then
|
|
if Assigned(Col.FSmrField) and (SType <> cstCount) then
|
|
V := Col.FSmrField.AsFloat
|
|
else V := 0
|
|
else
|
|
begin
|
|
if (List <> nil) and (J < List.Count) then
|
|
ColField := List[J]
|
|
else ColField := TdxDBGridColumn(FindColumnByFieldName(Col.FSummaryField));
|
|
if (ColField <> nil) and (SType <> cstCount) then
|
|
V := GetNodeVarData(ADetailNode, ColField)
|
|
else V := 0;
|
|
end;
|
|
if Assigned(Col.OnSummary) and Assigned(Datalink.DataSet) then Col.OnSummary(Col{Self}, Datalink.DataSet, V);
|
|
if Assigned(Self.FOnSummaryNode) and Assigned(Datalink.DataSet) then Self.FOnSummaryNode(Self, ANode, Datalink.DataSet, V);
|
|
if not ANode.FAssignedSummary then
|
|
ANode.FSummary := V
|
|
else
|
|
case SType of
|
|
cstMin: if V < ANode.FSummary then ANode.FSummary := V;
|
|
cstMax: if V > ANode.FSummary then ANode.FSummary := V;
|
|
cstSum, cstAvg: ANode.FSummary := ANode.FSummary + V;
|
|
end;
|
|
ANode.FAssignedSummary := True;
|
|
end;
|
|
// Summary Group
|
|
if (Col.FSummaryGroup <> nil) and (Col.FSummaryGroup.SummaryItems <> nil) then
|
|
begin
|
|
for I := 0 to Col.FSummaryGroup.SummaryItems.Count - 1 do
|
|
begin
|
|
// get Value
|
|
if ADetailNode = nil then
|
|
if Assigned(Col.FSummaryGroup.SummaryItems[I].SmrField) and
|
|
not (Col.FSummaryGroup.SummaryItems[I].SummaryType in [cstCount, cstNone]) then
|
|
V := Col.FSummaryGroup.SummaryItems[I].SmrField.AsFloat
|
|
else V := 0
|
|
else
|
|
begin
|
|
// TODO - list fields
|
|
s := Col.FSummaryGroup.SummaryItems[I].SummaryField;
|
|
if (S = '') and (Col.FSummaryGroup.SummaryItems[I].ColumnName <> '') then
|
|
begin
|
|
Column := ColumnByName(Col.FSummaryGroup.SummaryItems[I].ColumnName);
|
|
if Column <> nil then
|
|
S := Column.FieldName;
|
|
end;
|
|
ColField := TdxDBGridColumn(FindColumnByFieldName(s));
|
|
if (ColField <> nil) and
|
|
not (Col.FSummaryGroup.SummaryItems[I].SummaryType in [cstCount, cstNone]) then
|
|
V := GetNodeVarData(ADetailNode, ColField)
|
|
else V := 0;
|
|
end;
|
|
if Assigned(Col.FSummaryGroup.SummaryItems[I].OnSummary) and Assigned(Datalink.DataSet) then
|
|
Col.FSummaryGroup.SummaryItems[I].OnSummary(Col{Self}, Datalink.DataSet, V);
|
|
// create summary list item
|
|
if ANode.FSummaryList = nil then
|
|
ANode.FSummaryList := AllocMem(Col.FSummaryGroup.SummaryItems.Count * SizeOf(TSummaryValue));
|
|
PSummaryValues := ANode.FSummaryList;
|
|
if not PSummaryValues^[I].AssignedValue then
|
|
begin
|
|
PSummaryValues^[I].Value := V;
|
|
PSummaryValues^[I].AssignedValue := True;
|
|
end
|
|
else
|
|
case Col.FSummaryGroup.SummaryItems[I].SummaryType of
|
|
cstMin: if V < PSummaryValues^[I].Value then PSummaryValues^[I].Value := V;
|
|
cstMax: if V > PSummaryValues^[I].Value then PSummaryValues^[I].Value := V;
|
|
cstSum, cstAvg: PSummaryValues^[I].Value := PSummaryValues^[I].Value + V;
|
|
end;
|
|
end;
|
|
end;
|
|
ANode := TdxDBGridNode(ANode.Parent);
|
|
until ANode = nil;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.MakeSummaryFieldList(List: TList);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
List.Clear;
|
|
for i := 0 to GroupColumnCount - 1 do
|
|
begin
|
|
List.Add(FindColumnByFieldName(GroupColumns[i].FSummaryField));
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.MakeSummaryFooterFieldList(List: TList);
|
|
var
|
|
I: Integer;
|
|
S: string;
|
|
begin
|
|
List.Clear;
|
|
for I := 0 to ColumnCount - 1 do
|
|
begin
|
|
S := Columns[I].SummaryFooterField;
|
|
if S = '' then S := Columns[I].FieldName;
|
|
List.Add(FindColumnByFieldName(S));
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ReCalcSummary(ADetailNode: TdxDBGridNode);
|
|
var
|
|
Node, ANode, CurNode: TdxDBGridNode;
|
|
Column: TdxDBTreeListColumn;
|
|
i: Integer;
|
|
V: Variant;
|
|
FldList, FooterFldList: TList;
|
|
{$IFDEF EGRID_DEBUG} t1, t2: LongInt; {$ENDIF}
|
|
|
|
procedure ResetFlagNodes(Node : TdxTreeListNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
TdxDBGridNode(Node).FAssignedSummary := False;
|
|
// multi summary
|
|
if TdxDBGridNode(Node).FSummaryList <> nil then
|
|
begin
|
|
FreeMem(TdxDBGridNode(Node).FSummaryList);
|
|
TdxDBGridNode(Node).FSummaryList := nil;
|
|
end;
|
|
if Node.Count > 0 then
|
|
if Node[0].Count > 0 then
|
|
for I := 0 to Node.Count - 1 do ResetFlagNodes(Node[I]);
|
|
end;
|
|
|
|
procedure LoadValues(Node: TdxTreeListNode);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if Node.Count > 0 then
|
|
for I := 0 to Node.Count - 1 do
|
|
LoadValues(Node[I])
|
|
else
|
|
begin
|
|
LoadSummaryValues(TdxDBGridNode(Node.Parent), TdxDBGridNode(Node), FldList);
|
|
LoadSummaryFooterValues(TdxDBGridNode(Node), FooterFldList);
|
|
Inc(FTotalSummaryCount);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if (GroupColumnCount > 0) and (Count > 0) and (Items[0].HasChildren) then
|
|
begin
|
|
{$IFDEF EGRID_DEBUG} t1 := GetTickCount; {$ENDIF}
|
|
BeginUpdate;
|
|
try
|
|
Node := nil;
|
|
if ADetailNode <> nil then
|
|
for i:= 0 to GroupColumnCount - 1 do
|
|
begin
|
|
CurNode := Node;
|
|
Column := GroupColumns[i];
|
|
if Column <> nil then
|
|
V := GetColumnVariantData(ADetailNode, Column);
|
|
|
|
if Node = nil then
|
|
Node := TdxDBGridNode(Items[0])
|
|
else Node := TdxDBGridNode(Node[0]);
|
|
|
|
if not FindGroupNode(Node, V , ANode, Column.Sorted = csDown, 0) then
|
|
begin
|
|
if ANode = nil then //add
|
|
if (i > 0) then
|
|
Node := TdxDBGridNode(CurNode.AddChild)
|
|
else
|
|
if (Node = nil) or (Node.Parent = nil) then
|
|
Node := TdxDBGridNode(Self.Add)
|
|
else Node := TdxDBGridNode(Node.Parent.AddChild)
|
|
else
|
|
if ANode.Parent <> nil then
|
|
Node := TdxDBGridNode(ANode.Parent.InsertChild(ANode))
|
|
else Node := TdxDBGridNode(Self.Insert(ANode));
|
|
Node.FGroupId := V;
|
|
Node.Id := ADetailNode.Id;
|
|
Node.VariantData[Column.Index] := ADetailNode.VariantData[Column.Index];
|
|
Node.Strings[Column.Index] := ADetailNode.Strings[Column.Index];
|
|
Node.FRecNo := ADetailNode.FRecNo;
|
|
{Assign propery Data: Pointer}
|
|
DoRefreshNodeData(Node);
|
|
end
|
|
else
|
|
Node := ANode;
|
|
end;
|
|
|
|
if ADetailNode <> nil then
|
|
begin
|
|
ANode := TdxDBGridNode(ADetailNode.Parent);
|
|
if (ANode <> nil) and (ANode <> Node) then
|
|
begin
|
|
ADetailNode.MoveTo(Node, natlAddChild);
|
|
if ANode.Count = 0 then
|
|
DeleteRecurse(ANode);
|
|
end;
|
|
end;
|
|
|
|
{ reset flag AssignValues nodes }
|
|
for i := 0 to Count - 1 do ResetFlagNodes(Items[i]);
|
|
|
|
AssignSummaryFooterFields;
|
|
// before summary
|
|
DoBeforeCalcSummary;
|
|
|
|
FTotalSummaryCount := 0;
|
|
FldList := TList.Create;
|
|
FooterFldList := TList.Create;
|
|
try
|
|
MakeSummaryFieldList(FldList);
|
|
MakeSummaryFooterFieldList(FooterFldList);
|
|
for i := 0 to Count - 1 do
|
|
LoadValues(Items[i]);
|
|
finally
|
|
FooterFldList.Free;
|
|
FldList.Free;
|
|
end;
|
|
|
|
{sorting}
|
|
RefreshSorting;
|
|
{calc summary}
|
|
CalcSummary(False);
|
|
{group sorting - if SortBySummary}
|
|
SortingBySummary;
|
|
{correct group nodes id}
|
|
CorrectIdGroupNodes;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
{$IFDEF EGRID_DEBUG}
|
|
t2 := GetTickCount;
|
|
if Assigned(OnDebugEvent) then OnDebugEvent(Self, etRefreshNode, t2 - t1);
|
|
{$ENDIF}
|
|
end
|
|
else
|
|
begin
|
|
{$IFDEF EGRID_DEBUG} t1 := GetTickCount; {$ENDIF}
|
|
BeginUpdate;
|
|
try
|
|
AssignSummaryFooterFields;
|
|
// before summary
|
|
DoBeforeCalcSummary;
|
|
{refresh Summary Footer}
|
|
FooterFldList := TList.Create;
|
|
try
|
|
MakeSummaryFooterFieldList(FooterFldList);
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
LoadSummaryFooterValues(TdxDBGridNode(Items[i]), FooterFldList);
|
|
end;
|
|
finally
|
|
FooterFldList.Free;
|
|
end;
|
|
|
|
FTotalSummaryCount := Count;
|
|
{sorting}
|
|
RefreshSorting;
|
|
{calc summary}
|
|
CalcSummary(False);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
{$IFDEF EGRID_DEBUG}
|
|
t2 := GetTickCount;
|
|
if Assigned(OnDebugEvent) then OnDebugEvent(Self, etRefreshNode, t2 - t1);
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RefreshSummaryItems(const PrevName, NewName: string);
|
|
var
|
|
I, J: Integer;
|
|
S: string;
|
|
begin
|
|
for I := 0 to SummaryGroups.Count - 1 do
|
|
if SummaryGroups[I].SummaryItems <> nil then
|
|
for J := 0 to SummaryGroups[I].SummaryItems.Count - 1 do
|
|
begin
|
|
S := SummaryGroups[I].SummaryItems[J].ColumnName;
|
|
if AnsiCompareText(S, PrevName) = 0 then
|
|
SummaryGroups[I].SummaryItems[J].ColumnName := NewName;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SortingBySummary;
|
|
var
|
|
Column: TdxDBGridColumn{TdxDBTreeListColumn};
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to GroupColumnCount - 1 do
|
|
begin
|
|
Column := GroupColumns[i];
|
|
if Column.SortBySummary and (Column.Sorted <> csNone) then // TODO multi summary
|
|
begin
|
|
DoSortColumn(Column.GroupIndex, Column.Index, Column.Sorted = csDown);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Style
|
|
function TCustomdxDBGrid.CanDblClick: Boolean;
|
|
begin
|
|
Result := egoDblClick in Options;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.EndCustomLayout;
|
|
begin
|
|
inherited EndCustomLayout;
|
|
FKeyField := nil;
|
|
if FDataLink.Active and (KeyField <> '') then
|
|
FKeyField := FDataLink.DataSet.FindField(KeyField);
|
|
UpdateActive;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsSmartRecalcRowHeight: Boolean;
|
|
begin
|
|
Result := inherited IsSmartRecalcRowHeight and not IsGridMode;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsVScrollBarDisableHide: Boolean;
|
|
begin
|
|
Result := inherited IsVScrollBarDisableHide or
|
|
(IsVScrollBarVisible and IsGridMode);
|
|
end;
|
|
|
|
//based override (TCustomDxDBGrid)
|
|
procedure TCustomdxDBGrid.ClearListNodes;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
inherited ClearListNodes;
|
|
if FSummaryAbsoluteList <> nil then
|
|
begin
|
|
for I := 0 to FSummaryAbsoluteList.Count - 1 do
|
|
if FSummaryAbsoluteList[I] <> nil then
|
|
TList(FSummaryAbsoluteList[I]).Free;
|
|
FSummaryAbsoluteList.Clear;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.CreateNode: TdxTreeListNode;
|
|
begin
|
|
Result := TdxDBGridNode.Create(Self);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DoMouseWHeelScroll(AScrollUp: Boolean; AScrollLines: Integer);
|
|
begin
|
|
if IsGridMode then
|
|
begin
|
|
if AScrollUp then
|
|
GotoPrev(False)
|
|
else
|
|
GotoNext(False);
|
|
end
|
|
else
|
|
inherited DoMouseWHeelScroll(AScrollUp, AScrollLines);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetCellAlignment(Node: TdxTreeListNode; AbsoluteIndex: Integer): TAlignment;
|
|
begin
|
|
if IsRowGroup(Node) then
|
|
Result := taLeftJustify
|
|
else Result := inherited GetCellAlignment(Node, AbsoluteIndex);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetDataLink: TdxDBTreeListControlDataLink;
|
|
begin
|
|
Result := FDataLink;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetDataSource: TDataSource;
|
|
begin
|
|
Result := FDataLink.DataSource;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetFooterCellText(Node: TdxTreeListNode; AbsoluteIndex, FooterIndex: Integer): string;
|
|
var
|
|
SummaryGroup: TdxDBGridSummaryGroup;
|
|
Column: TdxDBTreeListColumn;
|
|
SummaryItem: TdxDBGridSummaryItem;
|
|
I, J, ALevel: Integer;
|
|
V: Extended;
|
|
DataType: TFieldType;
|
|
begin
|
|
if FooterIndex = -1 then // summary footer
|
|
begin
|
|
Result := TdxDBGridColumn(Columns[AbsoluteIndex]).GetSummaryFooterText;
|
|
end
|
|
else
|
|
begin
|
|
ALevel := GetNodeFooterLevel(Node, FooterIndex);
|
|
while Node.Level <> ALevel do
|
|
Node := Node.Parent;
|
|
SummaryGroup := GroupColumns[ALevel].FSummaryGroup;
|
|
if SummaryGroup.SummaryItems <> nil then
|
|
begin
|
|
I := -1;
|
|
if (FSummaryAbsoluteList <> nil) then
|
|
begin
|
|
if FSummaryAbsoluteList.Count = 0 then
|
|
MakeListNodes;
|
|
if (FSummaryAbsoluteList[ALevel] <> nil) then
|
|
begin
|
|
I := Integer(TList(FSummaryAbsoluteList[ALevel])[AbsoluteIndex]);
|
|
end;
|
|
end
|
|
else
|
|
for J := 0 to SummaryGroup.SummaryItems.Count - 1 do
|
|
begin
|
|
SummaryItem := SummaryGroup.SummaryItems[J];
|
|
if SummaryItem.ColumnName <> '' then
|
|
begin
|
|
Column := ColumnByName(SummaryItem.ColumnName);
|
|
if (Column <> nil) and (Column.Index = AbsoluteIndex) then
|
|
begin
|
|
I := J;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
// text
|
|
if I <> -1 then
|
|
begin
|
|
SummaryItem := SummaryGroup.SummaryItems[I];
|
|
V := TdxDBGridNode(Node).FSummaryList^[I].Value;
|
|
with SummaryItem do
|
|
begin
|
|
if (SummaryType <> cstCount) and Assigned(SmrField) and
|
|
(SmrField.DataType in [ftDate, ftTime, ftDateTime]) then
|
|
DataType := SmrField.DataType
|
|
else DataType := ftUnknown;
|
|
Result := GetSummaryString(SummaryType, SummaryFormat, V, DataType, True);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
Result := '';
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetNodeString(Node: TdxTreeListNode; Column: Integer): string;
|
|
var
|
|
OldActive: Integer;
|
|
DrawColumn: TdxDBGridColumn{TdxDBTreeListColumn};
|
|
begin
|
|
if IsGridMode then
|
|
begin
|
|
Result := '';
|
|
DrawColumn := TdxDBGridColumn(Columns[Column]);
|
|
if (DrawColumn = nil) then Exit;
|
|
if not (Assigned(DataLink) and DataLink.Active) then Exit;
|
|
OldActive := FDataLink.ActiveRecord;
|
|
try
|
|
FDataLink.ActiveRecord := Node.Index;
|
|
if Assigned(DrawColumn.Field) then
|
|
begin
|
|
DrawColumn.FActualNode := Node;
|
|
try
|
|
Result := DrawColumn.GetText(DrawColumn.GetDisplayText(Node));
|
|
finally
|
|
DrawColumn.FActualNode := nil;
|
|
end;
|
|
end;
|
|
// OnGetText
|
|
if Assigned(DrawColumn.OnGetText) then
|
|
DrawColumn.OnGetText(DrawColumn, Node, Result);
|
|
finally
|
|
FDataLink.ActiveRecord := OldActive;
|
|
end;
|
|
end
|
|
else
|
|
if Node.HasChildren then
|
|
begin
|
|
DrawColumn := GroupColumns[Node.Level];
|
|
// OnGetText
|
|
if Assigned(DrawColumn.OnGetText) then
|
|
begin
|
|
Result := DrawColumn.GetGroupText(inherited GetNodeString(Node, DrawColumn.Index));
|
|
DrawColumn.OnGetText(DrawColumn, Node, Result);
|
|
Result := DrawColumn.Caption + ' : ' +
|
|
Result+ ' ' +
|
|
GetSummaryText(Node, DrawColumn) + GetCustomSummaryText(Node);
|
|
end
|
|
else
|
|
Result := DrawColumn.Caption + ' : ' +
|
|
DrawColumn.GetGroupText(inherited GetNodeString(Node, DrawColumn.Index))+ ' ' +
|
|
GetSummaryText(Node, DrawColumn) + GetCustomSummaryText(Node);
|
|
end
|
|
else
|
|
begin
|
|
Result := inherited GetNodeString(Node, Column);
|
|
DrawColumn := TdxDBGridColumn(Columns[Column]);
|
|
// TODO OnGetText
|
|
if Assigned(DrawColumn.OnGetText) then
|
|
DrawColumn.OnGetText(DrawColumn, Node, Result);
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetNodeValue(Node: TdxTreeListNode; Column: Integer): Variant;
|
|
var
|
|
OldActive: Integer;
|
|
DrawColumn: TdxDBTreeListColumn;
|
|
begin
|
|
if IsGridMode then
|
|
begin
|
|
Result := Null;
|
|
DrawColumn := Columns[Column];
|
|
if (DrawColumn = nil) then Exit;
|
|
if not (Assigned(DataLink) and DataLink.Active) then Exit;
|
|
OldActive := FDataLink.ActiveRecord;
|
|
try
|
|
FDataLink.ActiveRecord := Node.Index;
|
|
if Assigned(DrawColumn.Field) then
|
|
Result := DrawColumn.Field.Value;
|
|
finally
|
|
FDataLink.ActiveRecord := OldActive;
|
|
end;
|
|
end
|
|
else
|
|
if Node.HasChildren then
|
|
Result := TdxDBGridNode(Node).GroupId
|
|
else
|
|
begin
|
|
with TdxDBGridNode(Node) do
|
|
if nadValue in VariantData[Column].AssignedValues then
|
|
Result := VariantData[Column].Value
|
|
else Result := Node.Strings[Column];
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetNodeVariant(Node: TdxTreeListNode; Column: Integer): Variant;
|
|
var
|
|
S: string;
|
|
begin
|
|
if IsSummaryColumn(Column) then
|
|
Result := TdxDBGridNode(Node).FSummary
|
|
else
|
|
begin
|
|
if Columns[Column].VariantType = varString then
|
|
begin
|
|
S := Node.Strings[Column];
|
|
if IsCaseInsensitive then
|
|
Result := AnsiUpperCase(S)
|
|
else Result := S;
|
|
end
|
|
else
|
|
Result := TdxDBGridNode(Node).VariantData[Column].Value;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetPreviewText(Node: TdxTreeListNode): string;
|
|
var
|
|
OldActive : Integer;
|
|
begin
|
|
Result := '';
|
|
if IsGridMode then
|
|
begin
|
|
if not (Assigned(DataLink) and DataLink.Active) then Exit;
|
|
OldActive := FDataLink.ActiveRecord;
|
|
try
|
|
FDataLink.ActiveRecord := Node.Index;
|
|
if Assigned(FPreviewField) then
|
|
begin
|
|
try
|
|
if Assigned(FPreviewField.OnGetText) then
|
|
FPreviewField.OnGetText(FPreviewField, Result, True)
|
|
else
|
|
Result := GetPreviewString(FPreviewField.AsString);
|
|
except
|
|
// ignore error
|
|
end;
|
|
end;
|
|
finally
|
|
FDataLink.ActiveRecord := OldActive;
|
|
end;
|
|
end
|
|
else
|
|
if not Node.HasChildren then
|
|
Result := (Node as TdxDBGridNode).Description;
|
|
if Assigned(OnGetPreviewText) then
|
|
OnGetPreviewText(Self, Node, Result);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsCancelOnExit: Boolean;
|
|
begin
|
|
Result := egoCancelOnExit in Options;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsCanInsert: Boolean;
|
|
begin
|
|
Result := egoCanInsert in Options;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsCanNavigation: Boolean;
|
|
begin
|
|
Result := IsGridMode or (egoCanNavigation in Options);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsEasySelect: Boolean;
|
|
begin
|
|
Result := IsGridMode;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsExistRowFooterCell(Node: TdxTreeListNode; AbsoluteIndex, FooterIndex: Integer): Boolean;
|
|
var
|
|
SummaryGroup: TdxDBGridSummaryGroup;
|
|
SummaryItem: TdxDBGridSummaryItem;
|
|
Column: TdxDBTreeListColumn;
|
|
J, L: Integer;
|
|
begin
|
|
Result := False;
|
|
L := GetNodeFooterLevel(Node, FooterIndex);
|
|
SummaryGroup := GroupColumns[L].FSummaryGroup;
|
|
if SummaryGroup.SummaryItems <> nil then
|
|
begin
|
|
if (FSummaryAbsoluteList <> nil) then
|
|
begin
|
|
if FSummaryAbsoluteList.Count = 0 then
|
|
MakeListNodes;
|
|
if FSummaryAbsoluteList[L] <> nil then
|
|
Result := Integer(TList(FSummaryAbsoluteList[L])[AbsoluteIndex]) <> -1;
|
|
end
|
|
else
|
|
for J := 0 to SummaryGroup.SummaryItems.Count - 1 do
|
|
begin
|
|
SummaryItem := SummaryGroup.SummaryItems[J];
|
|
if SummaryItem.ColumnName <> '' then
|
|
begin
|
|
Column := ColumnByName(SummaryItem.ColumnName);
|
|
if (Column <> nil) and (Column.Index = AbsoluteIndex) then
|
|
begin
|
|
Result := SummaryItem.SummaryType <> cstNone;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsKeyFieldEmpty: Boolean;
|
|
begin
|
|
Result := (FKeyField = nil) and not IsGridMode;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsLevelFooter(Level: Integer): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := (Level < GroupColumnCount) and (GroupColumns[Level].FSummaryGroup <> nil);
|
|
if Result then
|
|
begin
|
|
if GroupColumns[Level].FSummaryGroup.SummaryItems <> nil then
|
|
for I := 0 to GroupColumns[Level].FSummaryGroup.SummaryItems.Count - 1 do
|
|
if GroupColumns[Level].FSummaryGroup.SummaryItems[I].ColumnName <> '' then Exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsLoadedAll: Boolean;
|
|
begin
|
|
Result := IsLoadAllRecords or (FGridBOF and FGridEOF);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsRowGroup(Node: TdxTreeListNode): Boolean;
|
|
begin
|
|
if Node <> nil then
|
|
Result := Node.HasChildren
|
|
else Result := inherited IsRowGroup(Node);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsUseBookmarks: Boolean;
|
|
begin
|
|
Result := UseBookmarks or IsGridMode;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.MakeListNodes;
|
|
var
|
|
SummaryGroup: TdxDBGridSummaryGroup;
|
|
SummaryItem: TdxDBGridSummaryItem;
|
|
Column: TdxDBTreeListColumn;
|
|
I, J, K, C, G, Index: Integer;
|
|
List: TList;
|
|
begin
|
|
inherited MakeListNodes;
|
|
if FSummaryAbsoluteList <> nil then
|
|
begin
|
|
for I := 0 to FSummaryAbsoluteList.Count - 1 do
|
|
if FSummaryAbsoluteList[I] <> nil then
|
|
TList(FSummaryAbsoluteList[I]).Free;
|
|
FSummaryAbsoluteList.Clear;
|
|
G := GroupColumnCount;
|
|
FSummaryAbsoluteList.Capacity := G;
|
|
for I := 0 to G - 1 do
|
|
begin
|
|
SummaryGroup := GroupColumns[I].FSummaryGroup;
|
|
if (SummaryGroup <> nil) and (SummaryGroup.SummaryItems <> nil) then
|
|
begin
|
|
C := ColumnCount;
|
|
List := TList.Create;
|
|
List.Capacity := C;
|
|
for K := 0 to C - 1 do
|
|
begin
|
|
Index := -1;
|
|
for J := 0 to SummaryGroup.SummaryItems.Count - 1 do
|
|
begin
|
|
SummaryItem := SummaryGroup.SummaryItems[J];
|
|
if SummaryItem.ColumnName <> '' then
|
|
begin
|
|
Column := ColumnByName(SummaryItem.ColumnName);
|
|
if (Column <> nil) and (Column.Index = K) then
|
|
begin
|
|
Index := J;
|
|
if SummaryItem.SummaryType = cstNone then Index := -1;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
List.Add(Pointer(Index));
|
|
end;
|
|
end
|
|
else
|
|
List := nil;
|
|
FSummaryAbsoluteList.Add(Pointer(List));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RemoveColumn(Column: TdxTreeListColumn);
|
|
begin
|
|
if FGroupColumns <> nil then
|
|
FGroupColumns.Remove(Column);
|
|
if Assigned(FFilterPopupListBox) and (FFilterPopupListBox.Column = Column) then
|
|
FFilterPopupListBox.Column := nil;
|
|
inherited RemoveColumn(Column);
|
|
if FFilter <> nil then
|
|
FFilter.Remove(TdxDBTreeListColumn(Column));
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ResetAutoHeaderPanelRowCountOption;
|
|
begin
|
|
inherited ResetAutoHeaderPanelRowCountOption;
|
|
FOptionsEx := FOptionsEx - [egoAutoHeaderPanelHeight];
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetDataChangedBusy(Value: Boolean);
|
|
begin
|
|
DataChangedBusy := Value;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetFocusedNode(Node: TdxTreeListNode; Column: Integer; MakeVisibleFlag: Boolean);
|
|
var
|
|
Flag: Boolean;
|
|
begin
|
|
if Node = nil then Exit;
|
|
AddNodeLink(Node);
|
|
if not FInUpdateActive then
|
|
begin
|
|
inherited SetFocusedNode(FocusedNode, Column, False);
|
|
if IsGridMode then
|
|
begin
|
|
FocusedNumber := Node.Index;
|
|
Node := Items[FocusedNumber];
|
|
end
|
|
else
|
|
if not FRefreshGroupList then
|
|
begin
|
|
if IsUseLocate then
|
|
if not LocateByNode(TdxDBgridNode(FocusedNode), TdxDBgridNode(Node),
|
|
TdxDBGridNode(Node).Id, True) then Exit
|
|
else
|
|
else
|
|
begin
|
|
// Flag := Datalink.Dataset.State = dsInsert;
|
|
Flag := Datalink.Dataset.State in dsEditModes; // TODO new
|
|
if not LocateByNode(TdxDBgridNode(FocusedNode), TdxDBgridNode(Node), Null, False) then Exit;
|
|
if Flag then
|
|
if not ValidateNode(Node) then Exit
|
|
else
|
|
if TdxDBgridNode(Node).Id <> FKeyField.Value then
|
|
begin
|
|
if NodeLinkList <> nil then NodeLinkList.Remove(Node);
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
if not ValidateNode(Node) then Exit
|
|
else if NodeLinkList <> nil then {FNodeLink.Remove(Node)}; {WARNING}
|
|
inherited SetFocusedNode(Node, Column, MakeVisibleFlag);
|
|
//Save old focused column position
|
|
if FOldFocusedColumn = -1 then FOldFocusedColumn := 0;
|
|
if (FocusedNode <> nil) and not FocusedNode.HasChildren then
|
|
FOldFocusedColumn := FocusedColumn;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetFocusedNumber(AIndex: Integer);
|
|
var
|
|
FSelIndex, FCount: Integer;
|
|
begin
|
|
if Count = 0 then Exit;
|
|
if IsGridMode then
|
|
begin
|
|
if (Count = 0) or not FDatalink.Active then Exit;
|
|
if (AIndex-FocusedNumber) <> 0 then
|
|
begin
|
|
FCount := FocusedNumber;
|
|
FDatalink.DataSet.MoveBy(AIndex - FCount);
|
|
end;
|
|
end
|
|
else
|
|
//inherited SetFocusedNumber(AIndex);
|
|
begin
|
|
FSelIndex := FocusedNumber;
|
|
if FSelIndex <> AIndex then
|
|
begin
|
|
if (AIndex < 0) then AIndex := 0;
|
|
FCount := GetAbsoluteCount;
|
|
if AIndex > (FCount-1) then AIndex := FCount-1;
|
|
if (AIndex <> FSelIndex) then
|
|
begin
|
|
FSelIndex := FocusedColumn;
|
|
if FOldFocusedColumn > 0 then
|
|
FSelIndex := FOldFocusedColumn;
|
|
SetFocusedNode(GetAbsoluteNode(AIndex), FSelIndex, True);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetTopVisibleNode(Node: TdxTreeListNode);
|
|
var
|
|
AMinBufferCount, ACount: Integer;
|
|
begin
|
|
inherited SetTopVisibleNode(Node);
|
|
if (Node <> nil) and (FTopVisibleUpdate = 0) and
|
|
IsPartialLoad and HandleAllocated then
|
|
begin
|
|
AMinBufferCount := GetMinBufferCount;
|
|
ACount := GetAbsoluteCount;
|
|
// Check Range
|
|
if not FGridBOF and (Node.Index <= AMinBufferCount) then
|
|
GetNextNodes(lmPrior, rmNone, False)
|
|
else
|
|
if not FGridEOF and (Node.Index >= (ACount - AMinBufferCount*2)) then
|
|
GetNextNodes(lmNext, rmNone, False);
|
|
end;
|
|
end;
|
|
|
|
// based
|
|
procedure TCustomdxDBGrid.CorrectIdGroupNodes;
|
|
|
|
procedure SetId(Node : TdxDBGridNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Node.Count > 0 then
|
|
begin
|
|
if Node[0].Count > 0 then
|
|
for I := 0 to Node.Count - 1 do SetId(TdxDBGridNode(Node[I]));
|
|
Node.Id := TdxDBGridNode(Node[0]).Id;
|
|
Node.FRecNo := TdxDBGridNode(Node[0]).FRecNo;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if GroupColumnCount > 0 then
|
|
for i := 0 to Count - 1 do
|
|
SetId(TdxDBGridNode(Items[i]));
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetDefaultFields: Boolean;
|
|
begin
|
|
Result := FDefaultFields;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetMinBufferCount: Integer;
|
|
var
|
|
DrawInfo: TdxGridDrawInfo;
|
|
begin
|
|
CalcRectInfo(DrawInfo);
|
|
with DrawInfo.CellsRect do
|
|
Result := (Bottom - Top) div RowHeight + 1;
|
|
end;
|
|
|
|
type TDummyDataSet = class(TDataSet);
|
|
|
|
procedure TCustomdxDBGrid.GetNextNodes(Mode: TdxDBGridLoadMode; ResyncMode: TdxDBGridResyncMode; SelectMode: Boolean);
|
|
const
|
|
BufferPageCount = 5;
|
|
var
|
|
ABufferCount: Integer;
|
|
ASavePos: TBookmarkStr;
|
|
ASaveBOF, ASaveEOF: Boolean;
|
|
ASaveActiveRecord: Integer;
|
|
RecNumber, ARecRangeNumber: Integer;
|
|
Node: TdxDBGridNode;
|
|
APrevNodeOffset: Integer;
|
|
AStartBookmark, AEndBookmark: TBookmarkStr;
|
|
ASetStartBookmark, ASelEndBookmark: TBookmarkStr;
|
|
|
|
procedure AssignNode(ABackward: Boolean);
|
|
var
|
|
B: TBookmarkStr;
|
|
begin
|
|
Node.FGroupId := Null;
|
|
// Load Values
|
|
AssignNodeAllValues(Node);
|
|
Node.FRecNo := RecNumber;
|
|
B := DataLink.DataSet.Bookmark;
|
|
// restore selected (bookmars)
|
|
if FSaveBkmList.Count > 0 then
|
|
RestoreBookmark(Node, B, ABackward);
|
|
// select if SelectNodes
|
|
if SelectMode then
|
|
begin
|
|
if Mode = lmAllRecords then
|
|
begin
|
|
if CanNodeSelected(Node) then
|
|
BkmList.AddObject(B, Node);
|
|
end
|
|
else
|
|
begin
|
|
if (CompareBkm(ASetStartBookmark, B) <= 0) and
|
|
(CompareBkm(B, ASelEndBookmark) <= 0) and
|
|
CanNodeSelected(Node) then
|
|
begin
|
|
if ABackward then
|
|
BkmList.InsertObject(0, B, Node)
|
|
else BkmList.AddObject(B, Node);
|
|
end;
|
|
end;
|
|
end;
|
|
if CompareBkm(B, FBkmSelectionAnchor) = 0 then
|
|
SelectionAnchor := Node;
|
|
// Assign propery Data: Pointer
|
|
DoRefreshNodeData(Node);
|
|
end;
|
|
|
|
begin
|
|
// get records
|
|
if (DataLink.DataSet <> nil) and (DataLink.DataSet.Active) then
|
|
with DataLink.DataSet do
|
|
begin
|
|
DataChangedBusy := True;
|
|
Inc(FTopVisibleUpdate);
|
|
try
|
|
// reset FKeyField
|
|
FKeyField := nil;
|
|
if FDataLink.Active and (KeyField <> '') then
|
|
FKeyField := FDataLink.DataSet.FindField(KeyField);
|
|
// calc min BufferCount
|
|
ABufferCount := GetMinBufferCount * BufferPageCount;
|
|
if PartialLoadBufferCount > ABufferCount then
|
|
ABufferCount := PartialLoadBufferCount;
|
|
BeginUpdate;
|
|
// DateSet
|
|
try
|
|
DisableControls;
|
|
if Active and (State in dsEditModes) then
|
|
Cancel;
|
|
ASavePos := Bookmark;
|
|
ASaveBOF := BOF;
|
|
ASaveEOF := EOF;
|
|
ASaveActiveRecord := TDummyDataSet(DataLink.DataSet).ActiveRecord;
|
|
if Datalink.BufferCount <> 1 then Datalink.BufferCount := 1;
|
|
try
|
|
// save pos
|
|
if State in dsEditModes then Cancel;
|
|
// read data
|
|
case Mode of
|
|
// DataChanged
|
|
lmCurrent, lmAllRecords: begin
|
|
// save Top offset
|
|
if (FocusedNode <> nil) and (TopVisibleNode <> nil) then
|
|
APrevNodeOffset := FocusedNode.AbsoluteIndex - TopVisibleNode.AbsoluteIndex
|
|
else APrevNodeOffset := 0;
|
|
ClearNodes; // check empty
|
|
// load records
|
|
if BOF and EOF then {Empty DataSet}
|
|
begin
|
|
FGridBOF := True;
|
|
FGridEOF := True;
|
|
end
|
|
else
|
|
if Mode = lmAllRecords then
|
|
begin
|
|
FSaveBkmList.Clear;
|
|
RecNumber := 0;
|
|
First;
|
|
while not EOF do
|
|
begin
|
|
Node := TdxDBGridNode(Self.Add);
|
|
AssignNode(False);
|
|
Inc(RecNumber);
|
|
Next;
|
|
end;
|
|
FGridBOF := True;
|
|
FGridEOF := True;
|
|
end
|
|
else
|
|
begin
|
|
// Calc Ranges
|
|
if FSaveBkmList.Count > 0 then
|
|
begin
|
|
AStartBookmark := FSaveBkmList[0];
|
|
AEndBookmark := FSaveBkmList[FSaveBkmList.Count - 1];
|
|
if CompareBkm(ASavePos, AStartBookmark) < 0 then
|
|
AStartBookmark := ASavePos;
|
|
if CompareBkm(ASavePos, AStartBookmark) > 0 then
|
|
AEndBookmark := ASavePos;
|
|
end
|
|
else
|
|
begin
|
|
AStartBookmark := '';
|
|
AEndBookmark := AStartBookmark;
|
|
end;
|
|
// Calc Sel Ranges
|
|
if SelectMode then // SelectNodes
|
|
begin
|
|
ASetStartBookmark := FBkmSelectionAnchor;
|
|
ASelEndBookmark := Bookmark;
|
|
if CompareBkm(ASetStartBookmark, ASelEndBookmark) > 0 then
|
|
begin
|
|
ASetStartBookmark := ASelEndBookmark;
|
|
ASelEndBookmark := FBkmSelectionAnchor;
|
|
end;
|
|
// Correct Ranges
|
|
if AStartBookmark = '' then AStartBookmark := ASetStartBookmark;
|
|
if AEndBookmark = '' then AEndBookmark := ASelEndBookmark;
|
|
if CompareBkm(ASetStartBookmark, AStartBookmark) < 0 then
|
|
AStartBookmark := ASetStartBookmark;
|
|
if CompareBkm(AEndBookmark, ASelEndBookmark) < 0 then
|
|
AEndBookmark := ASelEndBookmark;
|
|
end
|
|
else
|
|
begin
|
|
ASetStartBookmark := '';
|
|
ASelEndBookmark := '';
|
|
end;
|
|
//
|
|
ARecRangeNumber := ABufferCount div 2;
|
|
RecNumber := ARecRangeNumber - 1;
|
|
// go top
|
|
while (RecNumber >= 0) or
|
|
((AStartBookmark <> '') and (CompareBkm(Bookmark, AStartBookmark) >= 0)) do
|
|
begin
|
|
Node := TdxDBGridNode(Self.Insert(Items[0]));
|
|
AssignNode(True);
|
|
Prior;
|
|
if not BOF then Dec(RecNumber)
|
|
else Break;
|
|
end;
|
|
FGridBOF := BOF;
|
|
// restore start pos
|
|
MoveBy(ARecRangeNumber - RecNumber);
|
|
RecNumber := ARecRangeNumber;
|
|
ARecRangeNumber := RecNumber + (ABufferCount - Self.Count);
|
|
if ARecRangeNumber < ABufferCount then
|
|
ARecRangeNumber := ABufferCount;
|
|
// go bottom
|
|
while not EOF and ((RecNumber < ARecRangeNumber) or
|
|
((AEndBookmark <> '') and (CompareBkm(Bookmark, AEndBookmark) <= 0))) do
|
|
begin
|
|
if (AEndBookmark <> '') and (RecNumber >= ARecRangeNumber) and
|
|
(CompareBkm(Bookmark, AEndBookmark) = 0) then
|
|
ARecRangeNumber := RecNumber + ABufferCount div 2;
|
|
Node := TdxDBGridNode(Self.Add);
|
|
AssignNode(False);
|
|
Inc(RecNumber);
|
|
Next;
|
|
end;
|
|
FGridEOF := EOF;
|
|
// remove bookmarks without clear nodes
|
|
if IsUseBookmarks then
|
|
RemoveDuplicateBookmarks(False);
|
|
end;
|
|
// correct TopIndex
|
|
if ResyncMode = rmTop then
|
|
APrevNodeOffset := 0
|
|
else
|
|
if ResyncMode = rmBottom then
|
|
APrevNodeOffset := TopIndex - RowCount;
|
|
// restore Top Offset
|
|
TopIndex := TopIndex - APrevNodeOffset;
|
|
end;
|
|
// Scroll Up
|
|
lmPrior: begin
|
|
if Self.Count > 0 then
|
|
begin
|
|
RecNumber := TdxDBGridNode(TopNode).RecNo - 1;
|
|
ARecRangeNumber := TdxDBGridNode(FocusedNode).RecNo;
|
|
MoveBy(RecNumber - ARecRangeNumber);
|
|
end
|
|
else RecNumber := 0;
|
|
ARecRangeNumber := RecNumber - ABufferCount;
|
|
// go top
|
|
while not BOF and (RecNumber > ARecRangeNumber) do
|
|
begin
|
|
Node := TdxDBGridNode(Self.Insert(Items[0]));
|
|
AssignNode(True);
|
|
Prior;
|
|
Dec(RecNumber);
|
|
end;
|
|
FGridBOF := BOF;
|
|
end;
|
|
// Scroll Down
|
|
lmNext: begin
|
|
if Self.Count > 0 then
|
|
begin
|
|
RecNumber := TdxDBGridNode(FocusedNode).RecNo;
|
|
ARecRangeNumber := TdxDBGridNode(LastNode).RecNo + 1;
|
|
MoveBy(ARecRangeNumber - RecNumber);
|
|
RecNumber := ARecRangeNumber;
|
|
end
|
|
else RecNumber := 0;
|
|
ARecRangeNumber := RecNumber + ABufferCount;
|
|
// go bottom
|
|
while not EOF and (RecNumber < ARecRangeNumber) do
|
|
begin
|
|
Node := TdxDBGridNode(Self.Add);
|
|
AssignNode(False);
|
|
Inc(RecNumber);
|
|
Next;
|
|
end;
|
|
FGridEOF := EOF;
|
|
end;
|
|
end;
|
|
finally
|
|
// restore pos
|
|
with TDummyDataSet(Datalink.DataSet) do
|
|
begin
|
|
if (Length(ASavePos) > 0) and BookmarkValid(TBookmark(ASavePos)) then
|
|
Bookmark := ASavePos;
|
|
if ActiveRecord > ASaveActiveRecord then
|
|
begin
|
|
MoveBy(BufferCount - ActiveRecord - 1 + ActiveRecord - ASaveActiveRecord);
|
|
MoveBy(ASaveActiveRecord - BufferCount + 1);
|
|
end
|
|
else
|
|
if ActiveRecord < ASaveActiveRecord then
|
|
begin
|
|
MoveBy(-ActiveRecord + ActiveRecord - ASaveActiveRecord);
|
|
MoveBy(ASaveActiveRecord);
|
|
end;
|
|
if ASaveBOF and not BOF then Prior;
|
|
if ASaveEOF and not EOF then Next;
|
|
EnableControls;
|
|
end;
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
finally
|
|
Dec(FTopVisibleUpdate);
|
|
DataChangedBusy := False;
|
|
if Mode = lmAllRecords then UpdateActive;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.FindNodeById;
|
|
|
|
function IsNodeIdEqual(ANode: TdxDBGridNode; const AID: Variant): Boolean;
|
|
begin
|
|
Result := not VarIsEmpty(ANode.Id) and (ANode.Id = AID);
|
|
end;
|
|
|
|
procedure GotoNode(Node: TdxTreeListNode);
|
|
begin
|
|
if egoSeekDetail in Options then
|
|
while Node.Count > 0 do Node := Node[0];
|
|
Node.MakeVisible;
|
|
Node.Focused := True;
|
|
end;
|
|
|
|
var
|
|
Id, V: Variant;
|
|
I: Integer;
|
|
Column: TdxDBTreeListColumn;
|
|
Node, ANode: TdxDBGridNode;
|
|
begin
|
|
if (FKeyField = nil) or not (egoCanNavigation in Options) then Exit;
|
|
if DataChangedBusy then Exit;
|
|
Id := FKeyField.Value;
|
|
Node := TdxDBGridNode(Items[0]);
|
|
if Node = nil then Exit;
|
|
if (GroupColumnCount > 0) then
|
|
begin
|
|
for i := 0 to GroupColumnCount - 1 do
|
|
begin
|
|
Column := GroupColumns[i];
|
|
V := Column.Field.Value;
|
|
if not FindGroupNode(Node, V , ANode, Column.Sorted = csDown, 0) then Exit;
|
|
if (ANode = nil) or (ANode.Count = 0) then Exit;
|
|
Node := TdxDBGridNode(ANode[0]);
|
|
end;
|
|
if (ANode <> Nil) then
|
|
begin
|
|
if IsNodeIdEqual(ANode, Id) and IsNodeIdEqual(ANode[0] as TdxDBGridNode, Id) then
|
|
begin
|
|
if IsNodeIdEqual(ANode[0] as TdxDBGridNode, Id) and ANode[0].Focused then
|
|
GotoNode(ANode[0])
|
|
else
|
|
begin
|
|
if DataChangedBusy or FInUpdateActive then
|
|
begin
|
|
while not ANode.IsVisible and (ANode.Parent <> Nil) and
|
|
(TdxDBGridNode(ANode.Parent).Id = Id) do ANode := TdxDBGridNode(ANode.Parent);
|
|
end;
|
|
GotoNode(ANode);
|
|
end;
|
|
end
|
|
else
|
|
for i := 0 to ANode.Count - 1 do
|
|
try
|
|
if IsNodeIdEqual(ANode[I] as TdxDBGridNode, Id) then
|
|
begin
|
|
GotoNode(ANode[i]);
|
|
Exit;
|
|
end;
|
|
except end;
|
|
end;
|
|
end
|
|
else {for LoadAllRecords options}
|
|
begin
|
|
for i := 0 to Self.Count - 1 do
|
|
try
|
|
if IsNodeIdEqual(Items[I] as TdxDBGridNode, Id) then
|
|
begin
|
|
GotoNode(Items[i]);
|
|
Exit;
|
|
end;
|
|
except end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.LoadGroupList(FNodes: TList);
|
|
|
|
function FindNodeId(var Node: TdxDBGridNode; const Value: Variant): Boolean;
|
|
var
|
|
L, H, I: Integer;
|
|
begin
|
|
Result := False;
|
|
L := 0;
|
|
H := FNodes.Count - 1;
|
|
while L <= H do
|
|
begin
|
|
I := (L + H) shr 1;
|
|
if (TdxDBGridNode(FNodes[I]).Id = Value) then
|
|
begin
|
|
if (TdxDBGridNode(FNodes[I]).Parent = Node) then
|
|
begin
|
|
Node := TdxDBGridNode(FNodes[I]);
|
|
FNodes.Delete(I);
|
|
Result := True;
|
|
end;
|
|
Break;
|
|
end
|
|
else
|
|
{$IFNDEF DELPHI6}
|
|
if (TdxDBGridNode(FNodes[I]).Id < Value) then L := I + 1
|
|
else H := I - 1;
|
|
{$ELSE}
|
|
if VarCompare(TdxDBGridNode(FNodes[I]).Id, Value) < 0 then
|
|
L := I + 1
|
|
else
|
|
H := I - 1;
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
function FindNullGroupField: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
for i:= 0 to GroupColumnCount - 1 do
|
|
if not Assigned(GroupColumns[i].Field) or
|
|
(GroupColumns[i].Field is TBlobField) then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
procedure LoadBookmark(Node: TdxDBGridNode);
|
|
var
|
|
B: TBookmarkStr;
|
|
Index: Integer;
|
|
begin
|
|
B := DataLink.DataSet.Bookmark;
|
|
if (Length(B) > 0) and
|
|
((Node.Count = 0) or (BkmList.IndexOfObject(Node) = -1)) and
|
|
CanNodeSelected(Node) then
|
|
begin
|
|
FindBkm(B, Index);
|
|
BkmList.InsertObject(Index, B, Node);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
Current : TBookMarkStr;
|
|
i : Integer;
|
|
Column : TdxDBTreeListColumn;
|
|
Node, ANode, CurNode, ATopNode : TdxDBGridNode;
|
|
V : Variant;
|
|
OldCursor : TCursor;
|
|
RecNumber, FilteredRecNumber: Integer;
|
|
VNode: TdxDbGridNode;
|
|
k, C, StartIndex: Integer;
|
|
ASmartReload: Boolean;
|
|
FldList, FooterFldList: TList;
|
|
ABOF, AEOF: Boolean;
|
|
{$IFDEF EGRID_DEBUG} t1, t2: LongInt; {$ENDIF}
|
|
|
|
function IsNodeFilterVisible(ATestNode: TdxDBGridNode): Boolean;
|
|
begin
|
|
Result := NodeFilterTestVisible(ATestNode);
|
|
if Result then
|
|
Inc(FilteredRecNumber);
|
|
end;
|
|
|
|
begin
|
|
if (csLoading in ComponentState) then Exit;
|
|
if IsPartialLoad then
|
|
begin
|
|
AssignPreviewField;
|
|
GetNextNodes(lmCurrent, rmNone, False);
|
|
Exit;
|
|
end;
|
|
{$IFDEF EGRID_DEBUG} t1 := GetTickCount; {$ENDIF}
|
|
DataChangedBusy := True;
|
|
try
|
|
FKeyField := Nil;
|
|
if FDataLink.Active and (KeyField <> '') then
|
|
FKeyField := FDataLink.DataSet.FindField(KeyField);
|
|
if not Assigned(FKeyField) and (egoCanNavigation in Options) then Exit;
|
|
|
|
Inc(FLockGrouping);
|
|
try
|
|
if FDataLink.Active and DataLink.DataSet.Active then
|
|
for I := GroupColumnCount-1 downto 0 do
|
|
with GroupColumns[I] do
|
|
if Field = nil then DeleteGroupColumn(I);
|
|
finally
|
|
Dec(FLockGrouping);
|
|
end;
|
|
|
|
if (not IsGridMode) and not FindNullGroupField and
|
|
(DataLink.DataSet <> nil) and (DataLink.DataSet.Active) then
|
|
with DataLink.DataSet do
|
|
begin
|
|
SelectionAnchor := nil;
|
|
ASmartReload := IsSmartReload;
|
|
AssignPreviewField;
|
|
AssignSummaryFields;
|
|
if not ASmartReload then AssignSummaryFooterFields; {***}
|
|
{save top visible position}
|
|
ATopNode := nil;
|
|
if VarType(FOldTopVisibleId) = varEmpty then
|
|
begin
|
|
FOldTopVisibleId := Null;
|
|
FOldTopVisibleLevel := 0;
|
|
end;
|
|
|
|
if not ASmartReload then {***}
|
|
begin
|
|
{save DataSet Position}
|
|
// Current := Bookmark;
|
|
DisableControls;
|
|
if DataLink.Active and
|
|
(DataLink.DataSet.State in dsEditModes) then
|
|
DataLink.DataSet.Cancel;
|
|
Current := Bookmark;
|
|
end;
|
|
OldCursor := Screen.Cursor;
|
|
if IsShowHourGlass then
|
|
Screen.Cursor := crHourglass; { Show hourglass cursor }
|
|
FldList := TList.Create;
|
|
try
|
|
MakeSummaryFieldList(FldList);
|
|
ABOF := BOF;
|
|
AEOF := EOF;
|
|
if not ASmartReload then
|
|
First;
|
|
RecNumber := 0;
|
|
FilteredRecNumber := 0;
|
|
// new - summary
|
|
DoClearNodeData;
|
|
DoBeforeCalcSummary;
|
|
if IsFilterMode then PrepareFilter;
|
|
if not ASmartReload then // normal
|
|
begin
|
|
FooterFldList := TList.Create;
|
|
try
|
|
MakeSummaryFooterFieldList(FooterFldList);
|
|
while not EOF do
|
|
begin
|
|
Node := nil;
|
|
if (GroupColumnCount <> 0) then
|
|
begin
|
|
for i:= 0 to GroupColumnCount - 1 do
|
|
begin
|
|
CurNode := Node;
|
|
Column := GroupColumns[i];
|
|
V := Column.Field.Value;
|
|
if Node = nil then
|
|
Node := TdxDBGridNode(Items[0])
|
|
else Node := TdxDBGridNode(Node[0]);
|
|
if not FindGroupNode(Node, V , ANode, Column.Sorted = csDown, 0) then
|
|
begin
|
|
if ANode = nil then //add
|
|
if (i > 0) then
|
|
Node := TdxDBGridNode(CurNode.AddChild)
|
|
else
|
|
if (Node = nil) or (Node.Parent = nil) then
|
|
Node := TdxDBGridNode(Self.Add)
|
|
else Node := TdxDBGridNode(Node.Parent.AddChild)
|
|
else
|
|
if ANode.Parent <> Nil then Node := TdxDBGridNode(ANode.Parent.InsertChild(ANode))
|
|
else Node := TdxDBGridNode(Self.Insert(ANode));
|
|
Node.FGroupId := V;
|
|
if Assigned(FKeyField) then Node.Id := FKeyField.Value;
|
|
// assign values
|
|
AssignNodeValues(TdxDBGridNode(Node), Column);
|
|
Node.FRecNo := RecNumber;
|
|
{Assign propery Data: Pointer}
|
|
DoRefreshNodeData(Node);
|
|
end
|
|
else
|
|
Node := ANode;
|
|
// select all
|
|
if FSelectAllFlag then LoadBookmark(Node);
|
|
end;
|
|
{Add (or Find) Child Node }
|
|
if (FNodes = nil) or (not Assigned(FKeyField)) or
|
|
(not FindNodeId(TdxDBGridNode(Node), FKeyField.Value)) then
|
|
Node := TdxDBGridNode(Node.AddChild);
|
|
end
|
|
else
|
|
begin
|
|
V := Null;
|
|
Node := TdxDBGridNode(Self.Add);
|
|
Node.FGroupId := V;
|
|
{restore selected (bookmars) - new}
|
|
if IsUseBookmarks then
|
|
RestoreBookmark(Node, Bookmark, False);
|
|
end;
|
|
{Load Values}
|
|
AssignNodeAllValues(Node);
|
|
Node.FRecNo := RecNumber;
|
|
{Assign propery Data: Pointer}
|
|
DoRefreshNodeData(Node);
|
|
// Check Filter
|
|
if IsNodeFilterVisible(Node) then
|
|
begin
|
|
// select all
|
|
if FSelectAllFlag then LoadBookmark(Node)
|
|
else
|
|
if not IsUseBookmarks then RestoreSelected(Node);
|
|
// Load summary values - send parent node method
|
|
if Node.Parent <> nil then
|
|
LoadSummaryValues(TdxDBGridNode(Node.Parent), nil, FldList);
|
|
// Load Summary Footer for current record
|
|
LoadSummaryFooterValues(nil, FooterFldList);
|
|
if FOldTopVisibleId = Node.Id then
|
|
begin
|
|
ATopNode := Node;
|
|
while (ATopNode.Parent <> nil) and
|
|
(FOldTopVisibleLevel < ATopNode.Level) do
|
|
ATopNode := TdxDBGridNode(ATopNode.Parent);
|
|
end;
|
|
end;
|
|
Next;
|
|
Inc(RecNumber);
|
|
end;
|
|
if TopVisibleNode = nil then // Filter
|
|
ResetTopFocusedNodes;
|
|
finally
|
|
FooterFldList.Free;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if (Items[0] <> nil) and (Items[0].Count > 0) then
|
|
MoveNodesToRoot;
|
|
RecNumber := Count;
|
|
if (GroupColumnCount <> 0) then
|
|
begin
|
|
C := Count;
|
|
for k := 0 to Count - 1 do
|
|
begin
|
|
VNode := TdxDBGridNode(Items[0]);
|
|
// CHeck Filter if ... visible then (filter)
|
|
// Check Filter
|
|
if IsNodeFilterVisible(VNode) then
|
|
begin
|
|
Node := nil;
|
|
for I := 0 to GroupColumnCount - 1 do
|
|
begin
|
|
CurNode := Node;
|
|
Column := GroupColumns[I];
|
|
if Column <> nil then
|
|
V := GetColumnVariantData(VNode, Column);
|
|
if Node = Nil then
|
|
begin
|
|
Node := TdxDBGridNode(Items[0]);
|
|
StartIndex := C - k;
|
|
end
|
|
else
|
|
begin
|
|
Node := TdxDBGridNode(Node[0]);
|
|
StartIndex := 0;
|
|
end;
|
|
if not FindGroupNode(Node, V , ANode, Column.Sorted = csDown, StartIndex) then
|
|
begin
|
|
if ANode = Nil then //add
|
|
if (i > 0) then
|
|
Node := TdxDBGridNode(CurNode.AddChild)
|
|
else
|
|
if (Node = Nil) or (Node.Parent = Nil) then
|
|
Node := TdxDBGridNode(Self.Add)
|
|
else Node := TdxDBGridNode(Node.Parent.AddChild)
|
|
else
|
|
if ANode.Parent <> Nil then Node := TdxDBGridNode(ANode.Parent.InsertChild(ANode))
|
|
else Node := TdxDBGridNode(Self.Insert(ANode));
|
|
Node.FGroupId := V;
|
|
Node.Id := VNode.Id;
|
|
Node.VariantData[Column.Index] := VNode.VariantData[Column.Index];
|
|
Node.Strings[Column.Index] := VNode.Strings[Column.Index];
|
|
Node.FRecNo := VNode.FRecNo;
|
|
{Assign propery Data: Pointer}
|
|
DoRefreshNodeData(Node);
|
|
end
|
|
else
|
|
Node := ANode;
|
|
end;
|
|
{Add (or Find) Child Node }
|
|
VNode.InternalMoveAsChild(Node, 0);
|
|
{Load summary values - send parent node method}
|
|
if VNode.Parent <> nil then
|
|
LoadSummaryValues(TdxDBGridNode(VNode.Parent), VNode, FldList);
|
|
end;
|
|
end;
|
|
ResetTopFocusedNodes;
|
|
end
|
|
else
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
begin
|
|
Node := TdxDBGridNode(Items[I]);
|
|
Node.FGroupId := Null;
|
|
{restore selected (bookmars) - new}
|
|
RestoreBookmark(Node, Bookmark, False);
|
|
IsNodeFilterVisible(Node);
|
|
end;
|
|
if TopVisibleNode = nil then // Filter
|
|
ResetTopFocusedNodes;
|
|
end;
|
|
end;
|
|
|
|
if IsFilterMode then
|
|
FTotalSummaryCount := FilteredRecNumber
|
|
else
|
|
FTotalSummaryCount := RecNumber;
|
|
|
|
{ free deleted nodes}
|
|
if FNodes <> nil then
|
|
for i := 0 to FNodes.Count - 1 do
|
|
if FNodes[i] <> Nil then
|
|
begin
|
|
Node := TdxDBGridNode(FNodes[i]);
|
|
DeleteRecurse(Node);
|
|
end;
|
|
|
|
{sorting}
|
|
RefreshSorting;
|
|
{calc summary}
|
|
CalcSummary(ASmartReload);
|
|
{group sorting - if SortBySummary}
|
|
SortingBySummary;
|
|
{correct group nodes id}
|
|
CorrectIdGroupNodes;
|
|
|
|
if not ASmartReload then {***}
|
|
begin
|
|
if (Length(Current) > 0) and BookmarkValid(TBookmark(Current)) then
|
|
Bookmark := Current;
|
|
//TODO? Bookmark := Current; {restore dataset pos}
|
|
if ABOF <> AEOF then
|
|
begin
|
|
if ABOF then Prior
|
|
else
|
|
if AEOF then Next;
|
|
end;
|
|
// TODO seek to group node id
|
|
if egoCollapsedReload in OptionsEx then
|
|
begin
|
|
Node := TdxDBgridNode(Items[0]);
|
|
if (GroupColumnCount > 0) and (Node <> nil) and (egoCanNavigation in Options) then
|
|
begin
|
|
Column := GroupColumns[0];
|
|
V := Column.Field.Value;
|
|
if FindGroupNode(Node, V , ANode, Column.Sorted = csDown, 0) then
|
|
begin
|
|
if (ANode <> nil) and not ANode.Expanded then
|
|
if IsUseLocate then
|
|
begin
|
|
DataChangedBusy := False;
|
|
try
|
|
LocateByNode(TdxDBgridNode(FocusedNode), TdxDBgridNode(ANode), TdxDBGridNode(ANode).Id, True);
|
|
finally
|
|
DataChangedBusy := True;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
First;
|
|
FDatalink.DataSet.MoveBy(ANode.RecNo);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{restore top visible pos}
|
|
if ATopNode <> nil then
|
|
TopIndex := GetAbsoluteIndex(ATopNode);
|
|
finally
|
|
FldList.Free;
|
|
Screen.Cursor := OldCursor; { Always restore to normal }
|
|
if not ASmartReload then {***}
|
|
EnableControls;
|
|
end;
|
|
end;
|
|
finally
|
|
DataChangedBusy := False;
|
|
FlagSmartReload := False;
|
|
end;
|
|
{$IFDEF EGRID_DEBUG}
|
|
t2 := GetTickCount;
|
|
if Assigned(OnDebugEvent) then OnDebugEvent(Self, etLoadData, t2 - t1);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TCustomdxDBGrid.LocateByNode(OldNode, Node: TdxDBgridNode;
|
|
const Value: Variant; UseLocate: Boolean) : Boolean;
|
|
var
|
|
Distance: Integer;
|
|
// FlagEqual: Boolean;
|
|
PrevNode: TdxDBGridNode;
|
|
begin
|
|
Result := True;
|
|
if DataChangedBusy then Exit;
|
|
if (FKeyField <> nil) and (FKeyField.Value = Node.Id) and
|
|
(OldNode = Node) or ((Node = FUpdatingNode) and Datalink.Active and
|
|
(Datalink.DataSet.State = dsInsert)) then Exit;
|
|
if not (egoCanNavigation in Options) then Exit;
|
|
DataChangedBusy := True;
|
|
try
|
|
try
|
|
if FDatalink.Active then
|
|
begin
|
|
DataChangedBusy := not (FDatalink.DataSet.State in dsEditModes);
|
|
if UseLocate then
|
|
begin
|
|
DataChangedLocate := True;
|
|
FDatalink.DataSet.Locate(KeyField, Value, []);
|
|
end
|
|
else
|
|
begin
|
|
FLocateByNode := True;
|
|
try
|
|
// FDatalink.DataSet.MoveBy(Node.RecNo - OldNode.RecNo);
|
|
with FDatalink.DataSet do
|
|
begin
|
|
PrevNode := OldNode;
|
|
// FlagEqual := False;
|
|
if (State = dsInsert) and (OldNode <> LastNode) then
|
|
begin
|
|
PrevNode := OldNode.GetNextNode as TdxDBGridNode;
|
|
if PrevNode = nil then
|
|
PrevNode := OldNode.GetPriorNode as TdxDBGridNode;
|
|
if PrevNode = nil then
|
|
PrevNode := OldNode;
|
|
// FlagEqual := Node.RecNo = PrevNode.RecNo;
|
|
end;
|
|
Distance := Node.RecNo - PrevNode.RecNo;
|
|
MoveBy(Distance);
|
|
Scroll(0); // TODO new
|
|
// if FlagEqual or
|
|
// ((Distance > 0) and EOF) or ((Distance < 0) and BOF) then Scroll(0);
|
|
end;
|
|
finally
|
|
FLocateByNode := False;
|
|
end;
|
|
end;
|
|
end;
|
|
except
|
|
Result := False;
|
|
raise;
|
|
end;
|
|
finally
|
|
if UseLocate then
|
|
DataChangedLocate := False;
|
|
DataChangedBusy := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RefreshGroupList;
|
|
var
|
|
List: TList;
|
|
I: Integer;
|
|
|
|
procedure LoadNodes(Node: TdxTreeListNode);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
TdxDBGridNode(Node).FAssignedSummary := False;
|
|
if Node.Count > 0 then
|
|
if Node[0].Count > 0 then
|
|
for I := 0 to Node.Count - 1 do LoadNodes(Node[I])
|
|
else Node.AddNodesToList(List);
|
|
end;
|
|
|
|
begin
|
|
FOldTopVisibleId := Null;
|
|
FOldTopVisibleLevel := 0;
|
|
// reset smart flags
|
|
FlagSmartReload := False;
|
|
FlagFullRefresh := False;
|
|
if (GroupColumnCount < 1) and not (egoLoadAllRecords in Options) and
|
|
not IsPartialLoad then
|
|
begin
|
|
ClearNodes;
|
|
// ClearSelection;
|
|
RemoveDuplicateBookmarks(True);
|
|
Exit;
|
|
end;
|
|
BeginUpdate;
|
|
try
|
|
{save Bookmarks}
|
|
SaveBookmarks;
|
|
{save top visible position}
|
|
if TopVisibleNode <> nil then
|
|
begin
|
|
FOldTopVisibleId := TdxDBGridNode(TopVisibleNode).Id;
|
|
FOldTopVisibleLevel := TopVisibleNode.Level;
|
|
end;
|
|
if not IsGridMode and not IsPartialLoad then
|
|
if (GroupColumnCount = 0) or
|
|
((Count = 1) and not Items[0].HasChildren {Append in Empty Dataset}) then
|
|
begin
|
|
ClearNodes;
|
|
ClearSelection;
|
|
end;
|
|
List := TList.Create;
|
|
try
|
|
{ load nodes in List }
|
|
if (Count > 0) and (Items[0].HasChildren) then
|
|
for i := 0 to Count - 1 do LoadNodes(Items[i]);
|
|
List.Sort(CompareNodeId);
|
|
{ reload nodes}
|
|
LoadGroupList(List);
|
|
finally
|
|
List.Free;
|
|
end;
|
|
finally
|
|
{clear Bookmarks}
|
|
ClearBookmarks;
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RefreshNodeValues(var Node: TdxDBTreeListControlNode);
|
|
|
|
function FindDetailNode(Node: TdxDBGridNode; const Id: Variant): TdxDBGridNode;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := nil;
|
|
for i := 0 to Node.Count - 1 do
|
|
if TdxDBGridNode(Node[i]).Id = Id then
|
|
begin
|
|
Result := TdxDBGridNode(Node[i]);
|
|
if Result.Count > 0 then
|
|
Result := FindDetailNode(Result, Id);
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if not IsGridMode and (egoCanNavigation in Options) and (Node <> nil) and
|
|
Assigned(FKeyField) then
|
|
begin
|
|
if Node.Count > 0 then // find Detail Node
|
|
Node := FindDetailNode(Node as TdxDBGridNode, FKeyField.Value);
|
|
if Node <> nil then
|
|
begin
|
|
AssignNodeAllValues(Node);
|
|
{Assign propery Data: Pointer}
|
|
DoRefreshNodeData(Node);
|
|
end;
|
|
end;
|
|
if IsRowAutoHeight or (IsShowPreview and IsAutoCalcPreviewLines) then
|
|
begin
|
|
// reset node height
|
|
ClearNodeRowHeight;
|
|
Invalidate;
|
|
end
|
|
else InvalidateRect(GetRectNode(Node));
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ReLoadGroupList;
|
|
var
|
|
i : integer;
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
HideEditor;
|
|
if not IsSmartReload then ClearNodes;
|
|
ClearSelection;
|
|
FDatalink.ClearMapping;
|
|
if FDatalink.Active then DefineFieldMap;
|
|
{ Force columns to reaquire fields (in case dataset has changed) }
|
|
for I := 0 to ColumnCount-1 do
|
|
Columns[I].FieldName := Columns[I].FieldName;
|
|
LoadGroupList(nil);
|
|
if Assigned(FOnReloadGroupList) then FOnReloadGroupList(Self);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RemoveDuplicateBookmarks(AClearNodes: Boolean);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := BkmList.Count - 1 downto 1 do
|
|
begin
|
|
if CompareBkm(BkmList[I], BkmList[I - 1]) = 0 then
|
|
BkmList.Delete(I)
|
|
else
|
|
if AClearNodes then
|
|
BkmList.Objects[I] := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.Scroll(Distance: Integer);
|
|
var
|
|
OldRect, NewRect: TRect;
|
|
DScroll: Integer;
|
|
begin
|
|
if IsGridMode then
|
|
begin
|
|
if HandleAllocated then
|
|
begin
|
|
if IsRowAutoHeight or (IsShowPreview and IsAutoCalcPreviewLines) then
|
|
begin
|
|
UpdateScrollBars;
|
|
Invalidate;
|
|
end
|
|
else
|
|
begin
|
|
OldRect := GetRectNode(FocusedNode);
|
|
UpdateScrollBars;
|
|
NewRect := GetRectNode(FocusedNode);
|
|
ValidateRect(Handle, @OldRect);
|
|
Windows.InvalidateRect(Handle, @OldRect, False);
|
|
Windows.InvalidateRect(Handle, @NewRect, False);
|
|
if Distance <> 0 then
|
|
begin
|
|
// scrolling
|
|
if Abs(Distance) > RowCount then
|
|
Invalidate
|
|
else
|
|
begin
|
|
DScroll := GetRowHeight(FocusedNode, DefaultRowHeight, True{ReCalc})*Distance;
|
|
HideDragImages;
|
|
try
|
|
ScrollGridVert(DScroll);
|
|
finally
|
|
ShowDragImages;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if IsPartialLoad and (Distance <> 0) then
|
|
begin
|
|
DScroll := -Distance;
|
|
SaveBookmarks;
|
|
try
|
|
if DScroll > 0 then
|
|
begin
|
|
if DScroll > (TdxDBGridNode(LastNode).RecNo - TdxDBGridNode(FocusedNode).RecNo) then
|
|
GetNextNodes(lmCurrent, rmBottom, False);
|
|
end
|
|
else
|
|
begin
|
|
if DScroll < (TdxDBGridNode(TopNode).RecNo - TdxDBGridNode(FocusedNode).RecNo) then
|
|
GetNextNodes(lmCurrent, rmTop, False);
|
|
end;
|
|
finally
|
|
ClearBookmarks;
|
|
end;
|
|
end;
|
|
UpdateActive;
|
|
end;
|
|
// new
|
|
if egoSyncSelection in OptionsEx then
|
|
ResyncSelection;
|
|
DoChangeNodeEx;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetGroupIndex(AColumn: TdxDBTreeListColumn; AIndex: Integer);
|
|
begin
|
|
with TdxDBGridColumn(AColumn) do
|
|
if (FGroupIndex <> AIndex) or FRefreshGroupColumns then
|
|
begin
|
|
BeginUpdate;
|
|
BeginGrouping;
|
|
try
|
|
if (FGroupIndex <> -1) and not FRefreshGroupColumns then
|
|
begin
|
|
DeleteGroupColumn(FGroupIndex);
|
|
end;
|
|
if AIndex <> -1 then
|
|
InsertGroupColumn(AIndex, AColumn);
|
|
finally
|
|
EndGrouping;
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SmartRefreshNode;
|
|
var
|
|
V: Variant;
|
|
i: Integer;
|
|
Node: TdxDBGridNode;
|
|
Flag: Boolean;
|
|
|
|
function FindNodeId(var Node: TdxDBGridNode; AUpdatingNode: TdxDBGridNode): Boolean;
|
|
var
|
|
i: Integer;
|
|
ANode: TdxDBGridNode;
|
|
begin
|
|
Result := False;
|
|
if (Node.Id = V) and (Node <> AUpdatingNode) then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end
|
|
else
|
|
if Node.Count > 0 then
|
|
for i := 0 to Node.Count - 1 do
|
|
begin
|
|
ANode := TdxDBGridNode(Node[i]);
|
|
if FindNodeId(ANode, AUpdatingNode) then
|
|
begin
|
|
Node := ANode;
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function FindOtherNodeId(ANode: TdxDBGridNode): Boolean;
|
|
var
|
|
Node: TdxDBGridNode;
|
|
i: Integer;
|
|
begin
|
|
Result := False;
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
Node := TdxDBGridNode(Items[i]);
|
|
if FindNodeId(Node, ANode) then
|
|
begin
|
|
Result := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function FindId: Boolean;
|
|
var
|
|
Node: TdxDBGridNode;
|
|
i: Integer;
|
|
begin
|
|
Result := False;
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
Node := TdxDBGridNode(Items[i]);
|
|
if FindNodeId(Node, nil) then
|
|
begin
|
|
Result := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if not IsGridMode and (FKeyField <> Nil) and
|
|
(egoCanNavigation in Options) then
|
|
begin // find Detail Node
|
|
Flag := False;
|
|
Node := nil;
|
|
V := FKeyField.Value;
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
Node := TdxDBGridNode(Items[i]);
|
|
if FindNodeId(TdxDBGridNode(Node), nil) then
|
|
begin
|
|
NodeRecordChanged(nil, TdxDBTreeListControlNode(Node));
|
|
Flag := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
// node actual ?
|
|
if FUpdatingNode <> nil then
|
|
begin
|
|
Node := TdxDBGridNode(FocusedNode);
|
|
while Node.Count > 0 do Node := TdxDBGridNode(Node[0]);
|
|
if (FUpdatingNode.Id = Node.Id) and ((Node.Id <> V) or FindOtherNodeId(Node)) then
|
|
begin
|
|
if (FPrevId = V) or not FindId then
|
|
NodeRecordChanged(nil, TdxDBTreeListControlNode(Node))
|
|
else
|
|
begin
|
|
DeleteRecurse(Node);
|
|
Node := nil;
|
|
end;
|
|
Flag := True;
|
|
end;
|
|
FUpdatingNode := nil;
|
|
end;
|
|
if Flag then
|
|
begin
|
|
// Filter (Check)
|
|
if IsFilterMode then
|
|
begin
|
|
PrepareFilter;
|
|
NodeRefreshFilter(Node, True);
|
|
if TopVisibleNode = nil then // Filter
|
|
ResetTopFocusedNodes;
|
|
end
|
|
else
|
|
ReCalcSummary(Node);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DataChanged;
|
|
begin
|
|
// ChangeNodeEx
|
|
if (LockSelection = 0) and Assigned(OnChangeNodeEx) then
|
|
begin
|
|
CheckChangeNodeEx;
|
|
LoadChangeNodeInfo(FPrevNodeInfo);
|
|
end;
|
|
{***}
|
|
if IsSmartRefresh and not DataChangedBusy and
|
|
not DataChangedLocateResync then
|
|
begin
|
|
FRefreshGroupList := True;
|
|
try
|
|
SmartRefreshNode;
|
|
finally
|
|
FRefreshGroupList := False;
|
|
end;
|
|
end;
|
|
if DataChangedLocate and not DataChangedBusy then
|
|
DataChangedLocateResync := True
|
|
else DataChangedLocateResync := False;
|
|
|
|
if (Datalink.DataSet.State <> dsInsert) then FUpdatingNode := nil;
|
|
{***}
|
|
if DataChangedLocate then
|
|
begin
|
|
DataChangedLocate := False;
|
|
Exit;
|
|
end;
|
|
if DataChangedBusy then Exit;
|
|
if (IsDBTreeListControlDataSetBusy(Datalink.DataSet){IsBusy} or LockRefresh) and
|
|
IsLoadAllRecords then
|
|
begin
|
|
Scroll(0);
|
|
Exit;
|
|
end;
|
|
|
|
if IsGridMode then
|
|
begin
|
|
if not HandleAllocated then Exit;
|
|
if not (Datalink.DataSet.IsEmpty and (Datalink.DataSet.State = dsInsert)) then // TODO: Check!
|
|
UpdateScrollBars;
|
|
if (InplaceEditor <> nil) and not InplaceEditor.DisableRefresh then
|
|
InvalidateEditor;
|
|
if (FDataLink.DataSet.State = dsInsert) then
|
|
begin
|
|
EndSearch;
|
|
if egoResetColumnFocus in Options then FocusedColumn := 0; {new}
|
|
end;
|
|
ValidateRect(Handle, nil);
|
|
Invalidate;
|
|
end
|
|
else
|
|
begin
|
|
if DataChangedBusy or (FDataLink.DataSet.State = dsEdit) or
|
|
(FDataLink.DataSet.State = dsSetKey) then Exit;
|
|
if Assigned(FKeyField) and (FDataLink.DataSet.State = dsInsert) then
|
|
begin
|
|
InsertNode(FDataLink.DataSet.EOF);
|
|
if egoResetColumnFocus in Options then FocusedColumn := 0;
|
|
Exit;
|
|
end;
|
|
|
|
FRefreshGroupList := True;
|
|
try
|
|
if not IsSmartRefresh then RefreshGroupList;
|
|
if not FLocateByNode then FindNodeById;
|
|
finally
|
|
FRefreshGroupList := False;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsDataSetBusy: Boolean;
|
|
begin
|
|
Result := DataSetChangedBusy or DataChangedLocate;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.LinkActive(Value: Boolean);
|
|
begin
|
|
if not Value then HideEditor;
|
|
ReLoadGroupList; {<-ClearSelection}
|
|
if Value and (FFilterStream <> nil) then
|
|
begin
|
|
LoadDBGridFilterFromStream(FFilterStream, Filter);
|
|
FFilterStream.Free;
|
|
FFilterStream := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RecordChanged(Field: TField);
|
|
var
|
|
Node: TdxDBGridNode;
|
|
begin
|
|
if not IsGridMode then
|
|
begin
|
|
if (FDatalink.Dataset.State = dsInsert) and
|
|
(FUpdatingNode = nil) then Exit; {*****}
|
|
if (FDatalink.Dataset.State = dsSetKey) then
|
|
Exit;
|
|
end;
|
|
Node := TdxDBGridNode(FocusedNode);
|
|
NodeRecordChanged(Field, TdxDBTreeListControlNode(Node));
|
|
if (FUpdatingNode = nil) and (Node <> nil) then
|
|
FPrevId := Node.Id;
|
|
FUpdatingNode := Node;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.UpdateActive;
|
|
begin
|
|
FInUpdateActive := True;
|
|
try
|
|
if FDatalink.Active {and HandleAllocated} and not (csLoading in ComponentState) then
|
|
if FDatalink.DataSet.Active then
|
|
begin
|
|
if IsGridMode then
|
|
begin
|
|
if HandleAllocated and
|
|
(FocusedNumber <> FDatalink.ActiveRecord) then
|
|
begin
|
|
HideEditor;
|
|
if FDatalink.ActiveRecord < Count then
|
|
Items[FDatalink.ActiveRecord].Focused := True;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if not HandleAllocated and (Parent <> nil) then HandleNeeded;
|
|
if not FRefreshGroupList then FindNodeById;
|
|
if HandleAllocated then
|
|
inherited UpdateScrollBars;
|
|
end;
|
|
// if Assigned(FocusedField) and
|
|
// (FocusedField.Text <> FEditValue) then InvalidateEditor;
|
|
InvalidateEditorValue;
|
|
end;
|
|
finally
|
|
FInUpdateActive := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.BeginReadSettings(ARegIniWrapper: TdxRegIniWrapper);
|
|
begin
|
|
inherited BeginReadSettings(ARegIniWrapper);
|
|
BeginGrouping;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.EndReadSettings(ARegIniWrapper: TdxRegIniWrapper);
|
|
begin
|
|
EndGrouping;
|
|
inherited EndReadSettings(ARegIniWrapper);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ReadColumn(ARegIniWrapper: TdxRegIniWrapper;
|
|
const APathCol: string; AColumn: TdxTreeListColumn);
|
|
begin
|
|
with ARegIniWrapper do
|
|
begin
|
|
TdxDBGridColumn(AColumn).GroupIndex :=
|
|
ReadInteger(APathCol, 'GroupIndex', TdxDBGridColumn(AColumn).GroupIndex);
|
|
end;
|
|
inherited ReadColumn(ARegIniWrapper, APathCol, AColumn);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ReadSettings(ARegIniWrapper: TdxRegIniWrapper; const APath: string);
|
|
var
|
|
AStream: TMemoryStream;
|
|
begin
|
|
inherited ReadSettings(ARegIniWrapper, APath);
|
|
with ARegIniWrapper do
|
|
begin
|
|
ShowGroupPanel := ReadBool(APath, 'ShowGroupPanel', ShowGroupPanel);
|
|
ShowNewItemRow := ReadBool(APath, 'ShowNewItemRow', ShowNewItemRow);
|
|
ShowRowFooter := ReadBool(APath, 'ShowRowFooter', ShowRowFooter);
|
|
ShowSummaryFooter := ReadBool(APath, 'ShowSummaryFooter', ShowSummaryFooter);
|
|
AStream := TMemoryStream.Create;
|
|
try
|
|
AStream.SetSize(ReadBinaryData(APath, 'Filter', AStream.Memory^, 0));
|
|
ReadBinaryData(APath, 'Filter', AStream.Memory^, AStream.Size);
|
|
if AStream.Size <> 0 then
|
|
begin
|
|
if (DataLink.DataSet = nil) or (DataLink.DataSet.FieldCount = 0) then
|
|
begin
|
|
FFilterStream := AStream;
|
|
AStream := nil;
|
|
end
|
|
else
|
|
LoadDBGridFilterFromStream(AStream, Filter);
|
|
end;
|
|
finally
|
|
if AStream <> nil then
|
|
AStream.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.WriteColumn(ARegIniWrapper: TdxRegIniWrapper;
|
|
const APathCol: string; AColumn: TdxTreeListColumn);
|
|
begin
|
|
inherited WriteColumn(ARegIniWrapper, APathCol, AColumn);
|
|
with ARegIniWrapper do
|
|
begin
|
|
WriteInteger(APathCol, 'GroupIndex', TdxDBGridColumn(AColumn).GroupIndex);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.WriteSettings(ARegIniWrapper: TdxRegIniWrapper; const APath: string);
|
|
var
|
|
AStream: TMemoryStream;
|
|
begin
|
|
inherited WriteSettings(ARegIniWrapper, APath);
|
|
with ARegIniWrapper do
|
|
begin
|
|
WriteBool(APath, 'ShowGroupPanel', ShowGroupPanel);
|
|
WriteBool(APath, 'ShowNewItemRow', ShowNewItemRow);
|
|
WriteBool(APath, 'ShowRowFooter', ShowRowFooter);
|
|
WriteBool(APath, 'ShowSummaryFooter', ShowSummaryFooter);
|
|
AStream := TMemoryStream.Create;
|
|
try
|
|
SaveDBGridFilterToStream(AStream, Filter);
|
|
WriteBinaryData(APath, 'Filter', AStream.Memory^, AStream.Size);
|
|
finally
|
|
AStream.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetNodeByNavigation(ANode: TdxTreeListNode;
|
|
ANavigationMode: TdxTreeListNavigationMode; AGotoHidden: Boolean): TdxTreeListNode;
|
|
var
|
|
ADataSet: TDataSet;
|
|
begin
|
|
if IsLoadedAll then
|
|
Result := inherited GetNodeByNavigation(ANode, ANavigationMode, AGotoHidden)
|
|
else
|
|
begin
|
|
Result := nil;
|
|
if Assigned(DataSource) and Assigned(DataSource.DataSet) then
|
|
begin
|
|
ADataSet := DataSource.DataSet;
|
|
case ANavigationMode of
|
|
tlnmFirst:
|
|
begin
|
|
ADataSet.First;
|
|
Result := FocusedNode;
|
|
end;
|
|
tlnmLast:
|
|
begin
|
|
ADataSet.Last;
|
|
Result := FocusedNode;
|
|
end;
|
|
tlnmPrev:
|
|
begin
|
|
ADataSet.Prior;
|
|
if not ADataSet.BOF then
|
|
Result := FocusedNode;
|
|
end;
|
|
tlnmNext:
|
|
begin
|
|
ADataSet.Next;
|
|
if not ADataSet.EOF then
|
|
Result := FocusedNode;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.CancelDragSizing;
|
|
begin
|
|
inherited CancelDragSizing;
|
|
if Assigned(FFilterPopupListBox) and FFilterPopupListBox.ListVisible then
|
|
FFilterPopupListBox.ClosePopup(False);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.CheckFilterNode(ANode: TdxDBGridNode): Boolean;
|
|
begin
|
|
Result := True;
|
|
if IsFilterMode then
|
|
Result := Filter.CheckFilterNode(ANode);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ClearFilter;
|
|
begin
|
|
FFiltering := False;
|
|
SetFilterMode;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.DestroyFilter;
|
|
begin
|
|
FFilterPopupListBox.Free;
|
|
FFilterPopupListBox := nil;
|
|
FFilter.Free;
|
|
FFilter := nil;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetHeaderDropDownButtonState(AbsoluteIndex: Integer): TdxHeaderDropDownButtonState;
|
|
begin
|
|
Result := inherited GetHeaderDropDownButtonState(AbsoluteIndex);
|
|
if hdbNormal in Result then
|
|
begin
|
|
if Filter.IsColumnFilterExist(Columns[AbsoluteIndex]) then
|
|
Result := Result + [hdbActive];
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetFilterClass: TdxDBGridFilterClass;
|
|
begin
|
|
Result := TdxDBGridFilter;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.IsAutoFilterValuesLoad: Boolean;
|
|
begin
|
|
Result := IsLoadAllRecords;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.NodeFilterTestVisible(ATestNode: TdxDBGridNode): Boolean;
|
|
begin
|
|
Result := not IsFilterMode or NodeRefreshFilter(ATestNode, False);
|
|
end;
|
|
|
|
function TCustomdxDBGrid.NodeRefreshFilter(ATestNode: TdxDBGridNode; ARecalc: Boolean): Boolean;
|
|
var
|
|
ADetailList, AHiddenList: TList;
|
|
APrevFocusedNode, ANewFocusedNode: TdxDBGridNode;
|
|
|
|
procedure DeleteEmptyParentRecurse(ANode: TdxTreeListNode);
|
|
var
|
|
AParentNode: TdxTreeListNode;
|
|
begin
|
|
if (ANode <> nil) and (ANode.Count = 0) then
|
|
begin
|
|
AParentNode := ANode.Parent;
|
|
if ANode = APrevFocusedNode then
|
|
APrevFocusedNode := nil;
|
|
ANode.Free;
|
|
DeleteEmptyParentRecurse(AParentNode);
|
|
end;
|
|
end;
|
|
|
|
procedure LoadDetailNodes(ANode: TdxTreeListNode);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ANode.Count > 0 then
|
|
for I := 0 to ANode.Count - 1 do
|
|
LoadDetailNodes(ANode[I])
|
|
else
|
|
ADetailList.Add(ANode);
|
|
end;
|
|
|
|
procedure MoveToGroup(ADetailNode: TdxDBGridNode);
|
|
var
|
|
I: Integer;
|
|
AColumn: TdxDBGridColumn;
|
|
V: Variant;
|
|
ANode, CurNode, Node: TdxDBGridNode;
|
|
begin
|
|
Node := nil;
|
|
for I := 0 to GroupColumnCount - 1 do
|
|
begin
|
|
AColumn := GroupColumns[I];
|
|
V := GetColumnVariantData(ADetailNode, AColumn);
|
|
CurNode := Node;
|
|
if Node = nil then
|
|
Node := TdxDBGridNode(Items[0])
|
|
else
|
|
Node := TdxDBGridNode(Node[0]);
|
|
if not FindGroupNode(Node, V, ANode, AColumn.Sorted = csDown, 0) then
|
|
begin
|
|
if ANode = nil then //add
|
|
if I > 0 then
|
|
Node := TdxDBGridNode(CurNode.AddChild)
|
|
else
|
|
if (Node = nil) or (Node.Parent = nil) then
|
|
Node := TdxDBGridNode(Self.Add)
|
|
else
|
|
Node := TdxDBGridNode(Node.Parent.AddChild)
|
|
else
|
|
if ANode.Parent <> nil then
|
|
Node := TdxDBGridNode(ANode.Parent.InsertChild(ANode))
|
|
else
|
|
Node := TdxDBGridNode(Self.Insert(ANode));
|
|
Node.FGroupId := V;
|
|
Node.Id := ADetailNode.Id;
|
|
Node.VariantData[AColumn.Index] := ADetailNode.VariantData[AColumn.Index];
|
|
Node.Strings[AColumn.Index] := ADetailNode.Strings[AColumn.Index];
|
|
Node.FRecNo := ADetailNode.FRecNo;
|
|
// Assign propery Data: Pointer
|
|
DoRefreshNodeData(Node);
|
|
end
|
|
else
|
|
Node := ANode;
|
|
end;
|
|
ADetailNode.InternalMove(Node, natlAddChild);
|
|
end;
|
|
|
|
var
|
|
I: Integer;
|
|
ANode, AParentNode: TdxDBGridNode;
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
ADetailList := TList.Create;
|
|
AHiddenList := TList.Create;
|
|
try
|
|
APrevFocusedNode := TdxDBGridNode(FocusedNode);
|
|
// ClearListNodes; // ??
|
|
SetEmptyNodes;
|
|
if ATestNode = nil then
|
|
begin
|
|
// Load Detail Nodes
|
|
ADetailList.Capacity := Count;
|
|
for I := 0 to Count - 1 do
|
|
LoadDetailNodes(Items[I]);
|
|
// Filtering
|
|
for I := FHiddenList.Count - 1 downto 0 do
|
|
begin
|
|
ANode := TdxDBGridNode(FHiddenList[I]);
|
|
if CheckFilterNode(ANode) then
|
|
begin
|
|
if GroupColumnCount > 0 then // TODO Group
|
|
MoveToGroup(ANode)
|
|
else
|
|
ANode.InternalMoveToRoot;
|
|
FHiddenList.Delete(I);
|
|
end;
|
|
end;
|
|
AHiddenList.Capacity := ADetailList.Count;
|
|
for I := 0 to ADetailList.Count - 1 do
|
|
begin
|
|
ANode := TdxDBGridNode(ADetailList[I]);
|
|
if not CheckFilterNode(ANode) then
|
|
AHiddenList.Add(ANode);
|
|
end;
|
|
end
|
|
else
|
|
if not CheckFilterNode(ATestNode) then
|
|
AHiddenList.Add(ATestNode);
|
|
// Hide Nodes
|
|
Result := AHiddenList.Count = 0;
|
|
for I := 0 to AHiddenList.Count - 1 do
|
|
begin
|
|
ANode := TdxDBGridNode(AHiddenList[I]);
|
|
AParentNode := TdxDBGridNode(ANode.Parent);
|
|
ANode.InternalRemove;
|
|
//if NodeLinkList <> nil then NodeLinkList.Remove(ANode); // TODO CHECK
|
|
DeleteEmptyParentRecurse(AParentNode);
|
|
end;
|
|
I := FHiddenList.Count;
|
|
if AHiddenList.Count <> 0 then
|
|
if AHiddenList.Count = 1 then
|
|
FHiddenList.Add(AHiddenList.List^[0])
|
|
else
|
|
begin
|
|
FHiddenList.Count := FHiddenList.Count + AHiddenList.Count;
|
|
System.Move(AHiddenList.List^[0], FHiddenList.List^[I], AHiddenList.Count * SizeOf(Pointer));
|
|
end;
|
|
if ATestNode = nil then
|
|
begin
|
|
// Make Focused (set pos in DataSet)
|
|
ResetTopFocusedNodes;
|
|
ANewFocusedNode := TdxDBGridNode(FocusedNode);
|
|
if Assigned(ANewFocusedNode) then
|
|
begin
|
|
if not Assigned(APrevFocusedNode) then
|
|
SetFocusedNode(ANewFocusedNode, FocusedColumn, False)
|
|
else
|
|
if FHiddenList.IndexOf(APrevFocusedNode) = -1 then
|
|
inherited SetFocusedNode(APrevFocusedNode, FocusedColumn, False)
|
|
else
|
|
begin
|
|
CorrectIdGroupNodes;
|
|
LocateByNode(APrevFocusedNode, ANewFocusedNode, ANewFocusedNode.Id, IsUseLocate);
|
|
end;
|
|
end;
|
|
end;
|
|
if (AHiddenList.Count = 1) and (AHiddenList[0] = ATestNode) then
|
|
ATestNode := nil;
|
|
if ARecalc {and (ATestNode <> nil)} then
|
|
ReCalcSummary(ATestNode);
|
|
finally
|
|
AHiddenList.Free;
|
|
ADetailList.Free;
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.PrepareFilter;
|
|
begin
|
|
Filter.Prepare;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RefreshFilter;
|
|
begin
|
|
PrepareFilter;
|
|
NodeRefreshFilter(nil, True);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.UpdateDataSetFilter;
|
|
var
|
|
ADataSet: TDataSet;
|
|
S: string;
|
|
begin
|
|
if Assigned(DataSource) then
|
|
ADataSet := DataSource.DataSet
|
|
else
|
|
ADataSet := nil;
|
|
S := Filter.FilterText;
|
|
if Assigned(FOnFilterChanged) then
|
|
FOnFilterChanged(Self, ADataSet, S);
|
|
// TODO AutoDataSetFilter changed
|
|
if Filter.AutoDataSetFilter and Assigned(ADataSet) then
|
|
begin
|
|
ADataSet.Filter := S;
|
|
if Filter.CaseInsensitive then
|
|
ADataSet.FilterOptions := ADataSet.FilterOptions + [foCaseInsensitive]
|
|
else
|
|
ADataSet.FilterOptions := ADataSet.FilterOptions - [foCaseInsensitive];
|
|
ADataSet.Filtered := ADataSet.Filter <> ''; // TODO: CHECK!!!
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetStatusButtonVisible: Boolean;
|
|
begin
|
|
Result := not Filter.IsEmpty;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetStatusText: string;
|
|
begin
|
|
Result := Filter.Criteria.FilterCaption;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetStatusCloseButtonHint: string;
|
|
begin
|
|
Result := dxSFilterStatusCloseButtonHint;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetFilterMode;
|
|
begin
|
|
FFiltering := not Filter.IsEmpty;
|
|
RefreshFilter;
|
|
UpdateDataSetFilter;
|
|
end;
|
|
|
|
// private
|
|
function TCustomdxDBGrid.CalcGroupPanelHeight(ColCount : Integer): Integer;
|
|
begin
|
|
Result := HeaderRowHeight + agpHOfs*2 + (ColCount-1)*(agpHDeltaY+(HeaderRowHeight div 2))
|
|
end;
|
|
|
|
function TCustomdxDBGrid.ChangedGroupFields: Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := (FGroupFields.Count <> GroupColumnCount);
|
|
if not Result then
|
|
for i := 0 to GroupColumnCount - 1 do
|
|
if AnsiCompareText(FGroupFields[i], GroupColumns[i].FieldName) <> 0 then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.CheckChangeNodeEx;
|
|
var
|
|
NodeInfo: TdxChangeNodeInfo;
|
|
begin
|
|
LoadChangeNodeInfo(NodeInfo);
|
|
if (FPrevNodeInfo.Level <> NodeInfo.Level) or
|
|
(FPrevNodeInfo.Selected <> NodeInfo.Selected) or
|
|
(VarType(FPrevNodeInfo.Id) <> VarType(NodeInfo.Id)) or
|
|
(FPrevNodeInfo.Id <> NodeInfo.Id) then
|
|
DoChangeNodeEx;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ClearBookmarks;
|
|
begin
|
|
ClearIdList;
|
|
FSaveBkmList.Clear;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ClearIdList;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if FSaveIdList = nil then Exit;
|
|
for I := 0 to FSaveIdList.Count - 1 do
|
|
Dispose(PVariant(FSaveIdList[I]));
|
|
FSaveIdList.Clear;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ClearGroupFields;
|
|
begin
|
|
FGroupFields.Clear;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetFilter: TdxDBGridFilter;
|
|
begin
|
|
if FFilter = nil then
|
|
FFilter := GetFilterClass.Create(Self);
|
|
Result := FFilter;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetFooterPanelVisible: Boolean;
|
|
begin
|
|
Result := inherited ShowFooter;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetGroupPanelVisible: Boolean;
|
|
begin
|
|
Result := FGroupPanelVisible;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetOptions: TdxDBGridOptions;
|
|
begin
|
|
Result := FOptions;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetOptionsEx: TdxDBGridOptionsEx;
|
|
begin
|
|
Result := FOptionsEx;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetOptionsBehavior: TdxDBGridOptionsBehavior;
|
|
begin
|
|
Result := FOptionsBehavior;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetOptionsCustomize: TdxDBGridOptionsCustomize;
|
|
begin
|
|
Result := FOptionsCustomize;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetOptionsDB: TdxDBGridOptionsDB;
|
|
begin
|
|
Result := FOptionsDB;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetOptionsView: TdxDBGridOptionsView;
|
|
begin
|
|
Result := FOptionsView;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetPartialLoad: Boolean;
|
|
begin
|
|
Result := edgoPartialLoad in OptionsDB;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.GetUseBookmarks: Boolean;
|
|
begin
|
|
Result := edgoUseBookmarks in OptionsDB;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.InsertNode(FAppend: Boolean);
|
|
var
|
|
ANode: TdxTreeListNode;
|
|
F: TField;
|
|
I: Integer;
|
|
MasterField: TField;
|
|
VData: TNodeVariantData;
|
|
PrevNewItemRowActive: Boolean;
|
|
begin
|
|
ClearListNodes; // TODO: check
|
|
ANode := FocusedNode;
|
|
if ANode <> nil then
|
|
while ANode.Count > 0 do ANode := ANode[0];
|
|
if ANode <> nil then
|
|
if ANode.Parent <> nil then
|
|
if not FAppend then ANode := ANode.Parent.InsertChild(ANode)
|
|
else ANode := ANode.Parent.AddChild
|
|
else if not FAppend then ANode := Self.Insert(ANode)
|
|
else ANode := Self.Add
|
|
else ANode := Self.Add;
|
|
ANode.MakeVisible;
|
|
// no locate !
|
|
TdxDBGridNode(ANode).Id := Null;
|
|
TdxDBGridNode(ANode).FGroupId := Null;
|
|
TdxDBGridNode(ANode).FRecNo := FTotalSummaryCount;
|
|
{*****}
|
|
AssignNodeAllValues(TdxDBGridNode(ANode));
|
|
{*****}
|
|
{***}
|
|
FUpdatingNode := TdxDBGridNode(ANode);
|
|
FPrevId := TdxDBGridNode(ANode).Id;
|
|
{***}
|
|
PrevNewItemRowActive := NewItemRowActive;
|
|
ANode.Focused := True;
|
|
if FocusedNode <> nil then // load group column values
|
|
for I := GroupColumnCount - 1 downto 0 do
|
|
if ANode <> nil then
|
|
begin
|
|
ANode := ANode.Parent;
|
|
F := GroupColumns[I].Field;
|
|
if Assigned(F) and (ANode <> nil) then
|
|
begin
|
|
if not F.Lookup and F.CanModify then
|
|
F.Value := TdxDBGridNode(ANode).GroupId;
|
|
if F.Lookup then
|
|
begin
|
|
MasterField := FDatalink.DataSet.FieldByName(F.KeyFields); // TODO multi lookup
|
|
VData := TdxDBGridNode(ANode).VariantData[GroupColumns[I].Index];
|
|
if MasterField.CanModify and (nadLookupValue in VData.AssignedValues) then
|
|
MasterField.Value := VData.LookupValue;
|
|
end;
|
|
end;
|
|
end;
|
|
NewItemRowActive := PrevNewItemRowActive;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.LoadChangeNodeInfo(var NodeInfo: TdxChangeNodeInfo);
|
|
begin
|
|
if (FocusedNode <> nil) and
|
|
Assigned(DataLink.DataSet) and DataLink.DataSet.Active then
|
|
begin
|
|
NodeInfo.Level := FocusedNode.Level;
|
|
NodeInfo.Selected := FocusedNode.Selected;
|
|
if Assigned(FKeyField) and Assigned(FKeyField.DataSet) then
|
|
NodeInfo.Id := FKeyField.Value
|
|
else NodeInfo.Id := Null;
|
|
end
|
|
else
|
|
begin
|
|
NodeInfo.Level := -1;
|
|
NodeInfo.Id := Null;
|
|
NodeInfo.Selected := False;
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.RestoreBookmark(ANode: TdxTreeListNode; const ABookmark: TBookmarkStr; ABackward: Boolean): Boolean;
|
|
var
|
|
L, H, I, C: Integer;
|
|
begin
|
|
Result := False;
|
|
L := 0;
|
|
H := FSaveBkmList.Count - 1;
|
|
while L <= H do
|
|
begin
|
|
I := (L + H) shr 1;
|
|
C := CompareBkm(FSaveBkmList[I], ABookmark);
|
|
if C < 0 then L := I + 1
|
|
else
|
|
begin
|
|
H := I - 1;
|
|
if C = 0 then
|
|
begin
|
|
if ABackward then
|
|
begin
|
|
BkmList.Insert(0, ABookmark);
|
|
BkmList.Objects[0] := ANode;
|
|
end
|
|
else
|
|
begin
|
|
BkmList.Add(ABookmark);
|
|
BkmList.Objects[BkmList.Count - 1] := ANode;
|
|
end;
|
|
Result := True;
|
|
FSaveBkmList.Delete(I);
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.RestoreSelected(ANode: TdxTreeListNode);
|
|
var
|
|
Index: Integer;
|
|
begin
|
|
if FindIdVariant(TdxDBGridNode(ANode).Id, Index) then
|
|
begin
|
|
ANode.Selected := True;
|
|
Dispose(PVariant(FSaveIdList[Index]));
|
|
FSaveIdList.Delete(Index);
|
|
end;
|
|
end;
|
|
|
|
function TCustomdxDBGrid.FindIdVariant(const Id: Variant; var Index: Integer): Boolean;
|
|
var
|
|
L, H, I, C: Integer;
|
|
begin
|
|
Result := False;
|
|
if FSaveIdList = nil then Exit;
|
|
L := 0;
|
|
H := FSaveIdList.Count - 1;
|
|
while L <= H do
|
|
begin
|
|
I := (L + H) shr 1;
|
|
C := VarCompare(PVariant(FSaveIdList[I])^, Id);
|
|
if C < 0 then L := I + 1 else
|
|
begin
|
|
H := I - 1;
|
|
if C = 0 then
|
|
begin
|
|
Result := True;
|
|
L := I;
|
|
end;
|
|
end;
|
|
end;
|
|
Index := L;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SaveBookmarks;
|
|
var
|
|
I: Integer;
|
|
P: PVariant;
|
|
begin
|
|
if not IsUseBookmarks then
|
|
begin
|
|
FSaveBkmList.Clear;
|
|
if (BkmList.Count > 0) and (BkmList.Objects[0] <> nil) then
|
|
begin
|
|
if FSaveIdList = nil then FSaveIdList := TList.Create
|
|
else ClearIdList;
|
|
for I := 0 to BkmList.Count - 1 do
|
|
begin
|
|
New(P);
|
|
P^ := TdxDBGridNode(BkmList.Objects[I]).Id;
|
|
FSaveIdList.Add(P);
|
|
end;
|
|
FSaveIdList.Sort(CompareVariant);
|
|
end;
|
|
end
|
|
else
|
|
FSaveBkmList.Assign(BkmList);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SaveGroupFields;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
FGroupFields.Clear;
|
|
for I := 0 to GroupColumnCount - 1 do
|
|
FGroupFields.Add(GroupColumns[I].FieldName);
|
|
if IsGridMode then
|
|
FPrevReloadMode := rlGridMode
|
|
else FPrevReloadMode := rlAllRecords;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.ScrollGroupPanel;
|
|
const
|
|
MaxTimeScroll = 400; {400 ms}
|
|
hOne = 20; {pixels - scroll on one pixel}
|
|
var
|
|
R, R1: TRect;
|
|
k, i, h, d, j: Integer;
|
|
t1, t2: LongInt;
|
|
FlagUp, Done: Boolean;
|
|
DrawInfo: TdxGridDrawInfo;
|
|
begin
|
|
h := CalcGroupPanelHeight(GroupColumnCount);
|
|
t1 := GetTickCount;
|
|
Done := False;
|
|
FlagUp := not ShowGroupPanel;
|
|
CalcRectInfo(DrawInfo);
|
|
with DrawInfo do
|
|
begin
|
|
R := CRect;
|
|
if not IsRectEmpty(FooterRect) then
|
|
R.Bottom := FooterRect.Top
|
|
end;
|
|
d := -1;
|
|
if not FlagUp then d := -d;
|
|
R1 := R;
|
|
if not FlagUp then R1 := Rect(R1.Left, 0, R1.Right, 0)
|
|
else R1.Top := R1.Bottom;
|
|
i := 0;
|
|
k := (R.Bottom - R.Top) div 256;
|
|
if k = 0 then k := 1;
|
|
while (not Done) do
|
|
begin
|
|
if i <= hOne then
|
|
begin
|
|
j := d*k
|
|
end
|
|
else
|
|
begin
|
|
Inc(k);
|
|
j := d*k;
|
|
end;
|
|
if ((i + abs(j)) > h) or (abs(j) > ((h-i) div 2) ) then j := d*(h - i);
|
|
ScrollWindowEx(Handle, 0, j, @R, @R, 0, nil, SW_Invalidate);
|
|
Inc(i, abs(j));
|
|
if not FlagUp then
|
|
begin
|
|
R1.Bottom := i;
|
|
Canvas.Brush.Color := dxclGroupPanel;
|
|
end
|
|
else
|
|
begin
|
|
R1.Top := R1.Bottom - i;
|
|
Canvas.Brush.Color := Color;
|
|
end;
|
|
Canvas.FillRect(R1);
|
|
|
|
t2 := GetTickCount;
|
|
Done := ((t2 - t1) > MaxTimeScroll) or (i >= h);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetDataSource(Value: TDataSource);
|
|
begin
|
|
if Value = FDatalink.Datasource then Exit;
|
|
ClearSelection;
|
|
FDataLink.DataSource := Value;
|
|
if Value <> nil then Value.FreeNotification(Self);
|
|
if IsGridMode then
|
|
LinkActive(FDataLink.Active);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetDefaultFields(Value: Boolean);
|
|
begin
|
|
if FDefaultFields <> Value then
|
|
begin
|
|
FDefaultFields := Value;
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
Options := Options - [egoLoadAllRecords];
|
|
ClearGroupColumns;
|
|
DestroyColumns;
|
|
DefaultLayout := True;
|
|
Filter.Active := False;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetFilter(Value: TdxDBGridFilter);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetFooterPanelVisible(Value: Boolean);
|
|
begin
|
|
inherited ShowFooter := Value;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetGroupPanelColor(Value: TColor);
|
|
begin
|
|
if GroupPanelColor <> Value then
|
|
begin
|
|
FGroupPanelColor := Value;
|
|
if ShowGroupPanel then Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetGroupPanelFontColor(Value: TColor);
|
|
begin
|
|
if FGroupPanelFontColor <> Value then
|
|
begin
|
|
FGroupPanelFontColor := Value;
|
|
if ShowGroupPanel then Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetGroupPanelVisible(Value: Boolean);
|
|
begin
|
|
if Value = FGroupPanelVisible then Exit;
|
|
HideEditor;
|
|
FGroupPanelVisible := Value;
|
|
if HandleAllocated then
|
|
begin
|
|
Windows.ValidateRect(Handle, nil);
|
|
// ani
|
|
if (LockUpdate = 0) and not (csDesigning in ComponentState) and
|
|
not (csLoading in ComponentState) and (State = tsNormal) then ScrollGroupPanel;
|
|
LayoutChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetOptions(Value: TdxDBGridOptions);
|
|
const
|
|
OptionsBehaviorCount = 12;
|
|
OptionsCustomizeCount = 3;
|
|
OptionsDBCount = 11;
|
|
OptionsViewCount = 6;
|
|
AOptionsBehavior: array [0..OptionsBehaviorCount - 1] of TdxDBGridOption = (
|
|
egoCaseInsensitive, egoDblClick, egoEditing, egoExtMultiSelect, egoImmediateEditor,
|
|
egoMultiSelect, egoSeekDetail, egoShowHourGlass, egoStoreToIniFile,
|
|
egoStoreToRegistry, egoTabs, egoTabThrough);
|
|
ANewOptionsBehavior: array [0..OptionsBehaviorCount - 1] of TdxDBGridOptionBehavior = (
|
|
edgoCaseInsensitive, edgoDblClick, edgoEditing, edgoExtMultiSelect, edgoImmediateEditor,
|
|
edgoMultiSelect, edgoSeekDetail, edgoShowHourGlass, edgoStoreToIniFile,
|
|
edgoStoreToRegistry, edgoTabs, edgoTabThrough);
|
|
AOptionsCustomize: array [0..OptionsCustomizeCount - 1] of TdxDBGridOption = (
|
|
egoColumnMoving, egoColumnSizing, egoExtCustomizing);
|
|
ANewOptionsCustomize: array [0..OptionsCustomizeCount - 1] of TdxDBGridOptionCustomize = (
|
|
edgoColumnMoving, edgoColumnSizing, edgoExtCustomizing);
|
|
AOptionsDB: array [0..OptionsDBCount - 1] of TdxDBGridOption = (
|
|
egoCanAppend, egoCancelOnExit, egoCanDelete, egoCanInsert, egoCanNavigation,
|
|
egoConfirmDelete, egoLoadAllRecords, egoResetColumnFocus, egoSmartRefresh,
|
|
egoSmartReload, egoUseLocate);
|
|
ANewOptionsDB: array [0..OptionsDBCount - 1] of TdxDBGridOptionDB = (
|
|
edgoCanAppend, edgoCancelOnExit, edgoCanDelete, edgoCanInsert, edgoCanNavigation,
|
|
edgoConfirmDelete, edgoLoadAllRecords, edgoResetColumnFocus, edgoSmartRefresh,
|
|
edgoSmartReload, edgoUseLocate);
|
|
AOptionsView: array [0..OptionsViewCount - 1] of TdxDBGridOption = (
|
|
egoAutoWidth, egoDrawEndEllipsis, egoHideFocusRect, egoIndicator, egoPreview,
|
|
egoRowSelect);
|
|
ANewOptionsView: array [0..OptionsViewCount - 1] of TdxDBGridOptionView = (
|
|
edgoAutoWidth, edgoDrawEndEllipsis, edgoHideFocusRect, edgoIndicator, edgoPreview,
|
|
edgoRowSelect);
|
|
var
|
|
PrevOptionsBehavior: TdxDBGridOptionsBehavior;
|
|
PrevOptionsCustomize: TdxDBGridOptionsCustomize;
|
|
PrevOptionsDB: TdxDBGridOptionsDB;
|
|
PrevOptionsView: TdxDBGridOptionsView;
|
|
I: Integer;
|
|
begin
|
|
PrevOptionsBehavior := OptionsBehavior;
|
|
PrevOptionsCustomize := OptionsCustomize;
|
|
PrevOptionsDB := OptionsDB;
|
|
PrevOptionsView := OptionsView;
|
|
// Behavior
|
|
for I := 0 to OptionsBehaviorCount - 1 do
|
|
if AOptionsBehavior[I] in Value then
|
|
Include(PrevOptionsBehavior, ANewOptionsBehavior[I])
|
|
else
|
|
Exclude(PrevOptionsBehavior, ANewOptionsBehavior[I]);
|
|
// Customize
|
|
for I := 0 to OptionsCustomizeCount - 1 do
|
|
if AOptionsCustomize[I] in Value then
|
|
Include(PrevOptionsCustomize, ANewOptionsCustomize[I])
|
|
else
|
|
Exclude(PrevOptionsCustomize, ANewOptionsCustomize[I]);
|
|
// DB
|
|
for I := 0 to OptionsDBCount - 1 do
|
|
if AOptionsDB[I] in Value then
|
|
Include(PrevOptionsDB, ANewOptionsDB[I])
|
|
else
|
|
Exclude(PrevOptionsDB, ANewOptionsDB[I]);
|
|
// View
|
|
for I := 0 to OptionsViewCount - 1 do
|
|
if AOptionsView[I] in Value then
|
|
Include(PrevOptionsView, ANewOptionsView[I])
|
|
else
|
|
Exclude(PrevOptionsView, ANewOptionsView[I]);
|
|
BeginUpdate;
|
|
try
|
|
OptionsBehavior := PrevOptionsBehavior;
|
|
OptionsCustomize := PrevOptionsCustomize;
|
|
OptionsDB := PrevOptionsDB;
|
|
OptionsView := PrevOptionsView;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetOptionsEx(Value: TdxDBGridOptionsEx);
|
|
const
|
|
OptionsBehaviorCount = 16;
|
|
OptionsCustomizeCount = 8;
|
|
OptionsDBCount = 1;
|
|
OptionsViewCount = 7;
|
|
AOptionsBehavior: array [0..OptionsBehaviorCount - 1] of TdxDBGridOptionEx = (
|
|
egoAnsiSort, egoAutoSearch, egoAutoSort, egoBandButtonClicking, egoCellMultiSelect,
|
|
egoCollapsedReload, egoDragCollapse, egoDragExpand, egoDragScroll, egoEnterShowEditor,
|
|
egoEnterThrough, egoHeaderButtonClicking, egoHorzThrough, egoMouseScroll, egoMultiSort,
|
|
egoVertThrough);
|
|
ANewOptionsBehavior: array [0..OptionsBehaviorCount - 1] of TdxDBGridOptionBehavior = (
|
|
edgoAnsiSort, edgoAutoSearch, edgoAutoSort, edgoBandButtonClicking, edgoCellMultiSelect,
|
|
edgoCollapsedReload, edgoDragCollapse, edgoDragExpand, edgoDragScroll, edgoEnterShowEditor,
|
|
edgoEnterThrough, edgoHeaderButtonClicking, edgoHorzThrough, edgoMouseScroll, edgoMultiSort,
|
|
edgoVertThrough);
|
|
AOptionsCustomize: array [0..OptionsCustomizeCount - 1] of TdxDBGridOptionEx = (
|
|
egoBandMoving, egoBandPanelSizing, egoBandSizing, egoFullSizing, egoHeaderPanelSizing,
|
|
egoKeepColumnWidth, egoNotHideColumn, egoRowSizing);
|
|
ANewOptionsCustomize: array [0..OptionsCustomizeCount - 1] of TdxDBGridOptionCustomize = (
|
|
edgoBandMoving, edgoBandPanelSizing, edgoBandSizing, edgoFullSizing, edgoHeaderPanelSizing,
|
|
edgoKeepColumnWidth, edgoNotHideColumn, edgoRowSizing);
|
|
AOptionsDB: array [0..OptionsDBCount - 1] of TdxDBGridOptionEx = (
|
|
egoSyncSelection);
|
|
ANewOptionsDB: array [0..OptionsDBCount - 1] of TdxDBGridOptionDB = (
|
|
edgoSyncSelection);
|
|
AOptionsView: array [0..OptionsViewCount - 1] of TdxDBGridOptionEx = (
|
|
egoAutoCalcPreviewLines, egoAutoHeaderPanelHeight, egoBandHeaderWidth,
|
|
egoInvertSelect, egoRowAutoHeight, egoShowButtonAlways, egoUseBitmap);
|
|
ANewOptionsView: array [0..OptionsViewCount - 1] of TdxDBGridOptionView = (
|
|
edgoAutoCalcPreviewLines, edgoAutoHeaderPanelHeight, edgoBandHeaderWidth,
|
|
edgoInvertSelect, edgoRowAutoHeight, edgoShowButtonAlways, edgoUseBitmap);
|
|
var
|
|
PrevOptionsBehavior: TdxDBGridOptionsBehavior;
|
|
PrevOptionsCustomize: TdxDBGridOptionsCustomize;
|
|
PrevOptionsDB: TdxDBGridOptionsDB;
|
|
PrevOptionsView: TdxDBGridOptionsView;
|
|
I: Integer;
|
|
begin
|
|
PrevOptionsBehavior := OptionsBehavior;
|
|
PrevOptionsCustomize := OptionsCustomize;
|
|
PrevOptionsDB := OptionsDB;
|
|
PrevOptionsView := OptionsView;
|
|
// Behavior
|
|
for I := 0 to OptionsBehaviorCount - 1 do
|
|
if AOptionsBehavior[I] in Value then
|
|
Include(PrevOptionsBehavior, ANewOptionsBehavior[I])
|
|
else
|
|
Exclude(PrevOptionsBehavior, ANewOptionsBehavior[I]);
|
|
// Customize
|
|
for I := 0 to OptionsCustomizeCount - 1 do
|
|
if AOptionsCustomize[I] in Value then
|
|
Include(PrevOptionsCustomize, ANewOptionsCustomize[I])
|
|
else
|
|
Exclude(PrevOptionsCustomize, ANewOptionsCustomize[I]);
|
|
// DB
|
|
for I := 0 to OptionsDBCount - 1 do
|
|
if AOptionsDB[I] in Value then
|
|
Include(PrevOptionsDB, ANewOptionsDB[I])
|
|
else
|
|
Exclude(PrevOptionsDB, ANewOptionsDB[I]);
|
|
// View
|
|
for I := 0 to OptionsViewCount - 1 do
|
|
if AOptionsView[I] in Value then
|
|
Include(PrevOptionsView, ANewOptionsView[I])
|
|
else
|
|
Exclude(PrevOptionsView, ANewOptionsView[I]);
|
|
BeginUpdate;
|
|
try
|
|
OptionsBehavior := PrevOptionsBehavior;
|
|
OptionsCustomize := PrevOptionsCustomize;
|
|
OptionsDB := PrevOptionsDB;
|
|
OptionsView := PrevOptionsView;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetOptionsBehavior(Value: TdxDBGridOptionsBehavior);
|
|
const
|
|
OptionCount = 12;
|
|
OptionExCount = 16;
|
|
AOptionsBehavior: array [0..OptionCount - 1] of TdxDBGridOptionBehavior = (edgoCaseInsensitive,
|
|
edgoDblClick, edgoEditing, edgoExtMultiSelect, edgoImmediateEditor, edgoMultiSelect,
|
|
edgoSeekDetail, edgoShowHourGlass, edgoStoreToIniFile, edgoStoreToRegistry, edgoTabs, edgoTabThrough);
|
|
AOptions: array [0..OptionCount - 1] of TdxDBGridOption = (egoCaseInsensitive,
|
|
egoDblClick, egoEditing, egoExtMultiSelect, egoImmediateEditor, egoMultiSelect,
|
|
egoSeekDetail, egoShowHourGlass, egoStoreToIniFile, egoStoreToRegistry, egoTabs, egoTabThrough);
|
|
AOptionsBehaviorEx: array [0..OptionExCount - 1] of TdxDBGridOptionBehavior = (edgoAnsiSort,
|
|
edgoAutoSearch, edgoAutoSort, edgoBandButtonClicking, edgoCellMultiSelect,
|
|
edgoCollapsedReload, edgoDragCollapse, edgoDragExpand, edgoDragScroll,
|
|
edgoEnterShowEditor, edgoEnterThrough, edgoHeaderButtonClicking, edgoHorzThrough,
|
|
edgoMouseScroll, edgoMultiSort, edgoVertThrough);
|
|
AOptionsEx: array [0..OptionExCount - 1] of TdxDBGridOptionEx = (egoAnsiSort,
|
|
egoAutoSearch, egoAutoSort, egoBandButtonClicking, egoCellMultiSelect,
|
|
egoCollapsedReload, egoDragCollapse, egoDragExpand, egoDragScroll,
|
|
egoEnterShowEditor, egoEnterThrough, egoHeaderButtonClicking, egoHorzThrough,
|
|
egoMouseScroll, egoMultiSort, egoVertThrough);
|
|
var
|
|
NewOptions: TdxTreeListOptions;
|
|
NewOptionsEx: TdxTreeListOptionsEx;
|
|
SyncOptions: TdxDBGridOptions;
|
|
SyncOptionsEx: TdxDBGridOptionsEx;
|
|
I: Integer;
|
|
begin
|
|
if FOptionsBehavior <> Value then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
NewOptions := inherited Options;
|
|
NewOptionsEx := inherited OptionsEx;
|
|
if edgoAnsiSort in Value then
|
|
Include(NewOptionsEx, aoAnsiSort)
|
|
else
|
|
Exclude(NewOptionsEx, aoAnsiSort);
|
|
if edgoAutoCopySelectedToClipboard in Value then
|
|
Include(NewOptionsEx, aoAutoCopySelectedToClipboard)
|
|
else
|
|
Exclude(NewOptionsEx, aoAutoCopySelectedToClipboard);
|
|
if edgoAutoSearch in Value then
|
|
Include(NewOptionsEx, aoAutoSearch)
|
|
else
|
|
Exclude(NewOptionsEx, aoAutoSearch);
|
|
if edgoAutoSort in Value then
|
|
Include(NewOptions, aoAutoSort)
|
|
else
|
|
Exclude(NewOptions, aoAutoSort);
|
|
if edgoBandButtonClicking in Value then
|
|
Include(NewOptionsEx, aoBandButtonClicking)
|
|
else
|
|
Exclude(NewOptionsEx, aoBandButtonClicking);
|
|
if edgoCaseInsensitive in Value then
|
|
Include(NewOptions, aoCaseInsensitive)
|
|
else
|
|
Exclude(NewOptions, aoCaseInsensitive);
|
|
if edgoCellMultiSelect in Value then
|
|
Include(NewOptionsEx, aoCellMultiSelect)
|
|
else
|
|
Exclude(NewOptionsEx, aoCellMultiSelect);
|
|
// edgoCollapsedReload
|
|
// edgoDblClick
|
|
if edgoDragCollapse in Value then
|
|
Include(NewOptionsEx, aoDragCollapse)
|
|
else
|
|
Exclude(NewOptionsEx, aoDragCollapse);
|
|
if edgoDragExpand in Value then
|
|
Include(NewOptionsEx, aoDragExpand)
|
|
else
|
|
Exclude(NewOptionsEx, aoDragExpand);
|
|
if edgoDragScroll in Value then
|
|
Include(NewOptionsEx, aoDragScroll)
|
|
else
|
|
Exclude(NewOptionsEx, aoDragScroll);
|
|
if edgoEditing in Value then
|
|
Include(NewOptions, aoEditing)
|
|
else
|
|
Exclude(NewOptions, aoEditing);
|
|
if edgoEnterShowEditor in Value then
|
|
Include(NewOptionsEx, aoEnterShowEditor)
|
|
else
|
|
Exclude(NewOptionsEx, aoEnterShowEditor);
|
|
if edgoEnterThrough in Value then
|
|
Include(NewOptionsEx, aoEnterThrough)
|
|
else
|
|
Exclude(NewOptionsEx, aoEnterThrough);
|
|
if edgoExtMultiSelect in Value then
|
|
Include(NewOptions, aoExtMultiSelect)
|
|
else
|
|
Exclude(NewOptions, aoExtMultiSelect);
|
|
if edgoHeaderButtonClicking in Value then
|
|
Include(NewOptionsEx, aoHeaderButtonClicking)
|
|
else
|
|
Exclude(NewOptionsEx, aoHeaderButtonClicking);
|
|
if edgoHorzThrough in Value then
|
|
Include(NewOptionsEx, aoHorzThrough)
|
|
else
|
|
Exclude(NewOptionsEx, aoHorzThrough);
|
|
if edgoImmediateEditor in Value then
|
|
Include(NewOptions, aoImmediateEditor)
|
|
else
|
|
Exclude(NewOptions, aoImmediateEditor);
|
|
if edgoMouseScroll in Value then
|
|
Include(NewOptionsEx, aoMouseScroll)
|
|
else
|
|
Exclude(NewOptionsEx, aoMouseScroll);
|
|
if edgoMultiSelect in Value then
|
|
Include(NewOptions, aoMultiSelect)
|
|
else
|
|
Exclude(NewOptions, aoMultiSelect);
|
|
// edgoMultiSort
|
|
// edgoSeekDetail
|
|
if edgoShowHourGlass in Value then
|
|
Include(NewOptionsEx, aoShowHourGlass)
|
|
else
|
|
Exclude(NewOptionsEx, aoShowHourGlass);
|
|
if edgoStoreToIniFile in Value then
|
|
Include(NewOptions, aoStoreToIniFile)
|
|
else
|
|
Exclude(NewOptions, aoStoreToIniFile);
|
|
if edgoStoreToRegistry in Value then
|
|
Include(NewOptions, aoStoreToRegistry)
|
|
else
|
|
Exclude(NewOptions, aoStoreToRegistry);
|
|
if edgoTabs in Value then
|
|
Include(NewOptions, aoTabs)
|
|
else
|
|
Exclude(NewOptions, aoTabs);
|
|
if edgoTabThrough in Value then
|
|
Include(NewOptions, aoTabThrough)
|
|
else
|
|
Exclude(NewOptions, aoTabThrough);
|
|
if edgoVertThrough in Value then
|
|
Include(NewOptionsEx, aoVertThrough)
|
|
else
|
|
Exclude(NewOptionsEx, aoVertThrough);
|
|
inherited Options := NewOptions;
|
|
inherited OptionsEx := NewOptionsEx;
|
|
if ((edgoAutoSort in Value) <> (edgoAutoSort in FOptionsBehavior)) or
|
|
((edgoMultiSort in Value) <> (edgoMultiSort in FOptionsBehavior)) then
|
|
ClearColumnsSorted;
|
|
FOptionsBehavior := Value;
|
|
// SyncOptions
|
|
SyncOptions := FOptions;
|
|
SyncOptionsEx := FOptionsEx;
|
|
for I := 0 to OptionCount - 1 do
|
|
if AOptionsBehavior[I] in FOptionsBehavior then
|
|
Include(SyncOptions, AOptions[I])
|
|
else
|
|
Exclude(SyncOptions, AOptions[I]);
|
|
for I := 0 to OptionExCount - 1 do
|
|
if AOptionsBehaviorEx[I] in FOptionsBehavior then
|
|
Include(SyncOptionsEx, AOptionsEx[I])
|
|
else
|
|
Exclude(SyncOptionsEx, AOptionsEx[I]);
|
|
FOptions := SyncOptions;
|
|
FOptionsEx := SyncOptionsEx;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetOptionsCustomize(Value: TdxDBGridOptionsCustomize);
|
|
const
|
|
OptionCount = 3;
|
|
OptionExCount = 8;
|
|
AOptionsCustomize: array [0..OptionCount - 1] of TdxDBGridOptionCustomize = (
|
|
edgoColumnMoving, edgoColumnSizing, edgoExtCustomizing);
|
|
AOptions: array [0..OptionCount - 1] of TdxDBGridOption = (
|
|
egoColumnMoving, egoColumnSizing, egoExtCustomizing);
|
|
AOptionsCustomizeEx: array [0..OptionExCount - 1] of TdxDBGridOptionCustomize = (edgoBandMoving,
|
|
edgoBandPanelSizing, edgoBandSizing, edgoFullSizing, edgoHeaderPanelSizing,
|
|
edgoKeepColumnWidth, edgoNotHideColumn, edgoRowSizing);
|
|
AOptionsEx: array [0..OptionExCount - 1] of TdxDBGridOptionEx = (egoBandMoving,
|
|
egoBandPanelSizing, egoBandSizing, egoFullSizing, egoHeaderPanelSizing,
|
|
egoKeepColumnWidth, egoNotHideColumn, egoRowSizing);
|
|
var
|
|
NewOptions: TdxTreeListOptions;
|
|
NewOptionsEx: TdxTreeListOptionsEx;
|
|
SyncOptions: TdxDBGridOptions;
|
|
SyncOptionsEx: TdxDBGridOptionsEx;
|
|
I: Integer;
|
|
begin
|
|
if FOptionsCustomize <> Value then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
NewOptions := inherited Options;
|
|
NewOptionsEx := inherited OptionsEx;
|
|
if edgoBandMoving in Value then
|
|
Include(NewOptionsEx, aoBandMoving)
|
|
else
|
|
Exclude(NewOptionsEx, aoBandMoving);
|
|
if edgoBandPanelSizing in Value then
|
|
Include(NewOptionsEx, aoBandPanelSizing)
|
|
else
|
|
Exclude(NewOptionsEx, aoBandPanelSizing);
|
|
if edgoBandSizing in Value then
|
|
Include(NewOptionsEx, aoBandSizing)
|
|
else
|
|
Exclude(NewOptionsEx, aoBandSizing);
|
|
if edgoColumnMoving in Value then
|
|
Include(NewOptions, aoColumnMoving)
|
|
else
|
|
Exclude(NewOptions, aoColumnMoving);
|
|
if edgoColumnSizing in Value then
|
|
Include(NewOptions, aoColumnSizing)
|
|
else
|
|
Exclude(NewOptions, aoColumnSizing);
|
|
if edgoExtCustomizing in Value then
|
|
Include(NewOptions, aoExtCustomizing)
|
|
else
|
|
Exclude(NewOptions, aoExtCustomizing);
|
|
if edgoFullSizing in Value then
|
|
Include(NewOptionsEx, aoFullSizing)
|
|
else
|
|
Exclude(NewOptionsEx, aoFullSizing);
|
|
if edgoHeaderPanelSizing in Value then
|
|
Include(NewOptionsEx, aoHeaderPanelSizing)
|
|
else
|
|
Exclude(NewOptionsEx, aoHeaderPanelSizing);
|
|
if edgoKeepColumnWidth in Value then
|
|
Include(NewOptionsEx, aoKeepColumnWidth)
|
|
else
|
|
Exclude(NewOptionsEx, aoKeepColumnWidth);
|
|
if edgoNotHideColumn in Value then
|
|
Include(NewOptionsEx, aoRowSizing)
|
|
else
|
|
Exclude(NewOptionsEx, aoRowSizing);
|
|
if edgoRowSizing in Value then
|
|
Include(NewOptionsEx, aoRowSizing)
|
|
else
|
|
Exclude(NewOptionsEx, aoRowSizing);
|
|
inherited Options := NewOptions;
|
|
inherited OptionsEx := NewOptionsEx;
|
|
FOptionsCustomize := Value;
|
|
// SyncOptions
|
|
SyncOptions := FOptions;
|
|
SyncOptionsEx := FOptionsEx;
|
|
for I := 0 to OptionCount - 1 do
|
|
if AOptionsCustomize[I] in FOptionsCustomize then
|
|
Include(SyncOptions, AOptions[I])
|
|
else
|
|
Exclude(SyncOptions, AOptions[I]);
|
|
for I := 0 to OptionExCount - 1 do
|
|
if AOptionsCustomizeEx[I] in FOptionsCustomize then
|
|
Include(SyncOptionsEx, AOptionsEx[I])
|
|
else
|
|
Exclude(SyncOptionsEx, AOptionsEx[I]);
|
|
FOptions := SyncOptions;
|
|
FOptionsEx := SyncOptionsEx;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetOptionsDB(Value: TdxDBGridOptionsDB);
|
|
const
|
|
OptionCount = 11;
|
|
OptionExCount = 1;
|
|
AOptionsDB: array [0..OptionCount - 1] of TdxDBGridOptionDB = (edgoCanAppend,
|
|
edgoCancelOnExit, edgoCanDelete, edgoCanInsert, edgoCanNavigation, edgoConfirmDelete,
|
|
edgoLoadAllRecords, edgoResetColumnFocus, edgoSmartRefresh, edgoSmartReload, edgoUseLocate);
|
|
AOptions: array [0..OptionCount - 1] of TdxDBGridOption = (egoCanAppend,
|
|
egoCancelOnExit, egoCanDelete, egoCanInsert, egoCanNavigation, egoConfirmDelete,
|
|
egoLoadAllRecords, egoResetColumnFocus, egoSmartRefresh, egoSmartReload, egoUseLocate);
|
|
AOptionsDBEx: array [0..OptionExCount - 1] of TdxDBGridOptionDB = (edgoSyncSelection);
|
|
AOptionsEx: array [0..OptionExCount - 1] of TdxDBGridOptionEx = (egoSyncSelection);
|
|
var
|
|
PrevOptions: TdxDBGridOptionsDB;
|
|
SyncOptions: TdxDBGridOptions;
|
|
SyncOptionsEx: TdxDBGridOptionsEx;
|
|
I: Integer;
|
|
begin
|
|
if FOptionsDB <> Value then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
PrevOptions := OptionsDB;
|
|
if edgoLoadAllRecords in Value then
|
|
Exclude(Value, edgoPartialLoad);
|
|
if edgoPartialLoad in Value then
|
|
Exclude(Value, edgoLoadAllRecords); // TODO ?
|
|
FOptionsDB := Value;
|
|
// SyncOptions
|
|
SyncOptions := FOptions;
|
|
SyncOptionsEx := FOptionsEx;
|
|
for I := 0 to OptionCount - 1 do
|
|
if AOptionsDB[I] in FOptionsDB then
|
|
Include(SyncOptions, AOptions[I])
|
|
else
|
|
Exclude(SyncOptions, AOptions[I]);
|
|
for I := 0 to OptionExCount - 1 do
|
|
if AOptionsDBEx[I] in FOptionsDB then
|
|
Include(SyncOptionsEx, AOptionsEx[I])
|
|
else
|
|
Exclude(SyncOptionsEx, AOptionsEx[I]);
|
|
FOptions := SyncOptions;
|
|
FOptionsEx := SyncOptionsEx;
|
|
// set properties
|
|
if (edgoPartialLoad in FOptionsDB) <> (edgoPartialLoad in PrevOptions) then
|
|
if InternalGridMode then
|
|
begin
|
|
if (edgoPartialLoad in FOptionsDB) and DefaultFields then
|
|
DefaultFields := False;
|
|
FGridBOF := False;
|
|
FGridEOF := False;
|
|
LinkActive(FDataLink.Active);
|
|
end;
|
|
if (edgoUseBookmarks in FOptionsDB) and not (edgoUseBookmarks in PrevOptions) then
|
|
ClearSelection;
|
|
// check default fields
|
|
if (edgoLoadAllRecords in FOptionsDB) and DefaultFields then
|
|
DefaultFields := False;
|
|
if (PrevOptions * [edgoLoadAllRecords] <> FOptionsDB * [edgoLoadAllRecords]) and
|
|
(GroupColumnCount = 0) then
|
|
begin
|
|
// check sorted
|
|
CheckSorted;
|
|
BeginGrouping;
|
|
// Filter Refresh (Auto set DataSet.FilterText)
|
|
// SetFilterMode;
|
|
EndGrouping;
|
|
SetFilterMode;
|
|
end
|
|
else
|
|
if (PrevOptions * [edgoCanNavigation] <> FOptionsDB * [edgoCanNavigation]) then
|
|
FullRefresh;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetOptionsView(Value: TdxDBGridOptionsView);
|
|
const
|
|
OptionCount = 6;
|
|
OptionExCount = 7;
|
|
AOptionsView: array [0..OptionCount - 1] of TdxDBGridOptionView = (edgoAutoWidth,
|
|
edgoDrawEndEllipsis, edgoHideFocusRect, edgoIndicator, edgoPreview, edgoRowSelect);
|
|
AOptions: array [0..OptionCount - 1] of TdxDBGridOption = (egoAutoWidth,
|
|
egoDrawEndEllipsis, egoHideFocusRect, egoIndicator, egoPreview, egoRowSelect);
|
|
AOptionsViewEx: array [0..OptionExCount - 1] of TdxDBGridOptionView = (edgoAutoCalcPreviewLines,
|
|
edgoAutoHeaderPanelHeight, edgoBandHeaderWidth, edgoInvertSelect,
|
|
edgoRowAutoHeight, edgoShowButtonAlways, edgoUseBitmap);
|
|
AOptionsEx: array [0..OptionExCount - 1] of TdxDBGridOptionEx = (egoAutoCalcPreviewLines,
|
|
egoAutoHeaderPanelHeight, egoBandHeaderWidth, egoInvertSelect,
|
|
egoRowAutoHeight, egoShowButtonAlways, egoUseBitmap);
|
|
var
|
|
NewOptions: TdxTreeListOptions;
|
|
NewOptionsEx: TdxTreeListOptionsEx;
|
|
PrevOptions: TdxDBGridOptionsView;
|
|
SyncOptions: TdxDBGridOptions;
|
|
SyncOptionsEx: TdxDBGridOptionsEx;
|
|
I: Integer;
|
|
begin
|
|
if FOptionsView <> Value then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
PrevOptions := OptionsView;
|
|
NewOptions := inherited Options;
|
|
NewOptionsEx := inherited OptionsEx;
|
|
if edgoAutoCalcPreviewLines in Value then
|
|
Include(NewOptionsEx, aoAutoCalcPreviewLines)
|
|
else
|
|
Exclude(NewOptionsEx, aoAutoCalcPreviewLines);
|
|
if edgoAutoHeaderPanelHeight in Value then
|
|
Include(NewOptionsEx, aoAutoHeaderPanelHeight)
|
|
else
|
|
Exclude(NewOptionsEx, aoAutoHeaderPanelHeight);
|
|
if edgoAutoWidth in Value then
|
|
Include(NewOptions, aoAutoWidth)
|
|
else
|
|
Exclude(NewOptions, aoAutoWidth);
|
|
if edgoBandHeaderWidth in Value then
|
|
Include(NewOptionsEx, aoBandHeaderWidth)
|
|
else
|
|
Exclude(NewOptionsEx, aoBandHeaderWidth);
|
|
if edgoDrawEndEllipsis in Value then
|
|
Include(NewOptions, aoDrawEndEllipsis)
|
|
else
|
|
Exclude(NewOptions, aoDrawEndEllipsis);
|
|
if edgoHideFocusRect in Value then
|
|
Include(NewOptions, aoHideFocusRect)
|
|
else
|
|
Exclude(NewOptions, aoHideFocusRect);
|
|
if edgoHotTrack in Value then
|
|
Include(NewOptionsEx, aoHotTrack)
|
|
else
|
|
Exclude(NewOptionsEx, aoHotTrack);
|
|
ShowIndicator := edgoIndicator in Value;
|
|
if edgoInvertSelect in Value then
|
|
begin
|
|
Include(NewOptionsEx, aoInvertSelect);
|
|
Exclude(Value, edgoRowSelect);
|
|
end
|
|
else
|
|
Exclude(NewOptionsEx, aoInvertSelect);
|
|
if edgoPreview in Value then
|
|
Include(NewOptions, aoPreview)
|
|
else
|
|
Exclude(NewOptions, aoPreview);
|
|
if edgoRowAutoHeight in Value then
|
|
Include(NewOptionsEx, aoRowAutoHeight)
|
|
else
|
|
Exclude(NewOptionsEx, aoRowAutoHeight);
|
|
if edgoRowSelect in Value then
|
|
begin
|
|
Include(NewOptions, aoRowSelect);
|
|
Exclude(NewOptionsEx, aoInvertSelect);
|
|
end
|
|
else
|
|
Exclude(NewOptions, aoRowSelect);
|
|
if edgoShowButtonAlways in Value then
|
|
Include(NewOptionsEx, aoShowButtonAlways)
|
|
else
|
|
Exclude(NewOptionsEx, aoShowButtonAlways);
|
|
if edgoUseBitmap in Value then
|
|
Include(NewOptionsEx, aoUseBitmap)
|
|
else
|
|
Exclude(NewOptionsEx, aoUseBitmap);
|
|
inherited Options := NewOptions;
|
|
inherited OptionsEx := NewOptionsEx;
|
|
FOptionsView := Value;
|
|
// SyncOptions
|
|
SyncOptions := FOptions;
|
|
SyncOptionsEx := FOptionsEx;
|
|
for I := 0 to OptionCount - 1 do
|
|
if AOptionsView[I] in FOptionsView then
|
|
Include(SyncOptions, AOptions[I])
|
|
else
|
|
Exclude(SyncOptions, AOptions[I]);
|
|
for I := 0 to OptionExCount - 1 do
|
|
if AOptionsViewEx[I] in FOptionsView then
|
|
Include(SyncOptionsEx, AOptionsEx[I])
|
|
else
|
|
Exclude(SyncOptionsEx, AOptionsEx[I]);
|
|
FOptions := SyncOptions;
|
|
FOptionsEx := SyncOptionsEx;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetPartialLoad(Value: Boolean);
|
|
begin
|
|
if Value then
|
|
OptionsDB := OptionsDB + [edgoPartialLoad]
|
|
else
|
|
OptionsDB := OptionsDB - [edgoPartialLoad];
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetPartialLoadBufferCount(Value: Integer);
|
|
begin
|
|
if FPartialLoadBufferCount <> Value then
|
|
FPartialLoadBufferCount := Value;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetSummaryGroups(Value: TdxDBGridSummaryGroups);
|
|
begin
|
|
SummaryGroups.Assign(Value);
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetSummarySeparator(Value: string);
|
|
begin
|
|
if FSummarySeparator <> Value then
|
|
begin
|
|
FSummarySeparator := Value;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.SetUseBookmarks(Value: Boolean);
|
|
begin
|
|
if Value then
|
|
OptionsDB := OptionsDB + [edgoUseBookmarks]
|
|
else
|
|
OptionsDB := OptionsDB - [edgoUseBookmarks];
|
|
end;
|
|
|
|
function TCustomdxDBGrid.InternalGridMode: Boolean;
|
|
begin
|
|
Result := True;
|
|
if Datalink.Active and (GroupColumnCount > 0) then
|
|
Result := False;
|
|
if egoLoadAllRecords in Options then Result := False;
|
|
end;
|
|
|
|
// private TCustomDxDBGrid
|
|
procedure TCustomdxDBGrid.WMVScroll(var Message: TWMVScroll);
|
|
{$IFDEF DELPHI3}
|
|
var
|
|
SI: TScrollInfo;
|
|
{$ENDIF}
|
|
begin
|
|
if not AcquireFocus then Exit;
|
|
if not IsGridMode then
|
|
begin
|
|
inherited;
|
|
// inherited UpdateScrollBars;
|
|
end
|
|
else
|
|
if FDatalink.Active then
|
|
begin
|
|
HideEditor;
|
|
with Message, FDataLink.DataSet do
|
|
case ScrollCode of
|
|
SB_LINEUP: MoveBy(-FDatalink.ActiveRecord - 1);
|
|
SB_LINEDOWN: MoveBy(FDatalink.RecordCount - FDatalink.ActiveRecord);
|
|
SB_PAGEUP: MoveBy(-VisibleRowCount);
|
|
SB_PAGEDOWN: MoveBy(VisibleRowCount);
|
|
SB_THUMBPOSITION:
|
|
begin
|
|
{$IFDEF DELPHI3}
|
|
if IsSequenced then
|
|
begin
|
|
SI.cbSize := sizeof(SI);
|
|
SI.fMask := SIF_ALL;
|
|
GetScrollInfo(SB_VERT, SI);
|
|
if SI.nTrackPos <= 1 then First
|
|
else if SI.nTrackPos >= RecordCount then Last
|
|
else RecNo := SI.nTrackPos;
|
|
end
|
|
else
|
|
{$ENDIF}
|
|
case Pos of
|
|
0: First;
|
|
1: MoveBy(-VisibleRowCount);
|
|
2: Exit;
|
|
3: MoveBy(VisibleRowCount);
|
|
4: Last;
|
|
end;
|
|
end;
|
|
SB_BOTTOM: Last;
|
|
SB_TOP: First;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomdxDBGrid.CMDesignHitTest(var Msg: TCMDesignHitTest);
|
|
begin
|
|
inherited;
|
|
if (Msg.Result <> 1) and (GetGroupColumnAt(Msg.Pos.X, Msg.Pos.Y) <> nil) then
|
|
Msg.Result := 1;
|
|
end;
|
|
|
|
{ TdxDBGridFilterValues }
|
|
destructor TdxDBGridFilterValues.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TdxDBGridFilterValues.AddValue(AText: string; AValue: Variant; ACustomLoad: Boolean);
|
|
var
|
|
AIndex: Integer;
|
|
P: PVariant;
|
|
begin
|
|
if CaseInsensitive and (VarType(AValue) = varString) then
|
|
begin
|
|
AValue := AnsiUpperCase(AValue);
|
|
AText := AnsiUpperCase(AText);
|
|
end;
|
|
if ACustomLoad or
|
|
((MaxCount = 0) or (Count < MaxCount)) and not FindValue(AValue, AIndex) then
|
|
begin
|
|
New(P);
|
|
P^ := AValue;
|
|
if ACustomLoad then
|
|
AddObject(AText, TObject(P))
|
|
else
|
|
InsertObject(AIndex, AText, TObject(P));
|
|
end;
|
|
end;
|
|
|
|
procedure TdxDBGridFilterValues.Clear;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
if Objects[I] <> nil then
|
|
Dispose(PVariant(Objects[I]));
|
|
inherited Clear;
|
|
end;
|
|
|
|
function TdxDBGridFilterValues.FindValue(const AValue: Variant; var AIndex: Integer): Boolean;
|
|
var
|
|
L, H, I, C: Integer;
|
|
begin
|
|
Result := False;
|
|
L := 0;
|
|
H := Count - 1;
|
|
while L <= H do
|
|
begin
|
|
I := (L + H) shr 1;
|
|
if AnsiSort and (VarType(AValue) = varString) then
|
|
C := AnsiCompareStr(PVariant(Objects[I])^, AValue)
|
|
else
|
|
C := VarCompare(PVariant(Objects[I])^, AValue);
|
|
if C < 0 then
|
|
L := I + 1
|
|
else
|
|
begin
|
|
if C = 0 then
|
|
begin
|
|
Result := True;
|
|
L := I;
|
|
end;
|
|
H := I - 1;
|
|
end;
|
|
end;
|
|
AIndex := L;
|
|
end;
|
|
|
|
{ TdxDBGridFilterPopupListBox }
|
|
constructor TdxDBGridFilterPopupListBox.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FFilterValues := TdxDBGridFilterValues.Create;
|
|
end;
|
|
|
|
destructor TdxDBGridFilterPopupListBox.Destroy;
|
|
begin
|
|
FFilterValues.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TdxDBGridFilterPopupListBox.KeyDown(var Key: Word; Shift: TShiftState);
|
|
begin
|
|
inherited KeyDown(Key, Shift);
|
|
if Key in [VK_RETURN, VK_ESCAPE, VK_F4] then
|
|
begin
|
|
case Key of
|
|
VK_RETURN: ClosePopup(True);
|
|
VK_ESCAPE, VK_F4: ClosePopup(False);
|
|
end;
|
|
KillMessage(Grid.Handle, WM_CHAR);
|
|
end;
|
|
end;
|
|
|
|
procedure TdxDBGridFilterPopupListBox.MouseUp(Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
inherited MouseUp(Button, Shift, X, Y);
|
|
if Button = mbLeft then
|
|
ClosePopup((X >= 0) and (Y >= 0) and (X < Width) and (Y < Height));
|
|
end;
|
|
|
|
procedure TdxDBGridFilterPopupListBox.ClosePopup(Accept: Boolean);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ListVisible then
|
|
begin
|
|
if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
|
|
SetWindowPos(Handle, 0, 0, 0, 0, 0, SWP_NOZORDER or
|
|
SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_HIDEWINDOW);
|
|
ListVisible := False;
|
|
if Accept then
|
|
begin
|
|
I := ItemIndex;
|
|
if (I <> -1) and Assigned(Column) and Assigned(Grid) then
|
|
begin
|
|
if I = 0 then
|
|
Grid.Filter.Remove(Column)
|
|
else
|
|
if I = 1 then
|
|
Grid.Filter.ShowCustomDialog(Column)
|
|
else
|
|
if (I = 2) and (Items.Objects[I] = nil) then // null
|
|
Grid.Filter.AddNull(Column, False)
|
|
else
|
|
if (I = 3) and (Items.Objects[I] = nil) then // not null
|
|
Grid.Filter.AddNull(Column, True)
|
|
else
|
|
Grid.Filter.Add(Column, PVariant(Items.Objects[I])^, Items[I]);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TdxDBGridFilterPopupListBox.GetGrid: TCustomdxDBGrid;
|
|
begin
|
|
if Assigned(FColumn) then
|
|
Result := TCustomdxDBGrid(FColumn.ATreeList)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TdxDBGridFilterPopupListBox.GetListVisible: Boolean;
|
|
begin
|
|
Result := Assigned(Grid) and Grid.FDropDownListVisible;
|
|
end;
|
|
|
|
procedure TdxDBGridFilterPopupListBox.SetListVisible(Value: Boolean);
|
|
begin
|
|
if Assigned(Grid) then
|
|
Grid.FDropDownListVisible := Value;
|
|
end;
|
|
|
|
{ TdxDBGridFilter }
|
|
constructor TdxDBGridFilter.Create(ADBGrid: TCustomdxDBGrid);
|
|
begin
|
|
inherited Create;
|
|
FDBGrid := ADBGrid;
|
|
FCriteria := TdxDBGridCriteria.Create(TdxDBGrid(ADBGrid));
|
|
FDropDownCount := 12;
|
|
FMaxDropDownCount := 1000;
|
|
end;
|
|
|
|
destructor TdxDBGridFilter.Destroy;
|
|
begin
|
|
FCriteria.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.Add(AColumn: TdxDBTreeListColumn; const AValue: Variant; const ADisplayValue: string);
|
|
begin
|
|
TdxDBGridCriteria(FCriteria).RemoveItemsByColumn(AColumn);
|
|
TdxDBGridCriteria(FCriteria).AddItem(nil, AColumn, otEqual, AValue, ADisplayValue, False);
|
|
FDBGrid.SetFilterMode;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.AddNull(AColumn: TdxDBTreeListColumn; IsNot: Boolean);
|
|
begin
|
|
TdxDBGridCriteria(FCriteria).RemoveItemsByColumn(AColumn);
|
|
TdxDBGridCriteria(FCriteria).AddItem(nil, AColumn, otIsNull, Null, '', IsNot);
|
|
FDBGrid.SetFilterMode;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.Clear;
|
|
begin
|
|
FCriteria.Clear;
|
|
FDBGrid.ClearFilter;
|
|
end;
|
|
|
|
function TdxDBGridFilter.GetFilterColumnCriteria(AColumn: TdxDBTreeListColumn;
|
|
var AValue: Variant): TdxDBGridFilterColumnCriteria;
|
|
var
|
|
ACriteriaItem: TdxCriteriaItem;
|
|
begin
|
|
Result := fcNone;
|
|
AValue := Null;
|
|
ACriteriaItem := TdxDBGridCriteria(FCriteria).FindItemByColumn(AColumn);
|
|
if ACriteriaItem <> nil then
|
|
begin
|
|
Result := fcValue;
|
|
AValue := ACriteriaItem.Value;
|
|
if VarEqualNull(AValue) and (ACriteriaItem.Operator = otIsNull) then
|
|
begin
|
|
if ACriteriaItem.IsNot then
|
|
Result := fcNonBlanks
|
|
else
|
|
Result := fcBlanks;
|
|
end
|
|
else
|
|
if TdxDBGridCriteria(FCriteria).ExistCustomCriteriaByColumn(AColumn) then
|
|
Result := fcCustom;
|
|
end;
|
|
end;
|
|
|
|
function TdxDBGridFilter.IsColumnFilterExist(AColumn: TdxDBTreeListColumn): Boolean;
|
|
begin
|
|
Result := TdxDBGridCriteria(FCriteria).FindItemByColumn(AColumn) <> nil;
|
|
end;
|
|
|
|
function TdxDBGridFilter.IsEmpty: Boolean;
|
|
begin
|
|
Result := Criteria.Root.Count = 0;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.Remove(AColumn: TdxDBTreeListColumn);
|
|
begin
|
|
TdxDBGridCriteria(FCriteria).RemoveItemsByColumn(AColumn);
|
|
FDBGrid.SetFilterMode;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.RestoreDefaults;
|
|
begin
|
|
Active := False;
|
|
AutoDataSetFilter := False;
|
|
CaseInsensitive := False;
|
|
DropDownCount := 12;
|
|
DropDownWidth := 0;
|
|
FilterStatus := fsAuto;
|
|
MaxDropDownCount := 1000;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.ShowCustomDialog(AColumn: TdxDBTreeListColumn);
|
|
begin
|
|
if TdxDBGridCriteria(FCriteria).ShowCustomDialog(AColumn) then
|
|
FDBGrid.SetFilterMode;
|
|
end;
|
|
|
|
type TdxDBGridCriteriaWrapper = class(TdxDBGridCriteria);
|
|
|
|
procedure TdxDBGridFilter.DefineProperties(Filer: TFiler);
|
|
begin
|
|
inherited DefineProperties(Filer);
|
|
Filer.DefineBinaryProperty('Criteria', TdxDBGridCriteriaWrapper(FCriteria).ReadData,
|
|
TdxDBGridCriteriaWrapper(FCriteria).WriteData, True{FCriteria.Root.Count > 0});
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.Changed;
|
|
begin
|
|
FDBGrid.SetFilterMode;
|
|
end;
|
|
|
|
function TdxDBGridFilter.CheckFilterNode(ANode: TdxDBGridNode): Boolean;
|
|
begin
|
|
Result := TdxDBGridCriteria(FCriteria).DoFilterNode(ANode);
|
|
end;
|
|
|
|
function TdxDBGridFilter.GetFilterCaption: string;
|
|
begin
|
|
Result := FCriteria.FilterCaption;
|
|
end;
|
|
|
|
function TdxDBGridFilter.GetFilterText: string;
|
|
begin
|
|
Result := FCriteria.FilterText;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.Prepare;
|
|
begin
|
|
FCriteria.Prepare;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.SetFilterText(const Value: string);
|
|
begin
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.SetActive(Value: Boolean);
|
|
begin
|
|
if FActive <> Value then
|
|
begin
|
|
FActive := Value;
|
|
if not Value then
|
|
Clear
|
|
else
|
|
FDBGrid.DefaultFields := False;
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.SetAutoDataSetFilter(Value: Boolean);
|
|
var
|
|
ADataSet: TDataSet;
|
|
begin
|
|
if FAutoDataSetFilter <> Value then
|
|
begin
|
|
FAutoDataSetFilter := Value;
|
|
if Assigned(DBGrid.DataSource) then
|
|
begin
|
|
ADataSet := DBGrid.DataSource.DataSet;
|
|
if not Value and Assigned(ADataSet) then
|
|
begin
|
|
ADataSet.Filter := '';
|
|
ADataSet.Filtered := False;
|
|
end;
|
|
end;
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.SetCaseInsensitive(Value: Boolean);
|
|
begin
|
|
if FCaseInsensitive <> Value then
|
|
begin
|
|
FCaseInsensitive := Value;
|
|
TdxDBGridCriteria(FCriteria).CaseInsensitive := FCaseInsensitive;
|
|
if AutoDataSetFilter then
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
procedure TdxDBGridFilter.SetFilterStatus(Value: TdxDBGridFilterStatus);
|
|
begin
|
|
if FFilterStatus <> Value then
|
|
begin
|
|
FFilterStatus := Value;
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
{ TdxDBGrid }
|
|
procedure TdxDBGrid.SaveToHTML(const AFileName: String; ASaveAll: Boolean);
|
|
begin
|
|
dxGrExpt.SaveToHTML(Self, AFileName, ASaveAll);
|
|
end;
|
|
|
|
procedure TdxDBGrid.SaveToXLS(const AFileName : String; ASaveAll: Boolean);
|
|
begin
|
|
dxGrExpt.SaveToXLS(Self, AFileName, ASaveAll);
|
|
end;
|
|
|
|
procedure TdxDBGrid.SaveToText(const AFileName: string; ASaveAll: Boolean;
|
|
const ASeparator, ABeginString, AEndString: string);
|
|
begin
|
|
dxGrExpt.SaveToText(Self, AFileName, ASaveAll, ASeparator, ABeginString, AEndString);
|
|
end;
|
|
|
|
procedure TdxDBGrid.SaveToXML(const AFileName: string; ASaveAll: Boolean);
|
|
begin
|
|
dxGrExpt.SaveToXML(Self, AFileName, ASaveAll);
|
|
end;
|
|
|
|
procedure InitDefaultDBGridColumnClasses(Proc: TInitColumnClassProc);
|
|
var
|
|
I: TFieldType;
|
|
C: TdxDBTreeListColumnClassInfo;
|
|
begin
|
|
for I := ftUnknown to ftTypedBinary do
|
|
begin
|
|
C := DefaultDBGridColumnType[I];
|
|
Proc(I, fkData, C);
|
|
if C.Version > DefaultDBGridColumnType[i].Version then
|
|
DefaultDBGridColumnType[I] := C;
|
|
end;
|
|
C := DefaultDBGridLookupColumnType;
|
|
Proc(ftUnknown, fkLookup, C);
|
|
if C.Version > DefaultDBGridLookupColumnType.Version then
|
|
DefaultDBGridLookupColumnType := C;
|
|
end;
|
|
|
|
procedure ResetDefaultDBGridColumnClasses;
|
|
var
|
|
I: TFieldType;
|
|
begin
|
|
DefaultDBGridColumnType[ftUnknown].ColumnClass := nil;
|
|
DefaultDBGridColumnType[ftUnknown].Version := 0;
|
|
for I := Succ(ftUnknown) to ftTypedBinary do
|
|
begin
|
|
DefaultDBGridColumnType[ftUnknown].ColumnClass := TdxDBGridColumn;
|
|
DefaultDBGridColumnType[ftUnknown].Version := 0;
|
|
end;
|
|
DefaultDBGridLookupColumnType.ColumnClass := TdxDBGridColumn;
|
|
DefaultDBGridLookupColumnType.Version := 0;
|
|
end;
|
|
|
|
initialization
|
|
Classes.RegisterClasses([TdxDBGridColumn, TdxDBGridMaskColumn]);
|
|
|
|
sdxPanelText := LoadStr(dxSPanelText);
|
|
|
|
end.
|